Integrated Circuit Overhaul 2.0 (#22647)

Complete overhaul of Integrated Circuits Final Edition!

- Adds phoron cost to circuits (not assemblies).
- Printer searching capability.
- Changed names of assemblies to better match their description.
- Adds cloning and auto appending JSON for large builds.
- Adds database, debug, advanced math circuits.
- Reworks the list circuits to finally accept anything other than NUM.
- Adds plenty of list circuits to allow functional building and clearing
of lists.
- Reworks Complexity/Space to allow a bit better flexibility in creation
of devices.
- Adds a "Headroom" concept where space/complexity can go beyond the
maximum to a very slight degree. Concretely, unused space gives `2`
extra complexity per unused component slot, capped at `15%` of
`max_complexity`. Unused complexity gives `1` extra component slot per
`6` unused complexity, capped at `15%` of `max_components`.
- Groups math and logic circuits and adds subgroups.
- Doesn't mess with the circuit tool kit.
- Adds a xenoarch drill assembly with a nice sprite. Thank you
@EnchantedCrocolisk for the sprite!
- Changes activation limit time to 1 second from 2 seconds, adds a 1
second timer that is unique to the INTEGRATED CIRCUIT PRINTER with the
advanced design disk.
- Ports several Baystation integrated-circuit manipulation components,
primarily from
Baystation12/Baystation12https://github.com/Baystation12/Baystation12/pull/22458,
with anchoring bolts and hatch lock sourced from
Baystation12/Baystation12https://github.com/Baystation12/Baystation12/pull/25052.
- Changes the electronic ear piece into a workable radio headset with
working encryption keys capability and unique ways the Microphone and
TTS interacts with it.
- Removed the ability for all the electronic wearables to spawn with a
basic cell.
- Combines https://github.com/Aurorastation/Aurora.3/pull/22466,
https://github.com/Aurorastation/Aurora.3/pull/22456,
https://github.com/Aurorastation/Aurora.3/pull/22455,
https://github.com/Aurorastation/Aurora.3/pull/22446 that are now
closed.
This commit is contained in:
mineymonkey
2026-07-26 19:52:31 +00:00
committed by GitHub
parent e7b3da61c6
commit 6c6f596d89
52 changed files with 10433 additions and 842 deletions
+4
View File
@@ -2592,6 +2592,7 @@
#include "code\modules\integrated_electronics\core\printer.dm"
#include "code\modules\integrated_electronics\core\tools.dm"
#include "code\modules\integrated_electronics\core\assemblies\clothing.dm"
#include "code\modules\integrated_electronics\core\assemblies\electronic_radio_headset.dm"
#include "code\modules\integrated_electronics\core\assemblies\generic.dm"
#include "code\modules\integrated_electronics\core\special_pins\boolean_pin.dm"
#include "code\modules\integrated_electronics\core\special_pins\char_pin.dm"
@@ -2607,15 +2608,18 @@
#include "code\modules\integrated_electronics\subtypes\built_in.dm"
#include "code\modules\integrated_electronics\subtypes\converters.dm"
#include "code\modules\integrated_electronics\subtypes\data_transfer.dm"
#include "code\modules\integrated_electronics\subtypes\debug.dm"
#include "code\modules\integrated_electronics\subtypes\input.dm"
#include "code\modules\integrated_electronics\subtypes\insert_slot.dm"
#include "code\modules\integrated_electronics\subtypes\lists.dm"
#include "code\modules\integrated_electronics\subtypes\logic.dm"
#include "code\modules\integrated_electronics\subtypes\manipulation.dm"
#include "code\modules\integrated_electronics\subtypes\math_extended.dm"
#include "code\modules\integrated_electronics\subtypes\memory.dm"
#include "code\modules\integrated_electronics\subtypes\output.dm"
#include "code\modules\integrated_electronics\subtypes\power.dm"
#include "code\modules\integrated_electronics\subtypes\reagents.dm"
#include "code\modules\integrated_electronics\subtypes\server.dm"
#include "code\modules\integrated_electronics\subtypes\smart.dm"
#include "code\modules\integrated_electronics\subtypes\time.dm"
#include "code\modules\integrated_electronics\subtypes\trig.dm"
+4
View File
@@ -1,2 +1,6 @@
#define IC_COMPONENTS_BASE 25
#define IC_COMPLEXITY_BASE 75
#define IC_OVERFLOW_COMPONENT_TO_COMPLEXITY 2
#define IC_OVERFLOW_COMPLEXITY_TO_COMPONENT 6
#define IC_OVERFLOW_LIMIT 0.15
@@ -1,13 +1,12 @@
#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer.
#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer.
#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it.
PROCESSING_SUBSYSTEM_DEF(electronics)
name = "Electronics"
wait = 2 SECONDS
wait = 1 SECOND
priority = SS_PRIORITY_ELECTRONICS
flags = SS_KEEP_TIMING
init_order = INIT_ORDER_MISC_FIRST
var/list/all_integrated_circuits = list()
var/list/printer_recipe_list = list()
var/list/printer_recipe_list_basic = list()
@@ -20,13 +19,14 @@ PROCESSING_SUBSYSTEM_DEF(electronics)
// First loop is to seperate the actual circuits from base circuits.
var/list/circuits_to_use = list()
for(var/obj/item/integrated_circuit/IC in all_integrated_circuits)
if((IC.spawn_flags & (IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH)))
if((IC.get_printer_spawn_flags() & (IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH)))
circuits_to_use += IC
// Second loop is to find all categories.
for(var/obj/item/integrated_circuit/IC in circuits_to_use)
if(!(IC.category_text in found_categories))
found_categories += IC.category_text
found_categories += "Assemblies"
found_categories += "Tools"
@@ -57,6 +57,7 @@ PROCESSING_SUBSYSTEM_DEF(electronics)
new /obj/item/electronic_assembly/medium/medical,
new /obj/item/electronic_assembly/medium/gun,
new /obj/item/electronic_assembly/medium/radio,
new /obj/item/electronic_assembly/medium/anomaly_drill,
new /obj/item/electronic_assembly/large/default,
new /obj/item/electronic_assembly/large/scope,
new /obj/item/electronic_assembly/large/terminal,
@@ -78,7 +79,7 @@ PROCESSING_SUBSYSTEM_DEF(electronics)
new /obj/item/clothing/glasses/circuitry,
new /obj/item/clothing/shoes/circuitry,
new /obj/item/clothing/head/circuitry,
new /obj/item/clothing/ears/circuitry,
new /obj/item/radio/headset/circuitry,
new /obj/item/clothing/suit/circuitry
)
@@ -94,12 +95,13 @@ PROCESSING_SUBSYSTEM_DEF(electronics)
var/is_basic = TRUE
if(istype(O, /obj/item/integrated_circuit))
var/obj/item/integrated_circuit/IC = O
if((IC.spawn_flags & IC_SPAWN_RESEARCH) && (!(IC.spawn_flags & IC_SPAWN_DEFAULT)))
var/printer_spawn_flags = IC.get_printer_spawn_flags()
if((printer_spawn_flags & IC_SPAWN_RESEARCH) && (!(printer_spawn_flags & IC_SPAWN_DEFAULT)))
is_basic = FALSE
printer_recipe_list_basic += list(list(path = "[O.type]", name = "[O.name]", desc = "[O.desc]", "basic" = is_basic, "category" = category))
printer_recipe_list_upgraded += list(list(path = "[O.type]", name = "[O.name]", desc = "[O.desc]", "basic" = TRUE, "category" = category))
var/list/recipe = list(path = "[O.type]", name = "[O.name]", desc = "[O.desc]", "basic" = is_basic, "category" = category)
if(is_basic)
printer_recipe_list_basic += list(recipe)
printer_recipe_list_upgraded += list(recipe)
return SS_INIT_SUCCESS
@@ -9,9 +9,9 @@
/obj/item/implant/integrated_circuit/isLegal()
return TRUE
/obj/item/implant/integrated_circuit/Initialize()
/obj/item/implant/integrated_circuit/Initialize(mapload, printed = FALSE)
. = ..()
IC = new(src)
IC = new(src, printed)
IC.implant = src
/obj/item/implant/integrated_circuit/Destroy()
+14 -9
View File
@@ -9,34 +9,39 @@
return 0
/obj/item/clothing/attackby(obj/item/attacking_item, mob/user)
if(IC && (istype(attacking_item, /obj/item/integrated_circuit) || attacking_item.tool_behaviour == TOOL_WRENCH || attacking_item.tool_behaviour == TOOL_CROWBAR || \
if(IC)
if(attacking_item.tool_behaviour == TOOL_CROWBAR)
IC.attackby(attacking_item, user)
return TRUE
if(IC.opened && (istype(attacking_item, /obj/item/integrated_circuit) || attacking_item.tool_behaviour == TOOL_WRENCH || \
istype(attacking_item, /obj/item/integrated_electronics/wirer) || istype(attacking_item, /obj/item/integrated_electronics/debugger) || \
attacking_item.tool_behaviour == TOOL_MULTITOOL || attacking_item.tool_behaviour == TOOL_SCREWDRIVER || istype(attacking_item, /obj/item/cell/device)))
IC.attackby(attacking_item, user)
IC.attackby(attacking_item, user)
return TRUE
else if(istype(attacking_item, /obj/item/clothing/accessory))
if(!valid_accessory_slots || !valid_accessory_slots.len)
to_chat(usr, SPAN_WARNING("You cannot attach accessories of any kind to \the [src]."))
return
to_chat(user, SPAN_WARNING("You cannot attach accessories of any kind to \the [src]."))
return TRUE
var/obj/item/clothing/accessory/A = attacking_item
A.before_attached(src, user)
if(can_attach_accessory(A))
user.drop_item()
attach_accessory(user, A)
return
return TRUE
else
to_chat(user, SPAN_WARNING("You cannot attach more accessories of this type to [src]."))
return
return TRUE
if(LAZYLEN(accessories))
for(var/obj/item/clothing/accessory/A in accessories)
A.attackby(attacking_item, user)
return
return TRUE
..()
return ..()
/obj/item/clothing/attack_hand(var/mob/user)
//only forward to the attached accessory if the clothing is equipped (not in a storage)
@@ -39,6 +39,9 @@
// Data limits.
#define IC_MAX_LIST_LENGTH 200
#define IC_DEFAULT_PHORON_COST 0.005
#define IC_BLUEPRINT_CHUNK_LIMIT 1000
#define IC_BLUEPRINT_BUFFER_LIMIT 500000
/obj/item/integrated_circuit
name = "integrated circuit"
@@ -61,6 +64,7 @@
var/power_draw_idle = 0 // How much power is drawn when doing nothing.
var/spawn_flags // Used for world initializing, see the #defines above.
var/category_text = "NO CATEGORY THIS IS A BUG" // To show up on circuit printer, and perhaps other places.
var/phoron_cost = IC_DEFAULT_PHORON_COST
var/removable = TRUE // Determines if a circuit is removable from the assembly.
var/displayed_name = ""
var/allow_multitool = TRUE // Allows additional multitool functionality
@@ -1,6 +1,11 @@
/*
* core/assemblies.dm
* Base electronic assembly behavior: circuit storage, open/closed handling, interaction, icon updates, cloning hooks, and wiring rules.
*/
/obj/item/electronic_assembly
name = "electronic assembly"
desc = "It's a case, for building small electronics with."
name = "small circuit case"
desc = "A case for building small electronic assemblies."
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/obj/assemblies/electronic_setups.dmi'
icon_state = "setup_small"
@@ -9,25 +14,32 @@
var/max_components = IC_COMPONENTS_BASE
var/max_complexity = IC_COMPLEXITY_BASE
var/max_components_device = 16
var/max_complexity_device = 42
var/max_components_implant = 16
var/max_complexity_implant = 42
// Whether the assembly panel is open for direct circuit access.
var/opened = 0
var/can_anchor = FALSE // If true, wrenching it will anchor it.
var/obj/item/cell/device/battery // Internal cell which most circuits need to work.
// Power cell or battery object feeding the assembly.
var/obj/item/cell/battery // Internal cell which most circuits need to work.
// User-selected detail color used for assembly and wearable overlays.
var/detail_color = COLOR_ASSEMBLY_BLACK
var/obj/item/card/id/access_card
/obj/item/electronic_assembly/implant
name = "electronic implant"
icon_state = "setup_implant"
desc = "It's a case, for building very tiny electronics with."
desc = "A case for building very small electronic assemblies."
w_class = WEIGHT_CLASS_TINY
max_components = IC_COMPONENTS_BASE * 3/4
max_complexity = IC_COMPLEXITY_BASE * 3/4
max_components = /obj/item/electronic_assembly::max_components_implant
max_complexity = /obj/item/electronic_assembly::max_complexity_implant
var/obj/item/implant/integrated_circuit/implant = null
/obj/item/electronic_assembly/Initialize(mapload, printed = FALSE)
. = ..()
if (!printed)
battery = new(src)
battery = new /obj/item/cell/device(src)
START_PROCESSING(SSelectronics, src)
access_card = new /obj/item/card/id(src)
@@ -86,11 +98,13 @@
total_parts += part.size
total_complexity = total_complexity + part.complexity
var/list/HTML = list()
var/effective_component_limit = get_effective_component_limit(total_complexity)
var/effective_complexity_limit = get_effective_complexity_limit(total_parts)
HTML += "<br><a href='byond://?src=[REF(src)]'>Refresh</a> | "
HTML += "<a href='byond://?src=[REF(src)];rename=1'>Rename</a><br>"
HTML += "[total_parts]/[max_components] ([round((total_parts / max_components) * 100, 0.1)]%) space taken up in the assembly.<br>"
HTML += "[total_complexity]/[max_complexity] ([round((total_complexity / max_complexity) * 100, 0.1)]%) maximum complexity.<br>"
HTML += "[total_parts]/[max_components] space taken up in the assembly[effective_component_limit > max_components ? " ([effective_component_limit] with headroom)" : ""].<br>"
HTML += "[total_complexity]/[max_complexity] maximum complexity[effective_complexity_limit > max_complexity ? " ([effective_complexity_limit] with headroom)" : ""].<br>"
if(battery)
HTML += "[round(battery.charge, 0.1)]/[battery.maxcharge] ([round(battery.percent(), 0.1)]%) cell charge. <a href='byond://?src=[REF(src)];remove_cell=1'>Remove</a>"
else
@@ -150,7 +164,7 @@
/obj/item/electronic_assembly/verb/rename()
set name = "Rename Circuit"
set category = "Object"
set desc = "Rename your circuit, useful to stay organized."
set desc = "Renames the circuit to make assemblies easier to organize."
set src in usr
var/mob/M = usr
@@ -186,7 +200,7 @@
/obj/item/electronic_assembly/feedback_hints(mob/user, distance, is_adjacent)
. = ..()
if(opened && is_adjacent)
if(is_adjacent && opened)
for(var/obj/item/integrated_circuit/IC in contents)
. += SPAN_NOTICE("It contains \a [IC].")
@@ -200,6 +214,16 @@
for(var/obj/item/integrated_circuit/part in contents)
. += part.size
/obj/item/electronic_assembly/proc/get_effective_complexity_limit(total_part_size)
var/remaining_size = max(0, max_components - total_part_size)
var/overflow_limit = round(max_complexity * IC_OVERFLOW_LIMIT)
return max_complexity + min(remaining_size * IC_OVERFLOW_COMPONENT_TO_COMPLEXITY, overflow_limit)
/obj/item/electronic_assembly/proc/get_effective_component_limit(total_complexity)
var/remaining_complexity = max(0, max_complexity - total_complexity)
var/overflow_limit = round(max_components * IC_OVERFLOW_LIMIT)
return max_components + min(round(remaining_complexity / IC_OVERFLOW_COMPLEXITY_TO_COMPONENT), overflow_limit)
// Returns true if the circuit made it inside.
/obj/item/electronic_assembly/proc/add_circuit(obj/item/integrated_circuit/IC, mob/user)
if(!opened)
@@ -212,11 +236,13 @@
var/total_part_size = get_part_size()
var/total_complexity = get_part_complexity()
var/new_part_size = total_part_size + IC.size
var/new_complexity = total_complexity + IC.complexity
if((total_part_size + IC.size) > max_components)
if(new_part_size > get_effective_component_limit(new_complexity))
to_chat(user, SPAN_WARNING("You can't seem to add the '[IC.name]', as there's insufficient space."))
return FALSE
if((total_complexity + IC.complexity) > max_complexity)
if(new_complexity > get_effective_complexity_limit(new_part_size))
to_chat(user, SPAN_WARNING("You can't seem to add the '[IC.name]', since this setup's too complicated for the case."))
return FALSE
@@ -273,7 +299,7 @@
return TRUE
else if(istype(attacking_item, /obj/item/cell/device))
else if(istype(attacking_item, /obj/item/cell))
if(!opened)
to_chat(user, SPAN_WARNING("\The [src] isn't open, so you can't put anything inside. Try using a crowbar."))
for(var/obj/item/integrated_circuit/input/S in contents)
@@ -286,7 +312,7 @@
S.attackby_react(attacking_item,user,user.a_intent)
return FALSE
var/obj/item/cell/device/cell = attacking_item
var/obj/item/cell/cell = attacking_item
user.drop_from_inventory(cell,src)
battery = cell
playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
@@ -313,6 +339,7 @@
return
if(opened)
interact(user)
return
var/list/input_selection = list()
var/list/available_inputs = list()
@@ -1,15 +1,28 @@
/*
* core/assemblies/clothing.dm
* Wearable electronic assemblies that can be embedded into clothing-style items and driven through item actions.
*/
// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities
// E.g. Glasses have less room than something worn over the chest.
// Note that the electronic assembly is INSIDE the object that actually gets worn, in a similar way to implants.
/obj/item/electronic_assembly
var/max_components_clothing = 26
var/max_complexity_clothing = 68
/obj/item/electronic_assembly/clothing
name = "electronic clothing"
var/clothing_icon_state = "circuitry" // Needs to match the clothing's base icon_state.
desc = "It's a case, for building machines attached to clothing."
desc = "A clothing-mounted case for integrated electronics."
w_class = WEIGHT_CLASS_SMALL
max_components = IC_COMPONENTS_BASE
max_complexity = IC_COMPLEXITY_BASE
var/max_components_clothing_small = 14
var/max_complexity_clothing_small = 36
var/max_components_clothing_large = 42
var/max_complexity_clothing_large = 105
max_components = /obj/item/electronic_assembly::max_components_clothing
max_complexity = /obj/item/electronic_assembly::max_complexity_clothing
var/obj/item/clothing/clothing = null
/obj/item/electronic_assembly/clothing/ui_host()
@@ -22,23 +35,29 @@
return clothing
/obj/item/electronic_assembly/clothing/update_icon()
clothing.icon_state = "[initial(clothing.icon_state)][opened ? "-open" : ""]"
if(!clothing)
return
clothing.icon_state = opened ? "[initial(clothing.icon_state)]-open" : initial(clothing.icon_state)
clothing.ClearOverlays()
var/image/detail_overlay = image('icons/obj/assemblies/wearable_electronic_setups.dmi', "[initial(clothing.icon_state)][opened ? "-open" : ""]-color")
var/image/detail_overlay = image('icons/obj/assemblies/wearable_electronic_setups.dmi', "[clothing.icon_state]-color")
detail_overlay.color = detail_color
clothing.AddOverlays(detail_overlay)
clothing.update_clothing_icon()
if(hascall(clothing, "update_clothing_icon"))
call(clothing, "update_clothing_icon")()
// This is 'small' relative to the size of regular clothing assemblies.
/obj/item/electronic_assembly/clothing/small
max_components = IC_COMPONENTS_BASE / 2
max_complexity = IC_COMPLEXITY_BASE / 2
max_components = /obj/item/electronic_assembly/clothing::max_components_clothing_small
max_complexity = /obj/item/electronic_assembly/clothing::max_complexity_clothing_small
w_class = WEIGHT_CLASS_TINY
// Ditto.
/obj/item/electronic_assembly/clothing/large
max_components = IC_COMPONENTS_BASE * 2
max_complexity = IC_COMPLEXITY_BASE * 2
max_components = /obj/item/electronic_assembly/clothing::max_components_clothing_large
max_complexity = /obj/item/electronic_assembly/clothing::max_complexity_clothing_large
w_class = WEIGHT_CLASS_NORMAL
/obj/item/electronic_assembly/clothing/rename()
@@ -63,9 +82,9 @@
..()
// Does most of the repeatative setup.
/obj/item/clothing/proc/setup_integrated_circuit(new_type)
/obj/item/clothing/proc/setup_integrated_circuit(new_type, printed = FALSE)
// Set up the internal circuit holder.
IC = new new_type(src)
IC = new new_type(src, printed)
IC.clothing = src
IC.name = name
@@ -83,22 +102,22 @@
// Jumpsuit.
/obj/item/clothing/under/circuitry
name = "electronic jumpsuit"
desc = "It's a wearable case for electronics. This one is a black jumpsuit with wiring weaved into the fabric."
desc = "A wearable case for integrated electronics. This one is a black jumpsuit with wiring weaved into the fabric."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "jumpsuit"
item_state = "jumpsuit"
worn_state = "jumpsuit"
/obj/item/clothing/under/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing)
/obj/item/clothing/under/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing, printed)
return ..()
/obj/item/clothing/under/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_w_uniform_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
@@ -106,22 +125,22 @@
// Gloves.
/obj/item/clothing/gloves/circuitry
name = "electronic gloves"
desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \
desc = "A wearable case for integrated electronics. This one is a pair of black gloves, with wires woven into them. A small \
device with a screen is attached to the left glove."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "gloves"
item_state = "gloves"
/obj/item/clothing/gloves/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small)
/obj/item/clothing/gloves/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small, printed)
return ..()
/obj/item/clothing/gloves/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_gloves_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
@@ -139,22 +158,22 @@
// Glasses.
/obj/item/clothing/glasses/circuitry
name = "electronic goggles"
desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \
desc = "A wearable case for integrated electronics. This one is a pair of goggles, with wiring sticking out. \
Could this augment your vision?" // Sadly it won't, or at least not yet.
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "goggles"
item_state = "goggles"
/obj/item/clothing/glasses/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small)
/obj/item/clothing/glasses/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small, printed)
return ..()
/obj/item/clothing/glasses/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_glasses_str, slot_l_hand_str, slot_r_hand_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
@@ -177,22 +196,22 @@
// Shoes
/obj/item/clothing/shoes/circuitry
name = "electronic boots"
desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \
desc = "A wearable case for integrated electronics. This one is a pair of boots, with wires attached to a small \
cover."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "shoes"
item_state = "shoes"
/obj/item/clothing/shoes/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small)
/obj/item/clothing/shoes/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small, printed)
return ..()
/obj/item/clothing/shoes/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_shoes_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
@@ -200,44 +219,44 @@
// Head
/obj/item/clothing/head/circuitry
name = "electronic headwear"
desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \
desc = "A wearable case for integrated electronics. This one appears to be a very technical-looking piece that \
goes around the collar, with a heads-up-display attached on the right."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "head"
item_state = "head"
/obj/item/clothing/head/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small)
/obj/item/clothing/head/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small, printed)
return ..()
/obj/item/clothing/head/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_head_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
// Ear
// Ears
/obj/item/clothing/ears/circuitry
name = "electronic earwear"
desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset."
desc = "A wearable case for integrated electronics. This one appears to be a technical-looking headset."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "headset"
item_state = "headset"
/obj/item/clothing/ears/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small)
/obj/item/clothing/ears/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small, printed)
return ..()
/obj/item/clothing/ears/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_l_ear_str, slot_r_ear_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
@@ -245,22 +264,22 @@
// Exo-slot
/obj/item/clothing/suit/circuitry
name = "electronic chestpiece"
desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \
desc = "A wearable case for integrated electronics. This one appears to be a very technical-looking vest, that \
almost looks professionally made, however the wiring popping out betrays that idea."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "chest"
item_state = "chest"
/obj/item/clothing/suit/circuitry/Initialize()
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/large)
/obj/item/clothing/suit/circuitry/Initialize(mapload, printed = FALSE)
setup_integrated_circuit(/obj/item/electronic_assembly/clothing/large, printed)
return ..()
/obj/item/clothing/suit/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_wear_suit_str)
if(IC.detail_color && (slot in valid_slots))
if(IC?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", IC.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
@@ -0,0 +1,261 @@
/obj/item/radio/headset/circuitry
name = "electronic radio headset"
desc = "A radio headset modified to accept integrated circuitry. Takes encryption keys."
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = "headset"
item_state = "headset"
slot_flags = SLOT_EARS
ks1type = null
ks2type = null
// Internal assembly that stores the actual circuits installed inside the host item.
var/obj/item/electronic_assembly/clothing/small/circuit_assembly = null
// Built-in action circuit used to expose an activation button to the wearer/user.
var/obj/item/integrated_circuit/built_in/action_button/circuit_action = null
/obj/item/radio/headset/circuitry/Initialize(mapload, printed = FALSE)
. = ..()
setup_headset_integrated_circuit()
refresh_headset_sprite()
refresh_radio_state()
/obj/item/radio/headset/circuitry/Destroy()
QDEL_NULL(circuit_assembly)
circuit_action = null
return ..()
/obj/item/radio/headset/circuitry/proc/setup_headset_integrated_circuit()
if(circuit_assembly)
circuit_assembly.forceMove(src)
circuit_assembly.clothing = src
circuit_assembly.name = name
else
circuit_assembly = new /obj/item/electronic_assembly/clothing/small(src, TRUE)
circuit_assembly.clothing = src
circuit_assembly.name = name
QDEL_NULL(circuit_assembly.battery)
for(var/obj/item/integrated_circuit/C in circuit_assembly.contents)
C.assembly = circuit_assembly
circuit_action = locate(/obj/item/integrated_circuit/built_in/action_button) in circuit_assembly.contents
if(!circuit_action)
circuit_action = new(circuit_assembly)
circuit_assembly.force_add_circuit(circuit_action)
default_action_type = /datum/action/item_action/integrated_circuit
action_button_name = "Activate [capitalize_first_letters(name)]"
/obj/item/radio/headset/circuitry/proc/get_cloneable_assembly()
return circuit_assembly
/obj/item/radio/headset/circuitry/proc/get_clone_host_type()
return type
/obj/item/radio/headset/circuitry/feedback_hints(mob/user, distance, is_adjacent)
. = ..()
if(distance <= 1 && !circuit_assembly?.opened)
return
var/obj/item/electronic_assembly/E = get_cloneable_assembly()
if(E)
for(var/obj/item/integrated_circuit/IC in E.contents)
. += SPAN_NOTICE("It contains \a [IC].")
/obj/item/radio/headset/circuitry/proc/clone_with_integrated_assembly(atom/location, obj/item/integrated_circuit_printer/printer, obj/item/electronic_assembly/source_assembly)
if(!location || !printer || !source_assembly)
return null
var/obj/item/radio/headset/circuitry/new_headset = new type(location)
if(!new_headset || !new_headset.circuit_assembly)
if(new_headset)
qdel(new_headset)
return null
var/obj/item/electronic_assembly/clothing/small/internal_assembly = new_headset.circuit_assembly
if(!printer.copy_assembly_into_existing(source_assembly, internal_assembly))
qdel(new_headset)
return null
internal_assembly.forceMove(new_headset)
internal_assembly.clothing = new_headset
internal_assembly.name = source_assembly.name
internal_assembly.detail_color = source_assembly.detail_color
internal_assembly.opened = FALSE
internal_assembly.battery = null
for(var/obj/item/integrated_circuit/C in internal_assembly.contents)
C.assembly = internal_assembly
new_headset.name = source_assembly.name
new_headset.circuit_assembly = internal_assembly
new_headset.circuit_action = locate(/obj/item/integrated_circuit/built_in/action_button) in internal_assembly.contents
if(!new_headset.circuit_action)
new_headset.circuit_action = new(internal_assembly)
internal_assembly.force_add_circuit(new_headset.circuit_action)
new_headset.default_action_type = /datum/action/item_action/integrated_circuit
new_headset.action_button_name = "Activate [capitalize_first_letters(new_headset.name)]"
new_headset.refresh_headset_sprite()
new_headset.refresh_radio_state()
return new_headset
/obj/item/radio/headset/circuitry/proc/refresh_radio_state()
on = TRUE
set_frequency(PUB_FREQ)
set_listening(TRUE)
recalculateChannels(TRUE)
if(ismob(loc))
possibly_deactivate_in_loc()
/obj/item/radio/headset/circuitry/proc/refresh_headset_sprite()
icon = 'icons/obj/assemblies/wearable_electronic_setups.dmi'
contained_sprite = TRUE
icon_state = circuit_assembly?.opened ? "headset-open" : "headset"
item_state = "headset"
ClearOverlays()
if(circuit_assembly?.detail_color)
var/image/detail_overlay = image('icons/obj/assemblies/wearable_electronic_setups.dmi', "[icon_state]-color")
detail_overlay.color = circuit_assembly.detail_color
AddOverlays(detail_overlay)
if(hascall(src, "update_clothing_icon"))
call(src, "update_clothing_icon")()
/obj/item/radio/headset/circuitry/attack_self(mob/user)
return ..()
/obj/item/radio/headset/circuitry/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/integrated_circuit_printer))
var/obj/item/integrated_circuit_printer/P = attacking_item
if(P.scan_cloneable_item(src, user))
return TRUE
if(attacking_item.tool_behaviour == TOOL_CROWBAR && circuit_assembly)
circuit_assembly.attackby(attacking_item, user)
refresh_headset_sprite()
refresh_radio_state()
return TRUE
if(istype(attacking_item, /obj/item/encryptionkey))
. = ..()
refresh_radio_state()
return
if(circuit_assembly?.opened)
circuit_assembly.attackby(attacking_item, user)
refresh_headset_sprite()
return TRUE
return ..()
/obj/item/radio/headset/circuitry/AltClick(mob/user)
if(!circuit_assembly)
return ..()
if(!circuit_assembly.opened)
to_chat(user, SPAN_WARNING("The circuit panel is closed."))
return
return circuit_assembly.attack_self(user)
/obj/item/radio/headset/circuitry/verb/access_integrated_circuit()
set category = "Object.Equipped"
set name = "Access Integrated Circuit"
set src in usr
if(!circuit_assembly)
return
if(!circuit_assembly.opened)
to_chat(usr, SPAN_WARNING("The circuit panel is closed."))
return
circuit_assembly.attack_self(usr)
/obj/item/radio/headset/circuitry/equipped(mob/user, slot)
. = ..()
if(slot == slot_l_ear || slot == slot_r_ear || slot == slot_l_ear_str || slot == slot_r_ear_str)
refresh_radio_state()
refresh_headset_sprite()
/obj/item/radio/headset/circuitry/dropped(mob/user)
. = ..()
set_listening(FALSE, actual_setting = FALSE)
/obj/item/radio/headset/circuitry/Moved(atom/old_loc, forced)
. = ..()
possibly_deactivate_in_loc()
/obj/item/radio/headset/circuitry/build_additional_parts(mob/living/carbon/human/H, mob_icon, slot)
var/static/list/valid_slots
if(!valid_slots)
valid_slots = list(slot_l_ear_str, slot_r_ear_str)
if(circuit_assembly?.detail_color && (slot in valid_slots))
var/image/electronic_overlay = overlay_image(icon, "[item_state][slot_str_to_contained_flag(slot)]-color", circuit_assembly.detail_color, RESET_COLOR)
return electronic_overlay
return ..()
/obj/item/radio/headset/circuitry/proc/get_wearer()
if(!ishuman(loc))
return null
var/mob/living/carbon/human/H = loc
if(H.l_ear == src)
return H
if(H.r_ear == src)
return H
return null
/obj/item/radio/headset/circuitry/proc/private_output(message)
var/mob/living/carbon/human/H = get_wearer()
if(!H)
return
to_chat(H, SPAN_NOTICE("[message]"))
/obj/item/radio/headset/circuitry/proc/private_tts_output(message)
var/mob/living/carbon/human/H = get_wearer()
if(!H)
return
to_chat(H, SPAN_NOTICE("\The [src] states, \"[message]\""))
@@ -1,213 +1,311 @@
/*
* core/assemblies/generic.dm
* Generic handheld, large, wall-mounted, and specialty electronic assembly variants.
*/
// Generic subtypes without a lot of special code.
// Anchored pickup protection.
// This prevents anchored electronic assemblies from being picked up like normal items.
// Anchored assemblies still pass ordinary hand interaction through attack_self(), so closed
// assemblies expose their external inputs and opened assemblies expose the circuit UI.
/obj/item/electronic_assembly
var/supports_builtin_powernet = FALSE
var/supports_builtin_locomotion = FALSE
var/max_components_tiny_assembly = 12
var/max_complexity_tiny_assembly = 30
var/max_components_medium_assembly = 55
var/max_complexity_medium_assembly = 140
var/max_components_large_assembly = 110
var/max_complexity_large_assembly = 240
var/max_components_drone = 38
var/max_complexity_drone = 95
var/max_components_wallmount = 55
var/max_complexity_wallmount = 140
/obj/item/electronic_assembly/attack_hand(mob/user)
if(anchored)
if(!check_interactivity(user))
return TRUE
attack_self(user)
return TRUE
return ..()
/obj/item/electronic_assembly/mob_can_equip(mob/user, slot, disable_warning = FALSE, bypass_blocked_check = FALSE, is_overlay_check = FALSE)
if(anchored)
if(!disable_warning)
to_chat(user, SPAN_WARNING("\The [src] is anchored in place."))
return FALSE
return ..(user, slot, disable_warning, bypass_blocked_check, is_overlay_check)
// Small assemblies.
/obj/item/electronic_assembly/default
name = "type-a electronic assembly"
name = "basic small circuit case"
/obj/item/electronic_assembly/calc
name = "type-b electronic assembly"
name = "calculator small circuit case"
icon_state = "setup_small_calc"
desc = "It's a case, for building small electronics with. This one resembles a pocket calculator."
desc = "A case for building small electronic assemblies. This one resembles a pocket calculator."
/obj/item/electronic_assembly/clam
name = "type-c electronic assembly"
name = "clamshell small circuit case"
icon_state = "setup_small_clam"
desc = "It's a case, for building small electronics with. This one has a clamshell design."
desc = "A case for building small electronic assemblies. This one has a clamshell design."
/obj/item/electronic_assembly/simple
name = "type-d electronic assembly"
name = "compact small circuit case"
icon_state = "setup_small_simple"
desc = "It's a case, for building small electronics with. This one has a simple design."
desc = "A case for building small electronic assemblies. This one has a simple design."
/obj/item/electronic_assembly/hook
name = "type-e electronic assembly"
name = "belt-clip small circuit case"
icon_state = "setup_small_hook"
desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative."
desc = "A case for building small electronic assemblies. This one looks like it has a belt clip, but it's purely decorative."
/obj/item/electronic_assembly/pda
name = "type-f electronic assembly"
name = "PDA-style small circuit case"
icon_state = "setup_small_pda"
desc = "It's a case, for building small electronics with. This one resembles a PDA."
desc = "A case for building small electronic assemblies. This one resembles a PDA."
// Tiny assemblies.
/obj/item/electronic_assembly/tiny
name = "electronic device"
name = "tiny circuit case"
icon_state = "setup_device"
desc = "It's a case, for building tiny-sized electronics with."
desc = "A case for building tiny electronic assemblies."
w_class = WEIGHT_CLASS_TINY
max_components = IC_COMPONENTS_BASE / 2
max_complexity = IC_COMPLEXITY_BASE / 2
max_components = /obj/item/electronic_assembly::max_components_tiny_assembly
max_complexity = /obj/item/electronic_assembly::max_complexity_tiny_assembly
/obj/item/electronic_assembly/tiny/default
name = "type-a electronic device"
name = "basic tiny circuit case"
/obj/item/electronic_assembly/tiny/cylinder
name = "type-b electronic device"
name = "cylindrical tiny circuit case"
icon_state = "setup_device_cylinder"
desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design."
desc = "A case for building tiny electronic assemblies. This one has a cylindrical design."
/obj/item/electronic_assembly/tiny/scanner
name = "type-c electronic device"
name = "scanner tiny circuit case"
icon_state = "setup_device_scanner"
desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design."
desc = "A case for building tiny electronic assemblies. This one has a scanner-like design."
/obj/item/electronic_assembly/tiny/hook
name = "type-d electronic device"
name = "belt-clip tiny circuit case"
icon_state = "setup_device_hook"
desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative."
desc = "A case for building tiny electronic assemblies. This one looks like it has a belt clip, but it's purely decorative."
/obj/item/electronic_assembly/tiny/box
name = "type-e electronic device"
name = "boxy tiny circuit case"
icon_state = "setup_device_box"
desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design."
desc = "A case for building tiny electronic assemblies. This one has a boxy design."
// Medium assemblies.
/obj/item/electronic_assembly/medium
name = "electronic mechanism"
name = "medium circuit case"
icon_state = "setup_medium"
desc = "It's a case, for building medium-sized electronics with."
desc = "A case for building medium electronic assemblies."
w_class = WEIGHT_CLASS_NORMAL
max_components = IC_COMPONENTS_BASE * 2
max_complexity = IC_COMPLEXITY_BASE * 2
var/max_components_anomaly_drill = 30
var/max_complexity_anomaly_drill = 80
max_components = /obj/item/electronic_assembly::max_components_medium_assembly
max_complexity = /obj/item/electronic_assembly::max_complexity_medium_assembly
/obj/item/electronic_assembly/medium/default
name = "type-a electronic mechanism"
name = "basic medium circuit case"
/obj/item/electronic_assembly/medium/box
name = "type-b electronic mechanism"
name = "boxy medium circuit case"
icon_state = "setup_medium_box"
desc = "It's a case, for building medium-sized electronics with. This one has a boxy design."
desc = "A case for building medium electronic assemblies. This one has a boxy design."
/obj/item/electronic_assembly/medium/clam
name = "type-c electronic mechanism"
name = "clamshell medium circuit case"
icon_state = "setup_medium_clam"
desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design."
desc = "A case for building medium electronic assemblies. This one has a clamshell design."
/obj/item/electronic_assembly/medium/medical
name = "type-d electronic mechanism"
name = "medical medium circuit case"
icon_state = "setup_medium_med"
desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus."
desc = "A case for building medium electronic assemblies. This one resembles some type of medical apparatus."
/obj/item/electronic_assembly/medium/gun
name = "type-e electronic mechanism"
name = "tool-shaped medium circuit case"
icon_state = "setup_medium_gun"
item_state = "circuitgun"
desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, \
desc = "A case for building medium electronic assemblies. This one resembles a gun, or some type of tool, \
if you're feeling optimistic."
/obj/item/electronic_assembly/medium/radio
name = "type-f electronic mechanism"
name = "radio medium circuit case"
icon_state = "setup_medium_radio"
desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio."
desc = "A case for building medium electronic assemblies. This one resembles an old radio."
/obj/item/electronic_assembly/medium/anomaly_drill
name = "anomaly drill assembly"
desc = "A prebuilt electronic assembly designed to drill toward anomaly sensor coordinates."
icon_state = "setup_medium_drill"
w_class = WEIGHT_CLASS_NORMAL
max_components = /obj/item/electronic_assembly/medium::max_components_anomaly_drill
max_complexity = /obj/item/electronic_assembly/medium::max_complexity_anomaly_drill
can_anchor = FALSE
/obj/item/electronic_assembly/medium/anomaly_drill/Initialize(mapload, printed = FALSE)
. = ..()
var/obj/item/integrated_circuit/manipulation/xenoarch_precision_excavator/drill_controller = new(src)
drill_controller.removable = FALSE
drill_controller.assembly = src
force_add_circuit(drill_controller)
// Large assemblies.
// These get a built-in, non-removable power network interface.
/obj/item/electronic_assembly/large
name = "electronic machine"
name = "large circuit machine"
icon_state = "setup_large"
desc = "It's a case, for building large electronics with."
desc = "A case for building large electronic assemblies."
w_class = WEIGHT_CLASS_BULKY
max_components = IC_COMPONENTS_BASE * 4
max_complexity = IC_COMPLEXITY_BASE * 4
max_components = /obj/item/electronic_assembly::max_components_large_assembly
max_complexity = /obj/item/electronic_assembly::max_complexity_large_assembly
can_anchor = TRUE
supports_builtin_powernet = TRUE
/obj/item/electronic_assembly/large/Initialize(mapload, printed = FALSE)
. = ..()
add_builtin_powernet()
/obj/item/electronic_assembly/large/default
name = "type-a electronic machine"
name = "basic large circuit machine"
/obj/item/electronic_assembly/large/scope
name = "type-b electronic machine"
name = "oscilloscope large circuit machine"
icon_state = "setup_large_scope"
desc = "It's a case, for building large electronics with. This one resembles an oscilloscope."
desc = "A case for building large electronic assemblies. This one resembles an oscilloscope."
/obj/item/electronic_assembly/large/terminal
name = "type-c electronic machine"
name = "terminal large circuit machine"
icon_state = "setup_large_terminal"
desc = "It's a case, for building large electronics with. This one resembles a computer terminal."
desc = "A case for building large electronic assemblies. This one resembles a computer terminal."
/obj/item/electronic_assembly/large/arm
name = "type-d electronic machine"
name = "robotic arm large circuit machine"
icon_state = "setup_large_arm"
desc = "It's a case, for building large electronics with. This one resembles a robotic arm."
desc = "A case for building large electronic assemblies. This one resembles a robotic arm."
/obj/item/electronic_assembly/large/tall
name = "type-e electronic machine"
name = "tall large circuit machine"
icon_state = "setup_large_tall"
desc = "It's a case, for building large electronics with. This one has a tall design."
desc = "A case for building large electronic assemblies. This one has a tall design."
/obj/item/electronic_assembly/large/industrial
name = "type-f electronic machine"
name = "industrial large circuit machine"
icon_state = "setup_large_industrial"
desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery."
desc = "A case for building large electronic assemblies. This one resembles some kind of industrial machinery."
// Drone assemblies, which can move with the locomotion circuit.
/obj/item/electronic_assembly/drone
name = "electronic drone"
name = "circuit drone"
icon_state = "setup_drone"
desc = "It's a case, for building mobile electronics with."
desc = "A mobile case for building electronic assemblies."
w_class = WEIGHT_CLASS_NORMAL
max_components = IC_COMPONENTS_BASE * 1.5
max_complexity = IC_COMPLEXITY_BASE * 1.5
max_components = /obj/item/electronic_assembly::max_components_drone
max_complexity = /obj/item/electronic_assembly::max_complexity_drone
can_anchor = FALSE
supports_builtin_locomotion = TRUE
/obj/item/electronic_assembly/drone/Initialize(mapload, printed = FALSE)
. = ..()
add_builtin_locomotion()
/obj/item/electronic_assembly/drone/can_move()
return TRUE
/obj/item/electronic_assembly/drone/default
name = "type-a electronic drone"
name = "basic circuit drone"
/obj/item/electronic_assembly/drone/arms
name = "type-b electronic drone"
name = "armed circuit drone"
icon_state = "setup_drone_arms"
desc = "It's a case, for building mobile electronics with. This one is armed and dangerous."
desc = "A mobile case for building weapon-capable electronic assemblies."
/obj/item/electronic_assembly/drone/medbot
name = "type-d electronic drone"
name = "medical circuit drone"
icon_state = "setup_drone_medbot"
desc = "It's a case, for building mobile electronics with. This one resembles a Medibot."
desc = "A mobile case for building electronic assemblies. This one resembles a Medibot."
/obj/item/electronic_assembly/drone/genbot
name = "type-e electronic drone"
name = "utility circuit drone"
icon_state = "setup_drone_genbot"
desc = "It's a case, for building mobile electronics with. This one has a generic bot design."
desc = "A mobile case for building electronic assemblies. This one has a generic bot design."
/obj/item/electronic_assembly/drone/android
name = "type-f electronic drone"
name = "android circuit drone"
icon_state = "setup_drone_android"
desc = "It's a case, for building mobile electronics with. This one has a hominoid design."
desc = "A mobile case for building electronic assemblies. This one has a hominoid design."
// Wall mounted assemblies.
// These get a built-in, non-removable power network interface.
/obj/item/electronic_assembly/wallmount
name = "wall-mounted electronic assembly"
name = "medium wall-mounted circuit case"
icon_state = "setup_wallmount_medium"
desc = "It's a case, for building medium-sized electronics with. It has a magnetized \
desc = "A case for building medium electronic assemblies. It has a magnetized \
backing to allow it to stick to walls."
w_class = WEIGHT_CLASS_NORMAL
max_components = IC_COMPONENTS_BASE * 2
max_complexity = IC_COMPLEXITY_BASE * 2
var/max_components_wallmount_heavy = 110
var/max_complexity_wallmount_heavy = 240
var/max_components_wallmount_light = IC_COMPONENTS_BASE
var/max_complexity_wallmount_light = IC_COMPLEXITY_BASE
var/max_components_wallmount_tiny = 12
var/max_complexity_wallmount_tiny = 30
max_components = /obj/item/electronic_assembly::max_components_wallmount
max_complexity = /obj/item/electronic_assembly::max_complexity_wallmount
can_anchor = TRUE
supports_builtin_powernet = TRUE
/obj/item/electronic_assembly/wallmount/Initialize(mapload, printed = FALSE)
. = ..()
add_builtin_powernet()
/obj/item/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user)
if(get_dist(on_wall,user) > 1)
if(get_dist(on_wall, user) > 1)
return
var/ndir = get_dir(on_wall, user)
if(!(ndir in GLOB.cardinals))
return
var/turf/T = get_turf(user)
if(!istype(T, /turf/simulated/floor))
to_chat(user, SPAN_WARNING("You cannot place \the [src] on this spot!"))
return
playsound(src.loc, 'sound/machines/click.ogg', 75, 1)
user.visible_message("\The [user] attaches \the [src] to the wall.",
user.visible_message(
"\The [user] attaches \the [src] to the wall.",
SPAN_NOTICE("You attach \the [src] to the wall."),
"<span class='italics'>You hear clicking.</span>")
if(isrobot(user)) //Robots cannot unequip/drop items, for Safety Reasons.
"<span class='italics'>You hear clicking.</span>"
)
if(isrobot(user)) // Robots cannot unequip/drop items, for safety reasons.
forceMove(T)
user.drop_item(T)
else
user.drop_item(T)
anchored = TRUE
on_anchored()
set_pixel_offsets()
@@ -222,31 +320,58 @@
pixel_y = DIR2PIXEL_Y(dir)
/obj/item/electronic_assembly/wallmount/heavy
name = "heavy wall-mounted electronic assembly"
name = "large wall-mounted circuit machine"
icon_state = "setup_wallmount_large"
desc = "It's a case, for building large electronics with. It has a magnetized backing \
desc = "A case for building large electronic assemblies. It has a magnetized backing \
to allow it to stick to walls."
w_class = WEIGHT_CLASS_BULKY
max_components = IC_COMPONENTS_BASE * 4
max_complexity = IC_COMPLEXITY_BASE * 4
max_components = /obj/item/electronic_assembly/wallmount::max_components_wallmount_heavy
max_complexity = /obj/item/electronic_assembly/wallmount::max_complexity_wallmount_heavy
/obj/item/electronic_assembly/wallmount/light
name = "light wall-mounted electronic assembly"
name = "small wall-mounted circuit case"
icon_state = "setup_wallmount_small"
desc = "It's a case, for building small electronics with. It has a magnetized backing \
desc = "A case for building small electronic assemblies. It has a magnetized backing \
to allow it to stick to walls."
w_class = WEIGHT_CLASS_SMALL
max_components = IC_COMPONENTS_BASE
max_complexity = IC_COMPLEXITY_BASE
max_components = /obj/item/electronic_assembly/wallmount::max_components_wallmount_light
max_complexity = /obj/item/electronic_assembly/wallmount::max_complexity_wallmount_light
/obj/item/electronic_assembly/wallmount/tiny
name = "tiny wall-mounted electronic assembly"
name = "tiny wall-mounted circuit case"
icon_state = "setup_wallmount_tiny"
desc = "It's a case, for building tiny electronics with. It has a magnetized backing \
desc = "A case for building tiny electronic assemblies. It has a magnetized backing \
to allow it to stick to walls."
w_class = WEIGHT_CLASS_TINY
max_components = IC_COMPONENTS_BASE / 2
max_complexity = IC_COMPLEXITY_BASE / 2
max_components = /obj/item/electronic_assembly/wallmount::max_components_wallmount_tiny
max_complexity = /obj/item/electronic_assembly/wallmount::max_complexity_wallmount_tiny
// Built-in circuits.
/obj/item/electronic_assembly/proc/add_builtin_powernet()
if(!supports_builtin_powernet)
return
for(var/obj/item/integrated_circuit/passive/power/powernet/P in contents)
return
var/obj/item/integrated_circuit/passive/power/powernet/builtin/P = new(src)
P.removable = FALSE
P.assembly = src
force_add_circuit(P)
/obj/item/electronic_assembly/proc/add_builtin_locomotion()
if(!supports_builtin_locomotion)
return
for(var/obj/item/integrated_circuit/manipulation/locomotion/builtin/L in contents)
return
var/obj/item/integrated_circuit/manipulation/locomotion/builtin/L = new(src)
L.removable = FALSE
L.assembly = src
force_add_circuit(L)
/obj/item/electronic_assembly/proc/get_assembly_holder()
return src
@@ -1,7 +1,13 @@
/*
* core/device.dm
* Base device support for electronics-related items that are not themselves circuit assemblies.
*/
/obj/item/assembly/electronic_assembly
name = "electronic device"
desc = "It's a case for building electronics with. It can be attached to other small devices."
desc = "A case for building electronics that can attach to other small devices."
icon_state = "setup_device"
// Whether the assembly panel is open for direct circuit access.
var/opened = 0
var/obj/item/electronic_assembly/device/EA
@@ -61,17 +67,17 @@
set src in usr
set category = "Object"
set name = "Open/Close Device Assembly"
set desc = "Open or close device assembly!"
set desc = "Opens or closes the device assembly."
toggle_open(usr)
/obj/item/electronic_assembly/device
name = "electronic device"
icon_state = "setup_device"
desc = "It's a tiny electronic device with specific use for attaching to other devices."
desc = "A tiny electronic device designed to attach to other devices."
w_class = WEIGHT_CLASS_TINY
max_components = IC_COMPONENTS_BASE * 3/4
max_complexity = IC_COMPLEXITY_BASE * 3/4
max_components = /obj/item/electronic_assembly::max_components_device
max_complexity = /obj/item/electronic_assembly::max_complexity_device
var/obj/item/assembly/electronic_assembly/holder
var/obj/item/integrated_circuit/built_in/device_input/input
@@ -1,13 +1,21 @@
/*
* core/helpers.dm
* Utility procs for integrated electronics, including formatting, data conversion, cloning, list handling, and shared validation.
*/
/obj/item/integrated_circuit/proc/setup_io(list/io_list, io_type, list/io_default_list)
var/list/io_list_copy = io_list.Copy()
io_list.Cut()
var/i = 0
for(var/io_entry in io_list_copy)
i++
var/default_data = null
var/io_type_override = null
// Override the default data.
if(LAZYLEN(io_default_list)) // List containing special pin types that need to be added.
default_data = io_default_list["[i]"] // This is deliberately text because the index is a number in text form.
if(LAZYLEN(io_default_list))
default_data = io_default_list["[i]"]
// Override the pin type.
if(io_list_copy[io_entry])
@@ -19,25 +27,32 @@
io_list += new io_type(src, io_entry, default_data)
/obj/item/integrated_circuit/proc/set_pin_data(pin_type, pin_number, datum/new_data)
if (istype(new_data) && !isweakref(new_data))
if(istype(new_data) && !isweakref(new_data))
new_data = WEAKREF(new_data)
var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
if (!pin)
if(!pin)
CRASH("Invalid pin ref.")
return pin.write_data_to_pin(new_data)
/obj/item/integrated_circuit/proc/get_pin_data(pin_type, pin_number, var/resolve_weakrefs = TRUE)
var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
if(!pin)
return null
return pin.get_data(resolve_weakrefs)
/obj/item/integrated_circuit/proc/get_pin_data_as_type(pin_type, pin_number, as_type)
var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
if(!pin)
return null
return pin.data_as_type(as_type)
/obj/item/integrated_circuit/proc/activate_pin(pin_number)
var/datum/integrated_io/activate/A = activators[pin_number]
if(!A)
return FALSE
A.push_data(pin_number)
return TRUE
/datum/integrated_io/proc/get_data(var/resolve_weakrefs = TRUE)
if(resolve_weakrefs && isweakref(data))
@@ -45,6 +60,9 @@
return data
/obj/item/integrated_circuit/proc/get_pin_ref(pin_type, pin_number)
if(!pin_number || pin_number < 1)
return null
switch(pin_type)
if(IC_INPUT)
if(pin_number > inputs.len)
@@ -58,26 +76,389 @@
if(pin_number > activators.len)
return null
return activators[pin_number]
return null
/obj/item/integrated_circuit/proc/handle_wire(datum/integrated_io/pin, obj/item/integrated_electronics/tool)
if(!pin || !tool)
return FALSE
if(istype(tool, /obj/item/integrated_electronics/wirer))
var/obj/item/integrated_electronics/wirer/wirer = tool
if(pin)
wirer.wire(pin, usr)
return 1
wirer.wire(pin, usr)
return TRUE
else if(istype(tool, /obj/item/integrated_electronics/debugger))
if(istype(tool, /obj/item/integrated_electronics/debugger))
var/obj/item/integrated_electronics/debugger/debugger = tool
if(pin)
debugger.write_data(pin, usr)
return 1
return 0
debugger.write_data(pin, usr)
return TRUE
return FALSE
/proc/XorEncrypt(string, key)
if(!string || !key ||!istext(string)||!istext(key))
return
if(!string || !key || !istext(string) || !istext(key))
return null
var/r
for(var/i = 1 to length(string))
r += ascii2text(text2ascii(string,i) ^ text2ascii(key,(i-1)%length(string)+1))
r += ascii2text(text2ascii(string, i) ^ text2ascii(key, (i - 1) % length(key) + 1))
return r
// ------------------------------------------------------------
// Integrated circuit helper procs.
// These are intentionally generic so new circuits can reuse them
// instead of duplicating null, list, text, number, and boolean checks.
// ------------------------------------------------------------
/proc/ic_is_null(value)
return isnull(value)
/proc/ic_is_text(value)
return istext(value)
/proc/ic_is_number(value)
return isnum(value)
/proc/ic_is_list(value)
return islist(value)
/proc/ic_is_ref(value)
if(isweakref(value))
return TRUE
if(istype(value, /datum))
return TRUE
return FALSE
/proc/ic_is_safe_ref_atom(atom/A)
if(!istype(A))
return FALSE
if(istype(A, /mob/abstract/ghost))
return FALSE
return TRUE
/proc/ic_truthy(value)
if(isnull(value))
return FALSE
if(isnum(value))
return value != 0
if(istext(value))
return length(value) > 0
if(islist(value))
var/list/L = value
return L.len > 0
return TRUE
/proc/ic_falsey(value)
return !ic_truthy(value)
/proc/ic_safe_number(value, default_value = 0)
if(isnum(value))
return value
if(istext(value))
var/converted = text2num(value)
if(!isnull(converted))
return converted
return default_value
/proc/ic_safe_text(value, default_value = "")
if(isnull(value))
return default_value
if(istext(value))
return value
if(isnum(value))
return num2text(value)
if(isweakref(value))
var/datum/weakref/W = value
var/datum/resolved = W.resolve()
if(resolved)
return "[resolved]"
return default_value
return "[value]"
/proc/ic_safe_bool(value)
return ic_truthy(value)
/proc/ic_display_value(value)
if(isnull(value))
return "null"
if(isweakref(value))
var/datum/weakref/W = value
var/datum/resolved = W.resolve()
if(resolved)
return "[resolved]"
return "null ref"
if(islist(value))
var/list/L = value
return ic_list_to_text(L, ", ")
if(istext(value))
return value
if(isnum(value))
return num2text(value)
return "[value]"
/proc/ic_type_text(value)
if(isnull(value))
return "null"
if(isweakref(value))
return "weakref"
if(isnum(value))
return "number"
if(istext(value))
return "string"
if(islist(value))
return "list"
if(istype(value, /datum))
return "ref"
return "unknown"
/proc/ic_list_to_text(list/L, separator = ", ")
if(!islist(L))
return ""
var/list/output = list()
for(var/entry in L)
output += ic_display_value(entry)
return jointext(output, separator)
/proc/ic_text_to_list(text, separator = ",")
if(!istext(text))
return list()
var/list/output = list()
var/list/split_values = splittext(text, separator)
for(var/entry in split_values)
output += trim(entry)
return output
/proc/ic_copy_list(list/L)
if(!islist(L))
return list()
return L.Copy()
/proc/ic_filter_nulls(list/L)
if(!islist(L))
return list()
var/list/output = list()
for(var/entry in L)
if(!isnull(entry))
output += entry
return output
/proc/ic_list_contains(list/L, value)
if(!islist(L))
return FALSE
return L.Find(value) ? TRUE : FALSE
/proc/ic_list_length(list/L)
if(!islist(L))
return 0
return L.len
/proc/ic_clamp_number(value, minimum, maximum)
var/number = ic_safe_number(value, minimum)
if(number < minimum)
return minimum
if(number > maximum)
return maximum
return number
/proc/ic_percent(value, maximum)
var/number = ic_safe_number(value, 0)
var/max_number = ic_safe_number(maximum, 0)
if(max_number <= 0)
return 0
return (number / max_number) * 100
/proc/ic_range_check(value, minimum, maximum)
var/number = ic_safe_number(value, 0)
if(number < minimum)
return -1
if(number > maximum)
return 1
return 0
/proc/ic_range_text(value, minimum, maximum)
var/range_result = ic_range_check(value, minimum, maximum)
if(range_result < 0)
return "low"
if(range_result > 0)
return "high"
return "normal"
/proc/ic_format_status(label, value)
return "[ic_safe_text(label, "Status")]: [ic_display_value(value)]"
/proc/ic_format_warning(label, value)
return "WARNING: [ic_safe_text(label, "Value")] [ic_display_value(value)]"
/proc/ic_format_error(label, value)
return "ERROR: [ic_safe_text(label, "Value")] [ic_display_value(value)]"
/proc/ic_join_lines(list/L)
if(!islist(L))
return ""
var/list/output = list()
for(var/entry in L)
if(isnull(entry))
continue
output += ic_display_value(entry)
return jointext(output, "\n")
/proc/ic_make_saveable_value(value)
if(isnull(value) || isnum(value) || istext(value))
return value
if(isweakref(value))
var/datum/weakref/W = value
var/datum/resolved = W.resolve()
if(resolved)
return "[resolved]"
return null
if(islist(value))
var/list/source_list = value
var/list/new_list = list()
for(var/entry in source_list)
new_list += ic_make_saveable_value(entry)
return new_list
return "[value]"
/proc/ic_sanitize_template_value(value)
var/text_value = ic_display_value(value)
text_value = replacetext(text_value, "\n", " ")
text_value = replacetext(text_value, ascii2text(13), " ")
return text_value
/proc/ic_apply_template(template, value_1 = null, value_2 = null, value_3 = null, value_4 = null, value_5 = null, value_6 = null, value_7 = null, value_8 = null)
if(!istext(template))
return ""
var/output = template
output = replacetext(output, "{1}", ic_sanitize_template_value(value_1))
output = replacetext(output, "{2}", ic_sanitize_template_value(value_2))
output = replacetext(output, "{3}", ic_sanitize_template_value(value_3))
output = replacetext(output, "{4}", ic_sanitize_template_value(value_4))
output = replacetext(output, "{5}", ic_sanitize_template_value(value_5))
output = replacetext(output, "{6}", ic_sanitize_template_value(value_6))
output = replacetext(output, "{7}", ic_sanitize_template_value(value_7))
output = replacetext(output, "{8}", ic_sanitize_template_value(value_8))
return output
/proc/ic_get_nested_list_value(list/L, index, default_value = null)
if(!islist(L))
return default_value
var/number_index = round(ic_safe_number(index, 0))
if(number_index < 1 || number_index > L.len)
return default_value
return L[number_index]
/proc/ic_set_nested_list_value(list/L, index, value)
if(!islist(L))
return list()
var/number_index = round(ic_safe_number(index, 0))
if(number_index < 1 || number_index > IC_MAX_LIST_LENGTH)
return L
while(L.len < number_index)
L += null
L[number_index] = value
return L
/proc/ic_number_or_null(value)
if(isnum(value))
return value
if(istext(value))
var/converted = text2num(value)
if(!isnull(converted))
return converted
return null
/proc/ic_add_numbers(value_1, value_2)
var/A = ic_number_or_null(value_1)
var/B = ic_number_or_null(value_2)
if(isnull(A) || isnull(B))
return null
return A + B
/proc/ic_subtract_numbers(value_1, value_2)
var/A = ic_number_or_null(value_1)
var/B = ic_number_or_null(value_2)
if(isnull(A) || isnull(B))
return null
return A - B
/proc/ic_multiply_numbers(value_1, value_2)
var/A = ic_number_or_null(value_1)
var/B = ic_number_or_null(value_2)
if(isnull(A) || isnull(B))
return null
return A * B
/proc/ic_divide_numbers(value_1, value_2)
var/A = ic_number_or_null(value_1)
var/B = ic_number_or_null(value_2)
if(isnull(A) || isnull(B) || B == 0)
return null
return A / B
@@ -29,6 +29,9 @@ a creative player the means to solve many problems. Circuits are held inside an
/obj/item/integrated_circuit/proc/on_data_written() //Override this for special behaviour when new data gets pushed to the circuit.
return
/obj/item/integrated_circuit/proc/get_printer_spawn_flags()
return spawn_flags
/obj/item/integrated_circuit/Destroy()
for(var/datum/integrated_io/I in inputs)
qdel(I)
@@ -70,7 +73,7 @@ a creative player the means to solve many problems. Circuits are held inside an
/obj/item/integrated_circuit/verb/rename_component()
set name = "Rename Circuit"
set category = "Object"
set desc = "Rename your circuit, useful to stay organized."
set desc = "Renames the circuit to make assemblies easier to organize."
var/mob/M = usr
if(!check_interactivity(M))
@@ -175,7 +178,8 @@ a creative player the means to solve many problems. Circuits are held inside an
HTML += "<br><span class='highlight'>Power Draw: [power_draw_idle] W (Idle)</span>"
if(power_draw_per_use)
HTML += "<br><span class='highlight'>Power Draw: [power_draw_per_use] W (Active)</span>" // Borgcode says that powercells' checked_use() takes joules as input.
HTML += "<br><span class='highlight'>[extended_desc]</span>"
if(extended_desc)
HTML += "<br><span class='highlight'>[extended_desc]</span>"
var/datum/browser/B = new(user, assembly ? "assembly-[REF(assembly)]" : "circuit-[REF(src)]", (displayed_name && displayed_name != name) ? "[displayed_name] ([name])" : name, window_width, window_height)
B.set_content(HTML.Join())
@@ -319,7 +323,7 @@ a creative player the means to solve many problems. Circuits are held inside an
/obj/item/integrated_circuit/proc/pull_data()
for(var/datum/integrated_io/I in inputs)
I.push_data()
I.pull_data()
/obj/item/integrated_circuit/proc/draw_idle_power()
if(assembly)
@@ -1,6 +1,6 @@
/*
Pins both hold data for circuits, as well move data between them. Some also cause circuits to do their function. DATA_CHANNEL pins are the data holding/moving kind,
where as PULSE_CHANNEL causes circuits to work() when their pulse hits them.
whereas PULSE_CHANNEL causes circuits to work() when their pulse hits them.
A visualization of how pins work is below. Imagine the below image involves an addition circuit.
@@ -68,14 +68,15 @@ list[](
if(islist(input))
var/list/my_list = input
var/result = "list\[[my_list.len]\]("
if(my_list.len)
var/list_length = length(my_list)
var/result = "list\[[list_length]\]("
if(list_length)
result += "<br>"
var/pos = 0
for(var/line in my_list)
result += "[display_data(line)]"
pos++
if(pos != my_list.len)
if(pos != list_length)
result += ",<br>"
result += "<br>"
result += ")"
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,8 @@
/*
* core/special_pins/boolean_pin.dm
* Boolean pin type behavior and conversion for TRUE/FALSE style circuit values.
*/
// These pins only contain 0 or 1. Null is not allowed.
/datum/integrated_io/boolean
name = "boolean pin"
@@ -1,3 +1,8 @@
/*
* core/special_pins/char_pin.dm
* Character/string single-character pin behavior and validation.
*/
// These pins can only contain a 1 character string or null.
/datum/integrated_io/char
name = "char pin"
@@ -1,3 +1,8 @@
/*
* core/special_pins/color_pin.dm
* Color pin parsing, validation, and display handling for circuit color values.
*/
// These pins can only contain a color (in the form of #FFFFFF) or null.
/datum/integrated_io/color
name = "color pin"
@@ -13,18 +18,19 @@
if(isnull(new_data) || istext(new_data))
if(istext(new_data))
new_data = uppertext(new_data)
if(length(new_data) != 7) // We can hex if we want to, we can leave your strings behind
return // Cause your strings don't hex and if they don't hex
var/friends = copytext(new_data, 2, 8) // Well they're are no strings of mine
// I say, we can go where we want to, a place where they will never find
if(length(new_data) != 7)
return
if(copytext(new_data, 1, 2) != "#")
return
var/hex_digits = copytext(new_data, 2, 8)
var/safety_dance = 1
while(safety_dance >= 7) // And we can act like we come from out of this world.log
var/hex = copytext(friends, safety_dance, safety_dance+1)
if(!(hex in list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")))
return // Leave the fake one far behind,
while(safety_dance <= 6)
var/hex = copytext(hex_digits, safety_dance, safety_dance + 1)
if(!findtext("0123456789ABCDEF", hex))
return
safety_dance++
data = new_data // And we can hex
data = new_data
holder.on_data_written()
// This randomizes the color.
@@ -1,3 +1,8 @@
/*
* core/special_pins/dir_pin.dm
* Direction pin conversion and validation for BYOND direction constants.
*/
// These pins can only contain directions (1,2,4,8...) or null.
/datum/integrated_io/dir
name = "dir pin"
@@ -1,4 +1,12 @@
// These pins contain a list. Null is not allowed.
/*
* core/special_pins/list_pin.dm
* List pin serialization, display, cloning, and safety handling for list-valued circuit data.
*/
// These pins contain a list.
// Lists may contain strings, numbers, weakrefs, nested lists, or null.
// Individual circuits are responsible for deciding what data types they accept.
/datum/integrated_io/list
name = "list pin"
data = list()
@@ -6,14 +14,10 @@
/datum/integrated_io/list/ask_for_pin_data(mob/user)
interact(user)
// Positionless remove/Edit are a bit weird,
// not sure if adding these buttons is quite a good idea.
// They introduce uncertainty, since, in case of 2 elements,
// they will work just with the 1st one
/datum/integrated_io/list/proc/interact(mob/user)
var/list/my_list = data
var/t = "<h2>[src]</h2><br>"
t += "List length: [my_list.len]<br>"
t += "List length: [length(my_list)]<br>"
t += "<a href='byond://?src=[REF(src)]'>Refresh</a> | "
t += "<a href='byond://?src=[REF(src)];add=1'>Add</a> | "
t += "<a href='byond://?src=[REF(src)];remove=1'>Remove</a> | "
@@ -21,113 +25,161 @@
t += "<a href='byond://?src=[REF(src)];swap=1'>Swap</a> | "
t += "<a href='byond://?src=[REF(src)];clear=1'>Clear</a><br>"
t += "<hr>"
// Iterating by index simplifies editing/deletion in game,
// since the href_list["pos"] var is consistent
for(var/i = 1; i <= length(my_list); i++)
t += "#[i] | [display_data(my_list[i])] | "
t += "<a href='byond://?src=[REF(src)];edit=1;pos=[i]'>Edit</a> | "
t += "<a href='byond://?src=[REF(src)];remove=1;pos=[i]'>Remove</a><br>"
var/datum/browser/B = new(user, "list_pin_[REF(src)]", null, 500, 400)
B.set_content(t)
B.open(FALSE)
/datum/integrated_io/list/proc/add_to_list(mob/user, new_entry)
if(!new_entry && user)
new_entry = ask_for_data_type(user)
// is_valid can't be used here, since is_valid checks "data" and has no arguments
if(!isnull(new_entry))
Add(new_entry)
if(isnull(new_entry) && user)
new_entry = ask_for_data_type(user, null, list("string", "number", "null"))
Add(new_entry)
/datum/integrated_io/list/proc/Add(new_entry)
if(!islist(data))
data = list()
var/list/my_list = data
if(my_list.len > IC_MAX_LIST_LENGTH)
if(length(my_list) >= IC_MAX_LIST_LENGTH)
my_list.Cut(1, 2)
my_list.Add(new_entry)
my_list += list(new_entry)
holder.on_data_written()
/datum/integrated_io/list/proc/remove_from_list_by_position(mob/user, position)
var/list/my_list = data
if(!my_list.len)
if(!length(my_list))
to_chat(user, SPAN_WARNING("The list is empty, there's nothing to remove."))
return
if(!position)
if(isnull(position))
return
if(position > my_list.len)
position = round(position)
if(position < 1 || position > length(my_list))
return
var/target_entry = my_list[position]
// Should be able to remove nulls, since we can add a null to the list from the outside.
my_list -= target_entry
my_list.Cut(position, position + 1)
holder.on_data_written()
/datum/integrated_io/list/proc/remove_from_list(mob/user, target_entry)
var/list/my_list = data
if(!my_list.len)
if(!length(my_list))
to_chat(user, SPAN_WARNING("The list is empty, there's nothing to remove."))
return
if(!target_entry)
if(isnull(target_entry))
target_entry = input("Which piece of data do you want to remove?", "Remove") as null|anything in my_list
if(target_entry)
my_list -= target_entry
if(!isnull(target_entry))
var/position = my_list.Find(target_entry)
if(position)
my_list.Cut(position, position + 1)
holder.on_data_written()
/datum/integrated_io/list/proc/edit_in_list(mob/user, target_entry)
var/list/my_list = data
if(!my_list.len)
if(!length(my_list))
to_chat(user, SPAN_WARNING("The list is empty, there's nothing to modify."))
return
if(!target_entry)
if(isnull(target_entry))
target_entry = input("Which piece of data do you want to edit?", "Edit") as null|anything in my_list
if(target_entry)
var/edited_entry = ask_for_data_type(user, target_entry)
var/i = my_list.Find(target_entry)
if(edited_entry)
my_list[i] = edited_entry
/datum/integrated_io/list/proc/edit_in_list_by_position(mob/user, var/position)
if(!isnull(target_entry))
var/i = my_list.Find(target_entry)
if(i)
var/edited_entry = ask_for_data_type(user, target_entry, list("string", "number", "null"))
my_list[i] = edited_entry
holder.on_data_written()
/datum/integrated_io/list/proc/edit_in_list_by_position(mob/user, position)
var/list/my_list = data
if(!my_list.len)
if(!length(my_list))
to_chat(user, SPAN_WARNING("The list is empty, there's nothing to modify."))
return
if(!position)
return
if(position > my_list.len)
return
var/target_entry = my_list[position]
if(target_entry)
var/edited_entry = ask_for_data_type(user, target_entry)
if(edited_entry)
my_list[position] = edited_entry
/datum/integrated_io/list/proc/swap_inside_list(mob/user, var/first_target, var/second_target)
if(isnull(position))
return
position = round(position)
if(position < 1 || position > length(my_list))
return
var/target_entry = my_list[position]
var/edited_entry = ask_for_data_type(user, target_entry, list("string", "number", "null"))
my_list[position] = edited_entry
holder.on_data_written()
/datum/integrated_io/list/proc/swap_inside_list(mob/user, first_target, second_target)
var/list/my_list = data
if(my_list.len <= 1)
if(length(my_list) <= 1)
to_chat(user, SPAN_WARNING("The list is empty, or too small to do any meaningful swapping."))
return
if(!first_target)
if(isnull(first_target))
first_target = input("Which piece of data do you want to swap? (1)", "Swap") as null|anything in my_list
if(first_target)
if(!second_target)
second_target = input("Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target
if(isnull(first_target))
return
if(second_target)
var/first_pos = my_list.Find(first_target)
var/second_pos = my_list.Find(second_target)
my_list.Swap(first_pos, second_pos)
if(isnull(second_target))
second_target = input("Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target
if(isnull(second_target))
return
var/first_pos = my_list.Find(first_target)
var/second_pos = my_list.Find(second_target)
if(first_pos && second_pos)
my_list.Swap(first_pos, second_pos)
holder.on_data_written()
/datum/integrated_io/list/proc/clear_list(mob/user)
var/list/my_list = data
my_list.Cut()
holder.on_data_written()
/datum/integrated_io/list/scramble()
var/list/my_list = data
my_list = shuffle(my_list)
data = shuffle(my_list)
push_data()
/datum/integrated_io/list/write_data_to_pin(var/new_data)
if(islist(new_data))
var/list/new_list = new_data
// Sanitize the input - no nulls allowed.
while(new_list.Remove(null) == 1)
data = new_list.Copy()
if(isnull(new_data))
data = list()
holder.on_data_written()
return
if(!islist(new_data))
return
var/list/new_list = new_data
var/list/sanitized = list()
for(var/value in new_list)
if(sanitized.len >= IC_MAX_LIST_LENGTH)
break
if(isnull(value) || isnum(value) || istext(value) || isweakref(value) || islist(value))
sanitized += list(value)
data = sanitized
holder.on_data_written()
/datum/integrated_io/list/display_pin_type()
return IC_FORMAT_LIST
@@ -135,8 +187,9 @@
/datum/integrated_io/list/Topic(href, href_list, state = GLOB.always_state)
if(!holder.check_interactivity(usr))
return
if(..())
return 1
return TRUE
if(href_list["add"])
add_to_list(usr)
@@ -159,5 +212,7 @@
else
edit_in_list(usr)
holder.interact(usr) // Refresh the main UI,
interact(usr) // and the list UI.
holder.interact(usr)
interact(usr)
return TRUE
@@ -1,3 +1,8 @@
/*
* core/special_pins/number_pin.dm
* Number pin conversion, validation, and formatting.
*/
// These pins can only contain numbers (int and floating point) or null.
/datum/integrated_io/number
name = "number pin"
@@ -1,3 +1,8 @@
/*
* core/special_pins/ref_pin.dm
* Reference pin behavior for passing object references between circuits.
*/
// These pins only contain weakrefs or null.
/datum/integrated_io/ref
name = "ref pin"
@@ -1,3 +1,8 @@
/*
* core/special_pins/string_pin.dm
* String pin conversion, validation, and formatting.
*/
// These pins can only contain text and null.
/datum/integrated_io/string
name = "string pin"
@@ -1,3 +1,8 @@
/*
* core/tools.dm
* Tools used to manipulate integrated electronics, such as wiring, debugging, and assembly maintenance helpers.
*/
#define WIRE "wire"
#define WIRING "wiring"
#define UNWIRE "unwire"
@@ -176,6 +181,7 @@
icon_state = "detailer"
item_flags = ITEM_FLAG_NO_BLUDGEON
w_class = WEIGHT_CLASS_SMALL
// User-selected detail color used for assembly and wearable overlays.
var/detail_color = COLOR_ASSEMBLY_WHITE
var/static/list/color_list = list(
"black" = COLOR_ASSEMBLY_BLACK,
@@ -217,7 +223,7 @@
/obj/item/storage/bag/circuits
name = "circuit kit"
desc = "This kit is essential for any circuitry projects."
desc = "A kit containing basic tools and parts for circuit projects."
icon = 'icons/obj/assemblies/electronic_tools.dmi'
icon_state = "circuit_kit"
w_class = WEIGHT_CLASS_NORMAL
@@ -279,7 +285,7 @@
/obj/item/storage/bag/circuits/mini
name = "circuit box"
desc = "Used to partition categories of circuits, for a neater workspace."
desc = "Stores circuits by category for easier organization."
w_class = WEIGHT_CLASS_SMALL
display_contents_with_number = TRUE
pickup_blacklist = list(
@@ -299,7 +305,7 @@
/obj/item/storage/bag/circuits/mini/arithmetic
name = "arithmetic circuit box"
desc = "Warning: Contains math."
desc = "Contains arithmetic and numeric utility circuits."
icon_state = "box_arithmetic"
spawn_types = list(
/obj/item/integrated_circuit/arithmetic
@@ -310,7 +316,7 @@
/obj/item/storage/bag/circuits/mini/trig
name = "trig circuit box"
desc = "Danger: Contains more math."
desc = "Contains trigonometric circuits."
icon_state = "box_trig"
spawn_types = list(
/obj/item/integrated_circuit/trig
@@ -321,7 +327,7 @@
/obj/item/storage/bag/circuits/mini/input
name = "input circuit box"
desc = "Tell these circuits everything you know."
desc = "Contains circuits that read data from the surrounding environment."
icon_state = "box_input"
spawn_types = list(
/obj/item/integrated_circuit/input
@@ -332,7 +338,7 @@
/obj/item/storage/bag/circuits/mini/output
name = "output circuit box"
desc = "Circuits to interface with the world beyond itself."
desc = "Contains circuits that display, announce, or transmit data."
icon_state = "box_output"
spawn_types = list(
/obj/item/integrated_circuit/output
@@ -343,7 +349,7 @@
/obj/item/storage/bag/circuits/mini/memory
name = "memory circuit box"
desc = "Machines can be quite forgetful without these."
desc = "Contains memory circuits for storing circuit data."
icon_state = "box_memory"
spawn_types = list(
/obj/item/integrated_circuit/memory
@@ -354,7 +360,7 @@
/obj/item/storage/bag/circuits/mini/logic
name = "logic circuit box"
desc = "May or may not be Turing complete."
desc = "Contains logic circuits for boolean-style control and comparisons."
icon_state = "box_logic"
spawn_types = list(
/obj/item/integrated_circuit/logic
@@ -365,18 +371,55 @@
/obj/item/storage/bag/circuits/mini/time
name = "time circuit box"
desc = "No time machine parts, sadly."
desc = "Contains timer and clock circuits."
icon_state = "box_time"
spawn_types = list(
/obj/item/integrated_circuit/time
)
/obj/item/storage/bag/circuits/mini/time/fill()
var/list/basic_time_circuits = list(
/obj/item/integrated_circuit/time/delay,
/obj/item/integrated_circuit/time/delay/five_sec,
/obj/item/integrated_circuit/time/delay/one_sec,
/obj/item/integrated_circuit/time/delay/half_sec,
/obj/item/integrated_circuit/time/delay/tenth_sec,
/obj/item/integrated_circuit/time/ticker/fast,
/obj/item/integrated_circuit/time/ticker,
/obj/item/integrated_circuit/time/ticker/slow,
/obj/item/integrated_circuit/time/ticker/very_slow,
/obj/item/integrated_circuit/time/clock
)
for(var/circuit_type in basic_time_circuits)
for(var/i in 1 to 4)
new circuit_type(src)
/obj/item/storage/bag/circuits/mini/time/all
spawn_flags_to_use = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/storage/bag/circuits/mini/time/all/fill()
var/list/all_time_circuits = list(
/obj/item/integrated_circuit/time/delay,
/obj/item/integrated_circuit/time/delay/five_sec,
/obj/item/integrated_circuit/time/delay/one_sec,
/obj/item/integrated_circuit/time/delay/half_sec,
/obj/item/integrated_circuit/time/delay/tenth_sec,
/obj/item/integrated_circuit/time/delay/custom,
/obj/item/integrated_circuit/time/ticker/rapid,
/obj/item/integrated_circuit/time/ticker/fast,
/obj/item/integrated_circuit/time/ticker,
/obj/item/integrated_circuit/time/ticker/slow,
/obj/item/integrated_circuit/time/ticker/very_slow,
/obj/item/integrated_circuit/time/ticker/custom,
/obj/item/integrated_circuit/time/clock
)
for(var/circuit_type in all_time_circuits)
for(var/i in 1 to 4)
new circuit_type(src)
/obj/item/storage/bag/circuits/mini/reagents
name = "reagent circuit box"
desc = "Unlike most electronics, these circuits are supposed to come in contact with liquids."
desc = "Contains circuits for handling reagents and containers."
icon_state = "box_reagents"
spawn_types = list(
/obj/item/integrated_circuit/reagent
@@ -387,7 +430,7 @@
/obj/item/storage/bag/circuits/mini/transfer
name = "transfer circuit box"
desc = "Useful for moving data representing something arbitrary to another arbitrary virtual place."
desc = "Contains circuits for routing data between pins and devices."
icon_state = "box_transfer"
spawn_types = list(
/obj/item/integrated_circuit/transfer
@@ -398,7 +441,7 @@
/obj/item/storage/bag/circuits/mini/converter
name = "converter circuit box"
desc = "Transform one piece of data to another type of data with these."
desc = "Contains circuits for converting data between supported formats."
icon_state = "box_converter"
spawn_types = list(
/obj/item/integrated_circuit/converter
@@ -409,7 +452,7 @@
/obj/item/storage/bag/circuits/mini/smart
name = "smart box"
desc = "Sentience not included."
desc = "Contains smart control circuits for navigation and automation."
icon_state = "box_ai"
spawn_types = list(
/obj/item/integrated_circuit/smart
@@ -420,7 +463,7 @@
/obj/item/storage/bag/circuits/mini/manipulation
name = "manipulation box"
desc = "Make your machines actually useful with these."
desc = "Contains circuits that perform physical actions through an assembly."
icon_state = "box_manipulation"
spawn_types = list(
/obj/item/integrated_circuit/manipulation
@@ -431,7 +474,7 @@
/obj/item/storage/bag/circuits/mini/power
name = "power circuit box"
desc = "Electronics generally require electricity."
desc = "Contains circuits that generate, receive, or transmit power."
icon_state = "box_power"
spawn_types = list(
/obj/item/integrated_circuit/passive/power,
@@ -1,3 +1,8 @@
/*
* passive/passive.dm
* Base passive circuit category for circuits that provide background behavior instead of triggered work.
*/
// 'Passive' components do not have any pins, and instead contribute in some form to the assembly holding them.
/obj/item/integrated_circuit/passive
inputs = list()
@@ -1,7 +1,12 @@
/*
* passive/power.dm
* Passive power circuits and power-network integration for assemblies.
*/
/obj/item/integrated_circuit/passive/power
name = "power thingy"
desc = "Does power stuff."
desc = "Base type for integrated power circuits."
complexity = 5
origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2, TECH_DATA = 2)
category_text = "Power - Passive"
@@ -12,14 +17,13 @@
// For calculators.
/obj/item/integrated_circuit/passive/power/solar_cell
name = "tiny photovoltaic cell"
desc = "It's a very tiny solar cell, generally used in calculators."
desc = "A very small solar cell for low-power assemblies."
extended_desc = "The cell generates up to 100 W of energy in optimal lighting conditions. Less light will result in less power being generated."
icon_state = "solar_cell"
complexity = 8
origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_DATA = 2)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/// Multiplied by amount of lumens to get amount of power to generate.
var/power_factor = 15
/obj/item/integrated_circuit/passive/power/solar_cell/make_energy()
@@ -33,7 +37,7 @@
// For fat machines that need fat power, like drones.
/obj/item/integrated_circuit/passive/power/relay
name = "tesla power relay"
desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them."
desc = "Connects to nearby APCs wirelessly and draws power from them."
w_class = WEIGHT_CLASS_NORMAL
extended_desc = "The siphon generates 1 kW of energy, so long as an APC is in the same room, with a cell that has energy. It will always drain \
from the 'equipment' power channel."
@@ -46,7 +50,7 @@
// For really fat machines.
/obj/item/integrated_circuit/passive/power/relay/large
name = "large tesla power relay"
desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industiral size!"
desc = "Connects to nearby APCs wirelessly and draws a larger amount of power from them."
w_class = WEIGHT_CLASS_BULKY
extended_desc = "The siphon generates 4 kW of energy, so long as an APC is in the same room, with a cell that has energy. It will always drain \
from the 'equipment' power channel."
@@ -68,7 +72,7 @@
// For implants.
/obj/item/integrated_circuit/passive/power/metabolic_siphon
name = "metabolic siphon"
desc = "A complicated piece of technology which converts bodily nutriments of a host into electricity, or vice versa."
desc = "Converts a host's nutrients into electricity, or uses electricity to supply nutrients."
extended_desc = "The siphon generates 10W of energy, so long as the siphon exists inside a biological entity. The entity will feel an increased \
appetite and will need to eat more often due to this. This device will fail if used inside synthetic entities.\
If the polarity is reversed, it will instead generate chemical energy with electricity, continuously consuming power from the assembly.\
@@ -122,7 +126,7 @@
name = "fuel cell"
desc = "Produces electricity from chemicals."
icon_state = "chemical_cell"
extended_desc = "This is effectively an internal beaker. It will consume and produce power from phoron, slime jelly, welding fuel, carbon,\
extended_desc = "Functions as an internal beaker for the assembly. It will consume and produce power from phoron, slime jelly, welding fuel, carbon,\
ethanol, nutriments and blood, in order of decreasing efficiency. It will consume fuel only if the battery can take more energy."
atom_flags = ATOM_FLAG_OPEN_CONTAINER
@@ -162,25 +166,33 @@
name = "power network interface"
desc = "Gives or takes power from a wire underneath the machine."
icon_state = "powernet"
extended_desc = "The assembly must be anchored, with a wrench, and a wire node must be avaiable directly underneath.<br>\
extended_desc = "The assembly must be anchored, with a wrench, and a wire node must be available directly underneath.<br>\
The first pin determines if power is moved at all. The second pin, if true, will draw from the powernet to charge the assembly's \
cell, otherwise it will give power from the cell to the powernet."
complexity = 20
size = 1
inputs = list(
"active" = IC_PINTYPE_BOOLEAN,
"draw power" = IC_PINTYPE_BOOLEAN
)
)
outputs = list(
"power in grid" = IC_PINTYPE_NUMBER,
"surplus power" = IC_PINTYPE_NUMBER,
"load" = IC_PINTYPE_NUMBER
)
)
activators = list()
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_POWER = 2)
var/obj/structure/machinery/power/circuit_io/IO = null // Dummy power machine to move energy in/out without a bunch of code duplication.
var/throughput = 10000 // Give/take up to 10kW.
/obj/item/integrated_circuit/passive/power/powernet/builtin
name = "built-in power network interface"
desc = "A built-in power network interface for machine-integrated electronic assemblies."
complexity = 0
size = 0
spawn_flags = 0
/obj/item/integrated_circuit/passive/power/powernet/Initialize()
IO = new(src)
return ..()
@@ -1,3 +1,8 @@
/*
* subtypes/arithmetic.dm
* Arithmetic circuits for numeric operations and math transformations.
*/
//These circuits do simple math.
/obj/item/integrated_circuit/arithmetic
complexity = 1
@@ -13,7 +18,7 @@
)
outputs = list("result" = IC_PINTYPE_NUMBER)
activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT)
category_text = "Arithmetic"
category_text = "MATH - Arithmetic"
power_draw_per_use = 50 // Math is pretty cheap.
// +Adding+ //
@@ -21,7 +26,7 @@
/obj/item/integrated_circuit/arithmetic/addition
name = "addition circuit"
desc = "This circuit can add numbers together."
extended_desc = "The order that the calculation goes is;<br>\
extended_desc = "Calculation order:<br>\
result = ((((A + B) + C) + D) ... ) and so on, until all pins have been added. \
Null pins are ignored."
icon_state = "addition"
@@ -43,7 +48,7 @@
/obj/item/integrated_circuit/arithmetic/subtraction
name = "subtraction circuit"
desc = "This circuit can subtract numbers."
extended_desc = "The order that the calculation goes is;<br>\
extended_desc = "Calculation order:<br>\
result = ((((A - B) - C) - D) ... ) and so on, until all pins have been subtracted. \
Null pins are ignored. Pin A <b>must</b> be a number or the circuit will not function."
icon_state = "subtraction"
@@ -71,7 +76,7 @@
/obj/item/integrated_circuit/arithmetic/multiplication
name = "multiplication circuit"
desc = "This circuit can multiply numbers."
extended_desc = "The order that the calculation goes is;<br>\
extended_desc = "Calculation order:<br>\
result = ((((A * B) * C) * D) ... ) and so on, until all pins have been multiplied. \
Null pins are ignored. Pin A <b>must</b> be a number or the circuit will not function."
icon_state = "multiplication"
@@ -98,8 +103,8 @@
/obj/item/integrated_circuit/arithmetic/division
name = "division circuit"
desc = "This circuit can divide numbers, just don't think about trying to divide by zero!"
extended_desc = "The order that the calculation goes is;<br>\
desc = "Divides one number by another. Division by zero is not valid."
extended_desc = "Calculation order:<br>\
result = ((((A / B) / C) / D) ... ) and so on, until all pins have been divided. \
Null pins, and pins containing 0, are ignored. Pin A <b>must</b> be a number or the circuit will not function."
icon_state = "division"
@@ -146,8 +151,8 @@
/obj/item/integrated_circuit/arithmetic/sign
name = "sign circuit"
desc = "This will say if a number is positive, negative, or zero."
extended_desc = "Will output 1, -1, or 0, depending on if A is a postive number, a negative number, or zero, respectively."
desc = "Outputs whether a number is positive, negative, or zero."
extended_desc = "Outputs 1 for positive numbers, -1 for negative numbers, and 0 for zero."
icon_state = "sign"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -196,7 +201,7 @@
/obj/item/integrated_circuit/arithmetic/absolute
name = "absolute circuit"
desc = "This outputs a non-negative version of the number you put in. This may also be thought of as its distance from zero."
desc = "Outputs the absolute value of A."
icon_state = "absolute"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -212,12 +217,70 @@
push_data()
activate_pin(2)
// Clamp and deadband //
/obj/item/integrated_circuit/arithmetic/clamp_deadband
name = "clamp and deadband circuit"
desc = "Clamps a number to a range and reports whether it is inside an optional deadband."
extended_desc = "If minimum and maximum are entered backward, they are swapped. Deadband radius values at or below zero disable the deadband check."
icon_state = "comparator"
complexity = 2
inputs = list(
"value" = IC_PINTYPE_NUMBER,
"minimum" = IC_PINTYPE_NUMBER,
"maximum" = IC_PINTYPE_NUMBER,
"deadband center" = IC_PINTYPE_NUMBER,
"deadband radius" = IC_PINTYPE_NUMBER
)
outputs = list(
"clamped value" = IC_PINTYPE_NUMBER,
"in range" = IC_PINTYPE_BOOLEAN,
"inside deadband" = IC_PINTYPE_BOOLEAN
)
activators = list(
"process" = IC_PINTYPE_PULSE_IN,
"on processed" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/arithmetic/clamp_deadband/do_work()
var/value = get_pin_data(IC_INPUT, 1)
var/minimum = get_pin_data(IC_INPUT, 2)
var/maximum = get_pin_data(IC_INPUT, 3)
var/deadband_center = get_pin_data(IC_INPUT, 4)
var/deadband_radius = get_pin_data(IC_INPUT, 5)
if(!isnum(value))
value = 0
if(!isnum(minimum))
minimum = value
if(!isnum(maximum))
maximum = value
if(minimum > maximum)
var/swap_value = minimum
minimum = maximum
maximum = swap_value
var/clamped_value = clamp(value, minimum, maximum)
var/in_range = (value >= minimum && value <= maximum)
var/inside_deadband = FALSE
if(isnum(deadband_center) && isnum(deadband_radius) && deadband_radius > 0)
inside_deadband = abs(value - deadband_center) <= abs(deadband_radius)
set_pin_data(IC_OUTPUT, 1, clamped_value)
set_pin_data(IC_OUTPUT, 2, in_range)
set_pin_data(IC_OUTPUT, 3, inside_deadband)
push_data()
activate_pin(2)
// Averaging //
/obj/item/integrated_circuit/arithmetic/average
name = "average circuit"
desc = "This circuit is of average quality, however it will compute the average for numbers you give it."
extended_desc = "Note that null pins are ignored, where as a pin containing 0 is included in the averaging calculation."
desc = "Calculates the average of the provided numeric inputs."
extended_desc = "Note that null pins are ignored, whereas a pin containing 0 is included in the averaging calculation."
icon_state = "average"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -240,7 +303,7 @@
// Pi, because why the hell not? //
/obj/item/integrated_circuit/arithmetic/pi
name = "pi constant circuit"
desc = "Not recommended for cooking. Outputs '3.14159' when it receives a pulse."
desc = "Outputs the numeric constant pi when pulsed."
icon_state = "pi"
inputs = list()
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -253,7 +316,7 @@
// Random //
/obj/item/integrated_circuit/arithmetic/random
name = "random number generator circuit"
desc = "This gives a random (integer) number between values A and B inclusive."
desc = "Outputs a random integer between A and B, inclusive."
extended_desc = "'Inclusive' means that the upper bound is included in the range of numbers, e.g. L = 1 and H = 3 will allow \
for outputs of 1, 2, or 3. H being the higher number is not <i>strictly</i> required."
icon_state = "random"
@@ -276,7 +339,7 @@
/obj/item/integrated_circuit/arithmetic/square_root
name = "square root circuit"
desc = "This outputs the square root of a number you put in."
desc = "Outputs the square root of A."
icon_state = "square_root"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -285,7 +348,7 @@
var/result = 0
for(var/datum/integrated_io/I in inputs)
I.pull_data()
if(isnum(I.data))
if(isnum(I.data) && I.data >= 0)
result = sqrt(I.data)
set_pin_data(IC_OUTPUT, 1, result)
@@ -296,7 +359,7 @@
/obj/item/integrated_circuit/arithmetic/modulo
name = "modulo circuit"
desc = "Gets the remainder of A / B."
desc = "Outputs the remainder of A divided by B."
icon_state = "modulo"
inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -316,7 +379,7 @@
/obj/item/integrated_circuit/arithmetic/min
name = "min circuit"
desc = "This outputs the smallest of the numbers you put in."
desc = "Outputs the smallest provided number."
// The states are rarely used symbols for the operations
// Letters didn't fit as well
icon_state = "min"
@@ -329,6 +392,9 @@
if(isnum(I.data))
values.Add(I.data)
if(!length(values))
return
set_pin_data(IC_OUTPUT, 1, min(values))
push_data()
activate_pin(2)
@@ -337,7 +403,7 @@
/obj/item/integrated_circuit/arithmetic/max
name = "max circuit"
desc = "This outputs the biggest of the numbers you put in."
desc = "Outputs the largest provided number."
icon_state = "max"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -348,6 +414,9 @@
if(isnum(I.data))
values.Add(I.data)
if(!length(values))
return
set_pin_data(IC_OUTPUT, 1, max(values))
push_data()
activate_pin(2)
@@ -1,6 +1,11 @@
/*
* subtypes/built_in.dm
* Built-in non-removable circuits used by assemblies to expose internal behavior to normal circuit networks.
*/
/obj/item/integrated_circuit/built_in
name = "integrated circuit"
desc = "It's a tiny chip! This one doesn't seem to do much, however."
desc = "A base integrated circuit component."
icon = 'icons/obj/assemblies/electronic_components.dmi'
icon_state = "template"
size = -1
@@ -9,7 +14,7 @@
/obj/item/integrated_circuit/built_in/device_input
name = "assembly input"
desc = "A built in chip for handling pulses from attached assembly items."
desc = "A built-in chip for handling pulses from attached assembly items."
complexity = 0 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'.
activators = list("on pulsed" = IC_PINTYPE_PULSE_OUT)
@@ -18,7 +23,7 @@
/obj/item/integrated_circuit/built_in/device_output
name = "assembly out"
desc = "A built in chip for pulsing attached assembly items."
desc = "A built-in chip for pulsing attached assembly items."
complexity = 0 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'.
activators = list("pulse attached" = IC_PINTYPE_PULSE_IN)
@@ -30,7 +35,7 @@
// Triggered when clothing assembly's hud button is clicked, used in-hand, or when another clothing-specific interaction occurs (like touching something with gloves on).
/obj/item/integrated_circuit/built_in/action_button
name = "external trigger circuit"
desc = "A built in chip that outputs a pulse when an external control event occurs. It also provides additional data depending on the nature of the event and device."
desc = "A built-in chip that outputs a pulse when an external control event occurs. It also provides additional data depending on the nature of the event and device."
extended_desc = "This outputs a pulse if the assembly's HUD button is clicked while the assembly is closed."
complexity = 0
activators = list("on activation" = IC_PINTYPE_PULSE_OUT)
@@ -1,3 +1,8 @@
/*
* subtypes/converters.dm
* Converter circuits that translate between numbers, strings, booleans, directions, colors, and other pin formats.
*/
//These circuits convert one variable to another.
/obj/item/integrated_circuit/converter
complexity = 2
@@ -10,7 +15,7 @@
/obj/item/integrated_circuit/converter/num2text
name = "number to string"
desc = "This circuit can convert a number variable into a string."
extended_desc = "Because of game limitations null/false variables will output a '0' string."
extended_desc = "Null or false-equivalent inputs output as the text value '0'."
complexity = 1
icon_state = "num-string"
inputs = list("input" = IC_PINTYPE_NUMBER)
@@ -33,7 +38,8 @@
/obj/item/integrated_circuit/converter/num2text4
name = "4-way number to string"
desc = "This circuit can convert up to four number variables into strings."
extended_desc = "Because of game limitations null/false variables will output a '0' string."
extended_desc = "Null or false-equivalent inputs output as the text value '0'."
complexity = 2
icon_state = "num-string"
inputs = list(
"input 1" = IC_PINTYPE_NUMBER,
@@ -69,7 +75,7 @@
/obj/item/integrated_circuit/converter/num2text8
name = "8-way number to string"
desc = "This circuit can convert up to eight number variables into strings."
extended_desc = "Because of game limitations null/false variables will output a '0' string."
extended_desc = "Null or false-equivalent inputs output as the text value '0'."
complexity = 4
icon_state = "num-string"
inputs = list(
@@ -116,7 +122,11 @@
desc = "This circuit can convert a string variable into a number."
icon_state = "string-num"
inputs = list("input" = IC_PINTYPE_STRING)
outputs = list("output" = IC_PINTYPE_NUMBER)
outputs = list(
"output" = IC_PINTYPE_NUMBER,
"valid" = IC_PINTYPE_BOOLEAN,
"status" = IC_PINTYPE_STRING
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/text2num/do_work()
@@ -126,7 +136,10 @@
if(!isnull(incoming))
result = text2num(incoming)
var/valid = isnum(result)
set_pin_data(IC_OUTPUT, 1, result)
set_pin_data(IC_OUTPUT, 2, valid)
set_pin_data(IC_OUTPUT, 3, valid ? "Number parsed." : "Input is not a number.")
push_data()
activate_pin(2)
@@ -149,9 +162,80 @@
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/ref_exists
name = "reference exists"
desc = "Checks whether a reference currently points to a valid atom."
icon_state = "ref-string"
inputs = list("input" = IC_PINTYPE_REF)
outputs = list(
"exists" = IC_PINTYPE_BOOLEAN,
"resolved ref" = IC_PINTYPE_REF
)
activators = list(
"convert" = IC_PINTYPE_PULSE_IN,
"on convert" = IC_PINTYPE_PULSE_OUT,
"on valid" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/ref_exists/do_work()
var/atom/A = get_pin_data_as_type(IC_INPUT, 1, /atom)
var/valid = !!A
set_pin_data(IC_OUTPUT, 1, valid)
set_pin_data(IC_OUTPUT, 2, valid ? A : null)
push_data()
activate_pin(2)
activate_pin(valid ? 3 : 4)
/obj/item/integrated_circuit/converter/ref_to_coords
name = "reference to coordinates"
desc = "Converts a reference into absolute X, Y, Z coordinates and its turf reference."
icon_state = "ref-string"
inputs = list("input" = IC_PINTYPE_REF)
outputs = list(
"X" = IC_PINTYPE_NUMBER,
"Y" = IC_PINTYPE_NUMBER,
"Z" = IC_PINTYPE_NUMBER,
"turf" = IC_PINTYPE_REF,
"valid" = IC_PINTYPE_BOOLEAN,
"status" = IC_PINTYPE_STRING
)
activators = list(
"convert" = IC_PINTYPE_PULSE_IN,
"on convert" = IC_PINTYPE_PULSE_OUT,
"on valid" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/ref_to_coords/do_work()
var/atom/A = get_pin_data_as_type(IC_INPUT, 1, /atom)
var/turf/T = A ? get_turf(A) : null
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, null)
set_pin_data(IC_OUTPUT, 3, null)
set_pin_data(IC_OUTPUT, 4, null)
set_pin_data(IC_OUTPUT, 5, FALSE)
set_pin_data(IC_OUTPUT, 6, "Invalid reference.")
if(T)
set_pin_data(IC_OUTPUT, 1, T.x)
set_pin_data(IC_OUTPUT, 2, T.y)
set_pin_data(IC_OUTPUT, 3, T.z)
set_pin_data(IC_OUTPUT, 4, T)
set_pin_data(IC_OUTPUT, 5, TRUE)
set_pin_data(IC_OUTPUT, 6, "Coordinates converted.")
push_data()
activate_pin(2)
activate_pin(T ? 3 : 4)
/obj/item/integrated_circuit/converter/lowercase
name = "lowercase string converter"
desc = "this will cause a string to come out in all lowercase."
desc = "Converts text to lowercase."
icon_state = "lowercase"
inputs = list("input" = IC_PINTYPE_STRING)
outputs = list("output" = IC_PINTYPE_STRING)
@@ -170,7 +254,7 @@
/obj/item/integrated_circuit/converter/uppercase
name = "uppercase string converter"
desc = "THIS WILL CAUSE A STRING TO COME OUT IN ALL UPPERCASE."
desc = "Converts text to uppercase."
icon_state = "uppercase"
inputs = list("input" = IC_PINTYPE_STRING)
outputs = list("output" = IC_PINTYPE_STRING)
@@ -189,7 +273,7 @@
/obj/item/integrated_circuit/converter/concatenator
name = "concatenator"
desc = "This joins many strings together to get one big string."
desc = "Joins multiple text inputs into one text output."
complexity = 4
inputs = list(
"A" = IC_PINTYPE_STRING,
@@ -218,8 +302,8 @@
/obj/item/integrated_circuit/converter/separator
name = "separator"
desc = "This splits as single string into two at the relative split point."
extended_desc = "This circuits splits a given string into two, based on the string, and the index value. \
desc = "Splits a text input into two outputs at the selected position."
extended_desc = "This circuit splits a given string into two, based on the string, and the index value. \
The index splits the string <b>after</b> the given index, including spaces. So 'a person' with an index of '3' \
will split into 'a p' and 'erson'."
complexity = 4
@@ -252,7 +336,7 @@
/obj/item/integrated_circuit/converter/findstring
name = "find text"
desc = "This gives position of sample in the string. Or returns 0."
desc = "Outputs the position of a sample text within the input text, or 0 if it is not found."
extended_desc = "The first pin is the string to be examined. The second pin is the sample to be found. \
For example, 'eat this burger',' ' will give you position 4. This circuit isn't case sensitive."
complexity = 4
@@ -297,43 +381,6 @@
activate_pin(2)
/obj/item/integrated_circuit/converter/radians2degrees
name = "radians to degrees converter"
desc = "Converts radians to degrees."
inputs = list("radian" = IC_PINTYPE_NUMBER)
outputs = list("degrees" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/radians2degrees/do_work()
var/result = null
pull_data()
var/incoming = get_pin_data(IC_INPUT, 1)
if(!isnull(incoming))
result = TO_DEGREES(incoming)
set_pin_data(IC_OUTPUT, 1, result)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/degrees2radians
name = "degrees to radians converter"
desc = "Converts degrees to radians."
inputs = list("degrees" = IC_PINTYPE_NUMBER)
outputs = list("radians" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/degrees2radians/do_work()
var/result = null
pull_data()
var/incoming = get_pin_data(IC_INPUT, 1)
if(!isnull(incoming))
result = TO_RADIANS(incoming)
set_pin_data(IC_OUTPUT, 1, result)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/abs_to_rel_coords
name = "abs to rel coordinate converter"
desc = "Easily convert absolute coordinates to relative coordinates with this."
@@ -365,6 +412,113 @@
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/dir_to_vector
name = "direction to vector"
desc = "Converts a BYOND direction into a two-number X/Y movement vector."
icon_state = "num-string"
inputs = list("direction" = IC_PINTYPE_DIR)
outputs = list(
"X" = IC_PINTYPE_NUMBER,
"Y" = IC_PINTYPE_NUMBER,
"vector" = IC_PINTYPE_LIST,
"valid" = IC_PINTYPE_BOOLEAN
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/dir_to_vector/do_work()
var/direction = get_pin_data(IC_INPUT, 1)
var/x = 0
var/y = 0
if(isnum(direction))
if(direction & EAST)
x++
if(direction & WEST)
x--
if(direction & NORTH)
y++
if(direction & SOUTH)
y--
set_pin_data(IC_OUTPUT, 1, x)
set_pin_data(IC_OUTPUT, 2, y)
set_pin_data(IC_OUTPUT, 3, list(x, y))
set_pin_data(IC_OUTPUT, 4, isnum(direction) && (x || y))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/vector_to_dir
name = "vector to direction"
desc = "Converts X/Y vector components into a BYOND direction."
icon_state = "num-string"
inputs = list(
"X" = IC_PINTYPE_NUMBER,
"Y" = IC_PINTYPE_NUMBER
)
outputs = list(
"direction" = IC_PINTYPE_DIR,
"direction text" = IC_PINTYPE_STRING,
"valid" = IC_PINTYPE_BOOLEAN
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/vector_to_dir/do_work()
var/x = get_pin_data(IC_INPUT, 1)
var/y = get_pin_data(IC_INPUT, 2)
var/direction = 0
if(isnum(x) && isnum(y))
if(x > 0)
direction |= EAST
else if(x < 0)
direction |= WEST
if(y > 0)
direction |= NORTH
else if(y < 0)
direction |= SOUTH
set_pin_data(IC_OUTPUT, 1, direction || null)
set_pin_data(IC_OUTPUT, 2, direction ? dir2text(direction) : null)
set_pin_data(IC_OUTPUT, 3, !!direction)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/delta_to_dir
name = "delta to direction"
desc = "Converts relative delta X/Y values into a BYOND direction."
icon_state = "num-string"
inputs = list(
"delta X" = IC_PINTYPE_NUMBER,
"delta Y" = IC_PINTYPE_NUMBER
)
outputs = list(
"direction" = IC_PINTYPE_DIR,
"direction text" = IC_PINTYPE_STRING,
"valid" = IC_PINTYPE_BOOLEAN
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/converter/delta_to_dir/do_work()
var/x = get_pin_data(IC_INPUT, 1)
var/y = get_pin_data(IC_INPUT, 2)
var/direction = 0
if(isnum(x) && isnum(y))
if(x > 0)
direction |= EAST
else if(x < 0)
direction |= WEST
if(y > 0)
direction |= NORTH
else if(y < 0)
direction |= SOUTH
set_pin_data(IC_OUTPUT, 1, direction || null)
set_pin_data(IC_OUTPUT, 2, direction ? dir2text(direction) : null)
set_pin_data(IC_OUTPUT, 3, !!direction)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/converter/stringlength
name = "len circuit"
desc = "This circuit will return the number of characters in a string."
@@ -1,10 +1,15 @@
/*
* subtypes/data_transfer.dm
* Data transfer circuits for copying, routing, packing, or unpacking values between pins.
*/
/obj/item/integrated_circuit/transfer
category_text = "Data Transfer"
power_draw_per_use = 20
/obj/item/integrated_circuit/transfer/multiplexer
name = "two multiplexer"
desc = "This is what those in the business tend to refer to as a 'mux' or data selector. It moves data from one of the selected inputs to the output."
desc = "Selects one input and sends that value to the output."
extended_desc = "The first input pin is used to select which of the other input pins which has its data moved to the output. \
If the input selection is outside the valid range then no output is given."
complexity = 2
@@ -58,7 +63,7 @@
/obj/item/integrated_circuit/transfer/demultiplexer
name = "two demultiplexer"
desc = "This is what those in the business tend to refer to as a 'demux'. It moves data from the input to one of the selected outputs."
desc = "Sends one input value to the selected output."
extended_desc = "The first input pin is used to select which of the output pins is given the data from the second input pin. \
If the output selection is outside the valid range then no output is given."
complexity = 2
@@ -114,8 +119,8 @@
/obj/item/integrated_circuit/transfer/wireless
name = "subspace transceiver"
desc = "A wireless transmitter-receiver pair that can transmit data between devices."
extended_desc = "The first input is the data to send to the other device, and the second input is the channel to send it on."
desc = "Transmits data wirelessly between linked devices."
extended_desc = "The first input is the data to send. The second input selects the transmission channel."
complexity = 12
spawn_flags = IC_SPAWN_RESEARCH
icon_state = "bluespace"
@@ -0,0 +1,151 @@
/*
* subtypes/debug.dm
* Debug/testing circuits used to inspect values, format helper output, and verify integrated electronics behavior.
*/
/obj/item/integrated_circuit/debug
category_text = "Debugging"
complexity = 1
spawn_flags = 0
/obj/item/integrated_circuit/debug/helper_test
name = "helper test circuit"
desc = "Tests integrated circuit helper procs."
extended_desc = "Outputs formatted helper proc results from one input."
icon_state = "template"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"input" = IC_PINTYPE_STRING,
"number input" = IC_PINTYPE_STRING,
"template" = IC_PINTYPE_STRING
)
outputs = list(
"display value" = IC_PINTYPE_STRING,
"type text" = IC_PINTYPE_STRING,
"safe number" = IC_PINTYPE_NUMBER,
"template output" = IC_PINTYPE_STRING
)
activators = list(
"test" = IC_PINTYPE_PULSE_IN,
"on tested" = IC_PINTYPE_PULSE_OUT
)
/obj/item/integrated_circuit/debug/helper_test/do_work()
pull_data()
var/input_value = get_pin_data(IC_INPUT, 1)
var/number_value = get_pin_data(IC_INPUT, 2)
var/template_value = get_pin_data(IC_INPUT, 3)
set_pin_data(IC_OUTPUT, 1, ic_display_value(input_value))
set_pin_data(IC_OUTPUT, 2, ic_type_text(input_value))
set_pin_data(IC_OUTPUT, 3, ic_safe_number(number_value, 0))
set_pin_data(IC_OUTPUT, 4, ic_apply_template(template_value, input_value, number_value, ic_type_text(input_value)))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/debug/one_shot_pulse
name = "one-shot pulse"
desc = "Allows one pulse through, then blocks further pulses until reset."
extended_desc = "This circuit is useful for preventing repeated signals. When triggered, it sends one pulse if it has not already fired. After firing, it will ignore further trigger pulses until the reset activator is pulsed."
icon_state = "template"
complexity = 2
w_class = WEIGHT_CLASS_TINY
size = 1
power_draw_per_use = 50
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
var/has_fired = FALSE
inputs = list(
"enabled" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"has fired" = IC_PINTYPE_BOOLEAN
)
activators = list(
"trigger" = IC_PINTYPE_PULSE_IN,
"reset" = IC_PINTYPE_PULSE_IN,
"pulse out" = IC_PINTYPE_PULSE_OUT,
"blocked" = IC_PINTYPE_PULSE_OUT
)
/obj/item/integrated_circuit/debug/one_shot_pulse/do_work(activator_id)
var/active_pin = activator_id
if(istype(active_pin, /datum/integrated_io))
active_pin = activators.Find(active_pin)
if(istext(active_pin))
for(var/i = 1 to activators.len)
var/datum/integrated_io/A = activators[i]
if(A.name == active_pin)
active_pin = i
break
if(!isnum(active_pin))
active_pin = 1
switch(active_pin)
if(1)
trigger_once()
return
if(2)
reset_latch()
return
/obj/item/integrated_circuit/debug/one_shot_pulse/proc/trigger_once()
pull_data()
var/enabled = get_pin_data(IC_INPUT, 1)
if(!enabled)
enabled = TRUE
if(!enabled)
set_pin_data(IC_OUTPUT, 1, has_fired)
push_data()
activate_pin(4)
return
if(has_fired)
set_pin_data(IC_OUTPUT, 1, has_fired)
push_data()
activate_pin(4)
return
has_fired = TRUE
set_pin_data(IC_OUTPUT, 1, has_fired)
push_data()
activate_pin(3)
/obj/item/integrated_circuit/debug/one_shot_pulse/proc/reset_latch()
has_fired = FALSE
set_pin_data(IC_OUTPUT, 1, has_fired)
push_data()
/obj/item/integrated_circuit/debug/one_shot_pulse/copy_clone_state_to(obj/item/integrated_circuit/target)
var/obj/item/integrated_circuit/debug/one_shot_pulse/new_latch = target
if(!istype(new_latch))
return
new_latch.has_fired = has_fired
/obj/item/integrated_circuit/debug/blank
name = "debug marker"
desc = "A blank debug circuit. It does nothing and exists only as a movable, removable, renameable marker."
extended_desc = "This circuit has no inputs, outputs, or activators. It is useful as a spacer, label, or visual divider inside an assembly."
icon_state = "template"
complexity = 0
size = 0
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list()
outputs = list()
activators = list()
/obj/item/integrated_circuit/debug/blank/do_work()
return
File diff suppressed because it is too large Load Diff
@@ -1,56 +1,114 @@
//Insert slots allow items to be inserted into assemblies from outside
//These items can then be used by other components
/*
* subtypes/insert_slot.dm
* Insert-slot circuits that hold items and expose slot state, item references, and capacity information to the circuit network.
*/
//Insert slots allow items to be inserted into assemblies from outside.
//These items can then be used by other components through ref pins.
/obj/item/integrated_circuit/insert_slot
category_text = "Insert slot"
var/capacity = 0
var/list/allowed_types = list()
var/list/items_contained = list()
activators = list("eject contents" = IC_PINTYPE_PULSE_IN)
outputs = list("has item" = IC_PINTYPE_BOOLEAN)
activators = list(
"eject contents" = IC_PINTYPE_PULSE_IN,
"on inserted" = IC_PINTYPE_PULSE_OUT,
"on ejected" = IC_PINTYPE_PULSE_OUT
)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER
)
power_draw_per_use = 10
w_class = WEIGHT_CLASS_NORMAL
size = 5
complexity = 1
//call this function from components that want to get items from this component
//set remove to FALSE if you dont want the item removed from the component and just want a reference to it
//(e.g. for beakers)
/obj/item/integrated_circuit/insert_slot/proc/get_item(var/remove = FALSE)
if(items_contained.len > 0)
var/itemToReturn = items_contained[1]
if(remove)
items_contained -= itemToReturn
if(items_contained.len <= 0)
set_pin_data(IC_OUTPUT, 1, FALSE)
push_data()
return itemToReturn
/obj/item/integrated_circuit/insert_slot/Initialize()
. = ..()
items_contained = list()
update_outputs()
/obj/item/integrated_circuit/insert_slot/interact(mob/user)
update_outputs()
. = ..()
/obj/item/integrated_circuit/insert_slot/proc/get_first_item()
if(items_contained.len)
return items_contained[1]
return null
//Call this from components that want to get items from this component.
//Set remove to FALSE if you do not want the item removed from the slot and just want a reference.
/obj/item/integrated_circuit/insert_slot/proc/get_item(var/remove = FALSE)
if(items_contained.len > 0)
var/obj/item/item_to_return = items_contained[1]
if(remove)
items_contained -= item_to_return
update_outputs()
push_data()
return item_to_return
return null
/obj/item/integrated_circuit/insert_slot/proc/update_outputs()
var/obj/item/held_item = get_first_item()
set_pin_data(IC_OUTPUT, 1, !!held_item)
set_pin_data(IC_OUTPUT, 2, src)
set_pin_data(IC_OUTPUT, 3, held_item)
set_pin_data(IC_OUTPUT, 4, held_item ? held_item.name : null)
set_pin_data(IC_OUTPUT, 5, items_contained.len)
set_pin_data(IC_OUTPUT, 6, max(capacity - items_contained.len, 0))
/obj/item/integrated_circuit/insert_slot/do_work()
for(var/obj/item/O in items_contained)
while(items_contained.len)
var/obj/item/O = items_contained[1]
items_contained -= O
O.forceMove(get_turf(src))
visible_message(SPAN_NOTICE("\The [src] drops [O] on the ground."))
items_contained -= O
set_pin_data(IC_OUTPUT, 1, FALSE)
update_outputs()
push_data()
activate_pin(3)
return TRUE
/obj/item/integrated_circuit/insert_slot/proc/insert(var/obj/item/O, var/mob/user)
if(is_type_in_list(O, allowed_types))
if(items_contained.len >= capacity)
to_chat(user, SPAN_WARNING("\The [src] is too full to add [O]."))
return FALSE
items_contained += O
user.drop_from_inventory(O,src)
to_chat(user, SPAN_NOTICE("You add [O] to \the [src]."))
set_pin_data(IC_OUTPUT, 1, TRUE)
return TRUE
if(!O || !user)
return FALSE
if(!is_type_in_list(O, allowed_types))
return FALSE
if(items_contained.len >= capacity)
to_chat(user, SPAN_WARNING("\The [src] is too full to add [O]."))
return FALSE
items_contained += O
user.drop_from_inventory(O, src)
to_chat(user, SPAN_NOTICE("You add [O] to \the [src]."))
update_outputs()
push_data()
activate_pin(2)
return TRUE
/obj/item/integrated_circuit/insert_slot/paper_tray
name = "paper tray"
desc = "A simple paper tray similar to one from a printer."
extended_desc = "A simple paper tray, paper can be inserted and used by other components. \
Paper can be inserted through a slot in the casing. Holds 10 sheets"
extended_desc = "A simple paper tray. Paper can be inserted and used by other components. Holds 10 sheets. \
The slot reference output can be wired directly into printer paper-source inputs."
capacity = 10
size = 8
power_draw_per_use = 30
@@ -58,13 +116,336 @@
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"top paper text" = IC_PINTYPE_STRING,
"top paper used length" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/insert_slot/paper_tray/update_outputs()
..()
var/obj/item/paper/P = get_first_item()
if(P)
set_pin_data(IC_OUTPUT, 7, P.info)
set_pin_data(IC_OUTPUT, 8, length(P.info))
else
set_pin_data(IC_OUTPUT, 7, null)
set_pin_data(IC_OUTPUT, 8, 0)
/obj/item/integrated_circuit/insert_slot/beaker_holder
name = "beaker holder"
desc = "A slot for holding a beaker"
extended_desc = "A slot for holding a beaker with reagents. \
It has a bracket to hold the beaker in place and a lid to prevent spillage. \
There is an extraction tube built into the lid"
desc = "A slot for holding a beaker."
extended_desc = "A slot for holding a beaker with reagents. It has a bracket to hold the beaker in place and a lid to prevent spillage. \
The slot reference output can be wired directly into reagent pump source inputs."
capacity = 1
allowed_types = list(/obj/item/reagent_containers/glass/beaker)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_BIO = 2, TECH_MATERIAL = 2)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"reagent volume" = IC_PINTYPE_NUMBER,
"maximum volume" = IC_PINTYPE_NUMBER,
"free space" = IC_PINTYPE_NUMBER,
"reagent names" = IC_PINTYPE_LIST
)
/obj/item/integrated_circuit/insert_slot/beaker_holder/update_outputs()
..()
var/obj/item/reagent_containers/glass/beaker/B = get_first_item()
var/list/reagent_names = list()
if(B && B.reagents)
for(var/_RE in B.reagents.reagent_volumes)
var/singleton/reagent/RE = GET_SINGLETON(_RE)
reagent_names += RE.name
set_pin_data(IC_OUTPUT, 7, B.reagents.total_volume)
set_pin_data(IC_OUTPUT, 8, B.reagents.maximum_volume)
set_pin_data(IC_OUTPUT, 9, REAGENTS_FREE_SPACE(B.reagents))
set_pin_data(IC_OUTPUT, 10, reagent_names)
else
set_pin_data(IC_OUTPUT, 7, 0)
set_pin_data(IC_OUTPUT, 8, 0)
set_pin_data(IC_OUTPUT, 9, 0)
set_pin_data(IC_OUTPUT, 10, list())
/obj/item/integrated_circuit/insert_slot/id_slot
name = "ID card slot"
desc = "A slot for holding and reading an ID card."
extended_desc = "A narrow slot for holding a single ID card. Outputs the card reference, registered name, assignment, job title, corporation, and access list. \
If credential caching is enabled, inserted access is copied to the assembly access card."
capacity = 1
size = 4
power_draw_per_use = 15
allowed_types = list(/obj/item/card/id)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2)
inputs = list(
"enable credential cache" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"registered name" = IC_PINTYPE_STRING,
"assignment" = IC_PINTYPE_STRING,
"job title" = IC_PINTYPE_STRING,
"corporation" = IC_PINTYPE_STRING,
"passkey" = IC_PINTYPE_LIST
)
/obj/item/integrated_circuit/insert_slot/id_slot/insert(var/obj/item/O, var/mob/user)
. = ..()
if(.)
cache_access_if_enabled(O)
/obj/item/integrated_circuit/insert_slot/id_slot/proc/cache_access_if_enabled(var/obj/item/I)
if(!get_pin_data(IC_INPUT, 1))
return
if(!assembly || !assembly.access_card)
return
var/list/access = I.GetAccess()
if(access)
assembly.access_card.access |= access
/obj/item/integrated_circuit/insert_slot/id_slot/update_outputs()
..()
var/obj/item/I = get_first_item()
set_pin_data(IC_OUTPUT, 1, !!I)
set_pin_data(IC_OUTPUT, 2, src)
set_pin_data(IC_OUTPUT, 3, I)
set_pin_data(IC_OUTPUT, 4, I ? I.name : null)
set_pin_data(IC_OUTPUT, 5, I ? 1 : 0)
set_pin_data(IC_OUTPUT, 6, I ? 0 : capacity)
var/obj/item/card/id/card = null
var/list/access = null
if(I)
card = I.GetID()
access = I.GetAccess()
if(card)
var/list/split_assignment = ic_split_assignment(card.assignment)
set_pin_data(IC_OUTPUT, 7, card.registered_name)
set_pin_data(IC_OUTPUT, 8, card.assignment)
set_pin_data(IC_OUTPUT, 9, split_assignment["job title"])
set_pin_data(IC_OUTPUT, 10, split_assignment["corporation"])
set_pin_data(IC_OUTPUT, 11, access ? access : list())
else
set_pin_data(IC_OUTPUT, 7, null)
set_pin_data(IC_OUTPUT, 8, null)
set_pin_data(IC_OUTPUT, 9, null)
set_pin_data(IC_OUTPUT, 10, null)
set_pin_data(IC_OUTPUT, 11, list())
/obj/item/integrated_circuit/insert_slot/card_slot
name = "card slot"
desc = "A generic slot for holding and reading card-like objects."
extended_desc = "A generic card slot. It accepts any card object, outputs the held item reference, and attempts to read access data from it."
capacity = 1
size = 4
power_draw_per_use = 15
allowed_types = list(/obj/item/card)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2)
inputs = list(
"enable credential cache" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"registered name" = IC_PINTYPE_STRING,
"assignment" = IC_PINTYPE_STRING,
"job title" = IC_PINTYPE_STRING,
"corporation" = IC_PINTYPE_STRING,
"passkey" = IC_PINTYPE_LIST,
"has access data" = IC_PINTYPE_BOOLEAN
)
/obj/item/integrated_circuit/insert_slot/card_slot/insert(var/obj/item/O, var/mob/user)
. = ..()
if(.)
cache_access_if_enabled(O)
/obj/item/integrated_circuit/insert_slot/card_slot/proc/cache_access_if_enabled(var/obj/item/I)
if(!get_pin_data(IC_INPUT, 1))
return
if(!assembly || !assembly.access_card)
return
var/list/access = I.GetAccess()
if(access)
assembly.access_card.access |= access
/obj/item/integrated_circuit/insert_slot/card_slot/update_outputs()
..()
var/obj/item/I = get_first_item()
var/obj/item/card/id/card = null
var/list/access = null
if(I)
card = I.GetID()
access = I.GetAccess()
if(card)
var/list/split_assignment = ic_split_assignment(card.assignment)
set_pin_data(IC_OUTPUT, 7, card.registered_name)
set_pin_data(IC_OUTPUT, 8, card.assignment)
set_pin_data(IC_OUTPUT, 9, split_assignment["job title"])
set_pin_data(IC_OUTPUT, 10, split_assignment["corporation"])
set_pin_data(IC_OUTPUT, 11, access ? access : list())
set_pin_data(IC_OUTPUT, 12, TRUE)
else
set_pin_data(IC_OUTPUT, 7, null)
set_pin_data(IC_OUTPUT, 8, null)
set_pin_data(IC_OUTPUT, 9, null)
set_pin_data(IC_OUTPUT, 10, null)
set_pin_data(IC_OUTPUT, 11, access ? access : list())
set_pin_data(IC_OUTPUT, 12, !!length(access))
/obj/item/integrated_circuit/insert_slot/power_cell_slot
name = "power cell slot"
desc = "A socket for holding and reading a power cell."
extended_desc = "A reinforced socket that holds a single power cell. Outputs charge, maximum charge, and charge percentage."
capacity = 1
size = 7
power_draw_per_use = 20
allowed_types = list(
/obj/item/cell,
/obj/item/cell/device,
/obj/item/cell/device/high
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_POWER = 3)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"cell charge" = IC_PINTYPE_NUMBER,
"max charge" = IC_PINTYPE_NUMBER,
"percentage" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/insert_slot/power_cell_slot/update_outputs()
..()
var/obj/item/cell/C = get_first_item()
if(C)
set_pin_data(IC_OUTPUT, 7, C.charge)
set_pin_data(IC_OUTPUT, 8, C.maxcharge)
set_pin_data(IC_OUTPUT, 9, C.percent())
else
set_pin_data(IC_OUTPUT, 7, 0)
set_pin_data(IC_OUTPUT, 8, 0)
set_pin_data(IC_OUTPUT, 9, 0)
/obj/item/integrated_circuit/insert_slot/storage_slot
name = "storage slot"
desc = "A bracket for holding a storage container."
extended_desc = "A bracket that can hold one storage item. Outputs the storage reference for scanner, grabber, sorter, or custom storage logic."
capacity = 1
size = 8
power_draw_per_use = 25
allowed_types = list(/obj/item/storage)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"storage reference" = IC_PINTYPE_REF,
"contained item count" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/insert_slot/storage_slot/update_outputs()
..()
var/obj/item/storage/S = get_first_item()
if(S)
set_pin_data(IC_OUTPUT, 7, S)
set_pin_data(IC_OUTPUT, 8, S.contents.len)
else
set_pin_data(IC_OUTPUT, 7, null)
set_pin_data(IC_OUTPUT, 8, 0)
/obj/item/integrated_circuit/insert_slot/tool_slot
name = "tool slot"
desc = "A reinforced slot for holding a tool."
extended_desc = "A utility slot that can hold one tool or general item. Outputs the held item reference for machines that require a physical tool."
capacity = 1
size = 6
power_draw_per_use = 20
allowed_types = list(/obj/item)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
outputs = list(
"has item" = IC_PINTYPE_BOOLEAN,
"slot reference" = IC_PINTYPE_REF,
"item reference" = IC_PINTYPE_REF,
"item name" = IC_PINTYPE_STRING,
"stored count" = IC_PINTYPE_NUMBER,
"remaining capacity" = IC_PINTYPE_NUMBER,
"tool reference" = IC_PINTYPE_REF
)
/obj/item/integrated_circuit/insert_slot/tool_slot/update_outputs()
..()
var/obj/item/I = get_first_item()
set_pin_data(IC_OUTPUT, 7, I)
/obj/item/integrated_circuit/insert_slot/item_tray
name = "general item tray"
desc = "A generic tray for holding small items."
extended_desc = "A broad item tray. It accepts ordinary items and exposes the held item reference, name, count, and remaining capacity. \
Use this when a more specific insert slot does not exist."
capacity = 5
size = 8
power_draw_per_use = 20
allowed_types = list(/obj/item)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2)
@@ -1,3 +1,8 @@
/*
* subtypes/lists.dm
* List circuits for creating, reading, writing, merging, filtering, and transforming list pin data.
*/
//These circuits do things with lists, and use special list pins for stability.
/obj/item/integrated_circuit/list
complexity = 1
@@ -9,27 +14,47 @@
category_text = "Lists"
power_draw_per_use = 200
/obj/item/integrated_circuit/list/empty
name = "empty list circuit"
desc = "This circuit outputs an empty list."
extended_desc = "Outputs an empty list that can be passed into append, write, and other list-editing circuits."
inputs = list()
outputs = list(
"empty list" = IC_PINTYPE_LIST
)
icon_state = "addition"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/empty/do_work()
set_pin_data(IC_OUTPUT, 1, list())
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/pick
name = "pick circuit"
desc = "This circuit will randomly 'pick' an element from a list that is inputted."
extended_desc = "Will output null if the list is empty. Input list is unmodified."
extended_desc = "Outputs null if the list is empty. The input list is not changed."
icon_state = "addition"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/pick/do_work()
var/result = null
var/list/input_list = get_pin_data(IC_INPUT, 1) // List pins guarantee that there is a list inside, even if just an empty one.
if(input_list.len)
var/list/input_list = get_pin_data(IC_INPUT, 1)
if(islist(input_list) && input_list.len)
result = pick(input_list)
set_pin_data(IC_OUTPUT, 1, result)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/append
name = "append circuit"
desc = "This circuit will add an element to a list."
extended_desc = "The new element will always be at the bottom of the list."
extended_desc = "Adds the new element to the end of the list."
inputs = list(
"list to append" = IC_PINTYPE_LIST,
"input" = IC_PINTYPE_ANY
@@ -42,14 +67,105 @@
/obj/item/integrated_circuit/list/append/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list/output_list = list()
var/new_entry = get_pin_data(IC_INPUT, 2)
output_list = input_list + new_entry
var/list/output_list = islist(input_list) ? input_list.Copy() : list()
var/new_entry = get_pin_data(IC_INPUT, 2, FALSE)
output_list += list(new_entry)
set_pin_data(IC_OUTPUT, 1, output_list)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/unique_append
name = "unique append circuit"
desc = "Appends a value only if the list does not already contain it."
extended_desc = "The output list is copied from the input list and capped at the requested max length, up to a hard maximum of 64 entries."
inputs = list(
"list to append" = IC_PINTYPE_LIST,
"input" = IC_PINTYPE_ANY,
"max length" = IC_PINTYPE_NUMBER
)
inputs_default = list(
"3" = 16
)
outputs = list(
"appended list" = IC_PINTYPE_LIST,
"appended" = IC_PINTYPE_BOOLEAN,
"duplicate" = IC_PINTYPE_BOOLEAN,
"full" = IC_PINTYPE_BOOLEAN,
"length" = IC_PINTYPE_NUMBER
)
activators = list(
"append" = IC_PINTYPE_PULSE_IN,
"on appended" = IC_PINTYPE_PULSE_OUT,
"on duplicate" = IC_PINTYPE_PULSE_OUT,
"on full" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
icon_state = "addition"
complexity = 3
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/unique_append/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/new_entry = get_pin_data(IC_INPUT, 2, FALSE)
var/max_length = get_pin_data(IC_INPUT, 3)
if(!islist(input_list))
set_pin_data(IC_OUTPUT, 1, list())
set_pin_data(IC_OUTPUT, 2, FALSE)
set_pin_data(IC_OUTPUT, 3, FALSE)
set_pin_data(IC_OUTPUT, 4, FALSE)
set_pin_data(IC_OUTPUT, 5, 0)
push_data()
activate_pin(5)
return
if(!isnum(max_length))
max_length = 16
max_length = clamp(round(max_length), 1, 64)
var/list/output_list = list()
for(var/i = 1 to min(input_list.len, max_length))
output_list += list(input_list[i])
var/appended = FALSE
// Match list_contains behavior so refs and display-equivalent primitive values do not create accidental duplicates.
for(var/item in output_list)
if(item == new_entry || "[item]" == "[new_entry]")
set_pin_data(IC_OUTPUT, 1, output_list)
set_pin_data(IC_OUTPUT, 2, FALSE)
set_pin_data(IC_OUTPUT, 3, TRUE)
set_pin_data(IC_OUTPUT, 4, FALSE)
set_pin_data(IC_OUTPUT, 5, output_list.len)
push_data()
activate_pin(3)
return
if(output_list.len >= max_length)
set_pin_data(IC_OUTPUT, 1, output_list)
set_pin_data(IC_OUTPUT, 2, FALSE)
set_pin_data(IC_OUTPUT, 3, FALSE)
set_pin_data(IC_OUTPUT, 4, TRUE)
set_pin_data(IC_OUTPUT, 5, output_list.len)
push_data()
activate_pin(4)
return
output_list += list(new_entry)
appended = TRUE
set_pin_data(IC_OUTPUT, 1, output_list)
set_pin_data(IC_OUTPUT, 2, appended)
set_pin_data(IC_OUTPUT, 3, FALSE)
set_pin_data(IC_OUTPUT, 4, FALSE)
set_pin_data(IC_OUTPUT, 5, output_list.len)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/search
name = "search circuit"
desc = "This circuit will give index of desired element in the list."
@@ -67,10 +183,12 @@
/obj/item/integrated_circuit/list/search/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/item = get_pin_data(IC_INPUT, 2)
set_pin_data(IC_OUTPUT, 1, input_list.Find(item))
set_pin_data(IC_OUTPUT, 1, islist(input_list) ? input_list.Find(item) : 0)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/at
name = "at circuit"
desc = "This circuit will pick an element from a list by index."
@@ -79,18 +197,102 @@
"list" = IC_PINTYPE_LIST,
"index" = IC_PINTYPE_NUMBER
)
outputs = list("item" = IC_PINTYPE_ANY)
outputs = list(
"item" = IC_PINTYPE_ANY,
"valid" = IC_PINTYPE_BOOLEAN,
"status" = IC_PINTYPE_STRING
)
icon_state = "addition"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/at/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/index = get_pin_data(IC_INPUT, 2)
var/item = input_list[index]
var/item = null
var/valid = FALSE
var/status = "Invalid index."
if(!islist(input_list))
input_list = list()
if(!isnull(index))
index = round(index)
if(index >= 1 && index <= input_list.len)
item = input_list[index]
valid = TRUE
status = "Item read."
else
status = "Index out of range."
set_pin_data(IC_OUTPUT, 1, item)
set_pin_data(IC_OUTPUT, 2, valid)
set_pin_data(IC_OUTPUT, 3, status)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/ring_index
name = "ring index circuit"
desc = "Advances an index through a list and wraps safely at either end."
extended_desc = "Outputs the wrapped next index and selected value. Empty lists return invalid without changing anything."
inputs = list(
"list" = IC_PINTYPE_LIST,
"current index" = IC_PINTYPE_NUMBER,
"step" = IC_PINTYPE_NUMBER
)
inputs_default = list(
"2" = 0,
"3" = 1
)
outputs = list(
"next index" = IC_PINTYPE_NUMBER,
"selected value" = IC_PINTYPE_ANY,
"length" = IC_PINTYPE_NUMBER,
"valid" = IC_PINTYPE_BOOLEAN
)
activators = list(
"select" = IC_PINTYPE_PULSE_IN,
"on selected" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/ring_index/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list_length = islist(input_list) ? input_list.len : 0
if(!list_length)
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, null)
set_pin_data(IC_OUTPUT, 3, 0)
set_pin_data(IC_OUTPUT, 4, FALSE)
push_data()
activate_pin(3)
return
var/current_index = get_pin_data(IC_INPUT, 2)
var/step = get_pin_data(IC_INPUT, 3)
if(!isnum(current_index))
current_index = 0
if(!isnum(step))
step = 1
var/next_index = ((round(current_index) + round(step) - 1) % list_length) + 1
if(next_index < 1)
next_index += list_length
set_pin_data(IC_OUTPUT, 1, next_index)
set_pin_data(IC_OUTPUT, 2, input_list[next_index])
set_pin_data(IC_OUTPUT, 3, list_length)
set_pin_data(IC_OUTPUT, 4, TRUE)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/delete
name = "delete circuit"
desc = "This circuit will delete the element from a list by index."
@@ -100,73 +302,183 @@
"index" = IC_PINTYPE_NUMBER
)
outputs = list(
"item" = IC_PINTYPE_LIST
"redacted list" = IC_PINTYPE_LIST,
"deleted" = IC_PINTYPE_BOOLEAN,
"status" = IC_PINTYPE_STRING
)
icon_state = "addition"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/delete/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list/red_list = list()
var/list/output_list = islist(input_list) ? input_list.Copy() : list()
var/index = get_pin_data(IC_INPUT, 2)
var/j = 0
for(var/I in input_list)
j = j + 1
if(j != index)
red_list.Add(I)
set_pin_data(IC_OUTPUT, 1, red_list)
var/deleted = FALSE
var/status = "Invalid index."
if(!isnull(index))
index = round(index)
if(index >= 1 && index <= output_list.len)
output_list.Cut(index, index + 1)
deleted = TRUE
status = "Item deleted."
else
status = "Index out of range."
set_pin_data(IC_OUTPUT, 1, output_list)
set_pin_data(IC_OUTPUT, 2, deleted)
set_pin_data(IC_OUTPUT, 3, status)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/write
name = "write circuit"
desc = "This circuit will write element in list with given index."
extended_desc = "If there is no element with such index, it will give the same list, as before."
desc = "This circuit will write an element into a list with a given index."
extended_desc = "If the index is beyond the current list length, the list will expand with null entries until the index exists."
inputs = list(
"list" = IC_PINTYPE_LIST,
"index" = IC_PINTYPE_NUMBER,
"item" = IC_PINTYPE_ANY
)
outputs = list(
"redacted list" = IC_PINTYPE_LIST
"redacted list" = IC_PINTYPE_LIST,
"written" = IC_PINTYPE_BOOLEAN,
"status" = IC_PINTYPE_STRING
)
icon_state = "addition"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/write/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list/output_list = islist(input_list) ? input_list.Copy() : list()
var/index = get_pin_data(IC_INPUT, 2)
var/item = get_pin_data(IC_INPUT, 3)
input_list[index] = item
set_pin_data(IC_OUTPUT, 1, input_list)
var/written = FALSE
var/status = "Invalid index."
if(!isnull(index))
index = round(index)
if(index >= 1 && index <= IC_MAX_LIST_LENGTH)
while(output_list.len < index)
output_list.Add(null)
output_list[index] = item
written = TRUE
status = "Item written."
else
status = "Index out of range."
set_pin_data(IC_OUTPUT, 1, output_list)
set_pin_data(IC_OUTPUT, 2, written)
set_pin_data(IC_OUTPUT, 3, status)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/write4
name = "4-way write circuit"
desc = "This circuit writes up to four items into a list."
extended_desc = "Each item is written directly to its matching list position. Item 1 writes to list position 1, item 2 writes to position 2, and so on."
inputs = list(
"list" = IC_PINTYPE_LIST,
"item 1" = IC_PINTYPE_ANY,
"item 2" = IC_PINTYPE_ANY,
"item 3" = IC_PINTYPE_ANY,
"item 4" = IC_PINTYPE_ANY
)
outputs = list(
"redacted list" = IC_PINTYPE_LIST
)
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/write4/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list/output_list = islist(input_list) ? input_list.Copy() : list()
for(var/i = 1; i <= 4; i++)
var/item = get_pin_data(IC_INPUT, i + 1)
while(output_list.len < i)
output_list.Add(null)
output_list[i] = item
set_pin_data(IC_OUTPUT, 1, output_list)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/write8
name = "8-way write circuit"
desc = "This circuit writes up to eight items into a list."
extended_desc = "Each item is written directly to its matching list position. Item 1 writes to list position 1, item 2 writes to position 2, and so on."
inputs = list(
"list" = IC_PINTYPE_LIST,
"item 1" = IC_PINTYPE_ANY,
"item 2" = IC_PINTYPE_ANY,
"item 3" = IC_PINTYPE_ANY,
"item 4" = IC_PINTYPE_ANY,
"item 5" = IC_PINTYPE_ANY,
"item 6" = IC_PINTYPE_ANY,
"item 7" = IC_PINTYPE_ANY,
"item 8" = IC_PINTYPE_ANY
)
outputs = list(
"redacted list" = IC_PINTYPE_LIST
)
icon_state = "addition"
complexity = 3
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/write8/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list/output_list = islist(input_list) ? input_list.Copy() : list()
for(var/i = 1; i <= 8; i++)
var/item = get_pin_data(IC_INPUT, i + 1)
while(output_list.len < i)
output_list.Add(null)
output_list[i] = item
set_pin_data(IC_OUTPUT, 1, output_list)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/len
name = "len circuit"
desc = "This circuit will give length of the list."
inputs = list(
"list" = IC_PINTYPE_LIST,
)
"list" = IC_PINTYPE_LIST
)
outputs = list(
"item" = IC_PINTYPE_NUMBER
"length" = IC_PINTYPE_NUMBER
)
icon_state = "addition"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/len/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
set_pin_data(IC_OUTPUT, 1, input_list.len)
set_pin_data(IC_OUTPUT, 1, islist(input_list) ? input_list.len : 0)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/jointext
name = "join text circuit"
desc = "This circuit will add all elements of a list into one string, seperated by a character."
extended_desc = "Default settings will encode the entire list into a string."
desc = "This circuit will add all elements of a list into one string, separated by a character."
extended_desc = "Default settings will encode the entire list into a string. Mixed list values are converted to display text before joining."
inputs = list(
"list to join" = IC_PINTYPE_LIST,
"delimiter" = IC_PINTYPE_CHAR,
"delimiter" = IC_PINTYPE_STRING,
"start" = IC_PINTYPE_NUMBER,
"end" = IC_PINTYPE_NUMBER
)
@@ -189,9 +501,501 @@
var/result = null
if(input_list.len && delimiter && !isnull(start) && !isnull(end))
result = jointext(input_list, delimiter, start, end)
if(islist(input_list) && input_list.len && !isnull(delimiter) && !isnull(start) && !isnull(end))
var/list/filtered_list = list()
for(var/item in input_list)
if(!isnull(item))
filtered_list += item
result = jointext(filtered_list, delimiter, start, end)
set_pin_data(IC_OUTPUT, 1, result)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/any_greater_than
name = "any greater than circuit"
desc = "This circuit checks whether any number in a list is greater than a target value."
extended_desc = "Non-number values are ignored. Outputs true if at least one number in the list is greater than the comparison value."
category_text = "MATH - List Comparisons"
inputs = list(
"list" = IC_PINTYPE_LIST,
"compare value" = IC_PINTYPE_NUMBER
)
outputs = list(
"result" = IC_PINTYPE_BOOLEAN
)
activators = list(
"compare" = IC_PINTYPE_PULSE_IN,
"on true result" = IC_PINTYPE_PULSE_OUT,
"on false result" = IC_PINTYPE_PULSE_OUT
)
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/any_greater_than/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/compare_value = get_pin_data(IC_INPUT, 2)
var/result = FALSE
if(islist(input_list) && !isnull(compare_value))
for(var/value in input_list)
if(!isnum(value))
continue
if(value > compare_value)
result = TRUE
break
set_pin_data(IC_OUTPUT, 1, result)
push_data()
if(result)
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/list/any_less_than
name = "any less than circuit"
desc = "This circuit checks whether any number in a list is less than a target value."
extended_desc = "Non-number values are ignored. Outputs true if at least one number in the list is less than the comparison value."
category_text = "MATH - List Comparisons"
inputs = list(
"list" = IC_PINTYPE_LIST,
"compare value" = IC_PINTYPE_NUMBER
)
outputs = list(
"result" = IC_PINTYPE_BOOLEAN
)
activators = list(
"compare" = IC_PINTYPE_PULSE_IN,
"on true result" = IC_PINTYPE_PULSE_OUT,
"on false result" = IC_PINTYPE_PULSE_OUT
)
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/any_less_than/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/compare_value = get_pin_data(IC_INPUT, 2)
var/result = FALSE
if(islist(input_list) && !isnull(compare_value))
for(var/value in input_list)
if(!isnum(value))
continue
if(value < compare_value)
result = TRUE
break
set_pin_data(IC_OUTPUT, 1, result)
push_data()
if(result)
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/list/any_equal_to
name = "any equal to circuit"
desc = "This circuit checks whether any value in a list is equal to a target value."
extended_desc = "Outputs true if at least one value in the list is equal to the comparison value. This can compare numbers, strings, booleans, refs, or null."
category_text = "MATH - List Comparisons"
inputs = list(
"list" = IC_PINTYPE_LIST,
"compare value" = IC_PINTYPE_ANY
)
outputs = list(
"result" = IC_PINTYPE_BOOLEAN
)
activators = list(
"compare" = IC_PINTYPE_PULSE_IN,
"on true result" = IC_PINTYPE_PULSE_OUT,
"on false result" = IC_PINTYPE_PULSE_OUT
)
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/any_equal_to/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/compare_value = get_pin_data(IC_INPUT, 2)
var/result = FALSE
if(islist(input_list))
for(var/value in input_list)
if(value == compare_value)
result = TRUE
break
set_pin_data(IC_OUTPUT, 1, result)
push_data()
if(result)
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/list/arithmetic
name = "list arithmetic"
desc = "This circuit performs basic arithmetic between two lists of numbers."
extended_desc = "Takes two lists and applies the selected operation index-by-index. If either value is not a number, the output at that position is null. Supported operations are add, subtract, multiply, and divide."
category_text = "MATH - List Math"
icon_state = "addition"
complexity = 8
inputs = list(
"list A" = IC_PINTYPE_LIST,
"list B" = IC_PINTYPE_LIST,
"operation" = IC_PINTYPE_STRING
)
outputs = list(
"result" = IC_PINTYPE_LIST
)
activators = list(
"calculate" = IC_PINTYPE_PULSE_IN,
"on calculated" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/arithmetic/do_work()
pull_data()
var/list/list_a = get_pin_data(IC_INPUT, 1)
var/list/list_b = get_pin_data(IC_INPUT, 2)
var/operation = lowertext("[get_pin_data(IC_INPUT, 3)]")
var/list/result = list()
if(!islist(list_a) || !islist(list_b))
set_pin_data(IC_OUTPUT, 1, null)
push_data()
activate_pin(2)
return
var/max_length = max(length(list_a), length(list_b))
for(var/i = 1 to max_length)
var/a = list_a[i]
var/b = list_b[i]
if(!isnum(a) || !isnum(b))
result.len++
continue
switch(operation)
if("add", "+", "addition")
result += a + b
if("subtract", "-", "subtraction")
result += a - b
if("multiply", "*", "multiplication")
result += a * b
if("divide", "/", "division")
if(b == 0)
result.len++
else
result += a / b
else
result.len++
set_pin_data(IC_OUTPUT, 1, result)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/list_contains
name = "list contains"
desc = "Checks whether a list contains a value."
category_text = "MATH - List Comparisons"
icon_state = "template"
complexity = 3
inputs = list(
"list" = IC_PINTYPE_LIST,
"value" = IC_PINTYPE_ANY
)
outputs = list(
"contains" = IC_PINTYPE_BOOLEAN,
"index" = IC_PINTYPE_NUMBER,
"matched value" = IC_PINTYPE_ANY
)
activators = list(
"check" = IC_PINTYPE_PULSE_IN,
"found" = IC_PINTYPE_PULSE_OUT,
"not found" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/list_contains/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/value = get_pin_data(IC_INPUT, 2, FALSE)
set_pin_data(IC_OUTPUT, 1, FALSE)
set_pin_data(IC_OUTPUT, 2, null)
set_pin_data(IC_OUTPUT, 3, null)
if(!islist(input_list) || !length(input_list))
push_data()
activate_pin(3)
return
var/index = 1
for(var/item in input_list)
if(item == value || "[item]" == "[value]")
set_pin_data(IC_OUTPUT, 1, TRUE)
set_pin_data(IC_OUTPUT, 2, index)
set_pin_data(IC_OUTPUT, 3, item)
push_data()
activate_pin(2)
return
index++
push_data()
activate_pin(3)
/obj/item/integrated_circuit/list/list_filter_text
name = "list text filter"
desc = "Filters a list by matching text against each value."
icon_state = "template"
complexity = 5
inputs = list(
"list" = IC_PINTYPE_LIST,
"match text" = IC_PINTYPE_STRING
)
outputs = list(
"filtered list" = IC_PINTYPE_LIST,
"matches found" = IC_PINTYPE_BOOLEAN
)
activators = list(
"filter" = IC_PINTYPE_PULSE_IN,
"found" = IC_PINTYPE_PULSE_OUT,
"not found" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/list_filter_text/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/match_text = get_pin_data(IC_INPUT, 2)
var/list/filtered_list = list()
if(!islist(input_list) || !istext(match_text) || !length(match_text))
set_pin_data(IC_OUTPUT, 1, filtered_list)
set_pin_data(IC_OUTPUT, 2, FALSE)
push_data()
activate_pin(3)
return
var/lower_match = lowertext(match_text)
for(var/item in input_list)
var/check_text = lowertext("[item]")
if(isweakref(item))
var/datum/weakref/WR = item
var/atom/A = WR.resolve()
if(A)
check_text = lowertext("[A.name] [A.desc]")
else if(istype(item, /atom))
var/atom/A = item
check_text = lowertext("[A.name] [A.desc]")
if(findtext(check_text, lower_match))
filtered_list += item
set_pin_data(IC_OUTPUT, 1, filtered_list)
set_pin_data(IC_OUTPUT, 2, length(filtered_list) ? TRUE : FALSE)
push_data()
activate_pin(length(filtered_list) ? 2 : 3)
/obj/item/integrated_circuit/list/list_join
name = "list joiner"
desc = "Joins a list into a single string using a separator."
icon_state = "template"
complexity = 3
inputs = list(
"list" = IC_PINTYPE_LIST,
"separator" = IC_PINTYPE_STRING
)
outputs = list(
"joined string" = IC_PINTYPE_STRING
)
activators = list(
"join" = IC_PINTYPE_PULSE_IN,
"on joined" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/list_join/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/separator = get_pin_data(IC_INPUT, 2)
if(!islist(input_list))
input_list = list()
if(!istext(separator))
separator = ", "
var/list/text_values = list()
for(var/item in input_list)
text_values += "[item]"
set_pin_data(IC_OUTPUT, 1, jointext(text_values, separator))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/list_sort
name = "list sorter"
desc = "Sorts a list."
extended_desc = "Sorts values alphabetically or numerically depending on what the list contains."
icon_state = "template"
complexity = 4
inputs = list(
"list" = IC_PINTYPE_LIST,
"ascending" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"sorted list" = IC_PINTYPE_LIST
)
activators = list(
"sort" = IC_PINTYPE_PULSE_IN,
"on sorted" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/list_sort/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/ascending = get_pin_data(IC_INPUT, 2)
if(!islist(input_list))
input_list = list()
var/list/sorted_list = input_list.Copy()
sortTim(sorted_list, /proc/cmp_text_asc)
if(!ascending)
sorted_list = reverseList(sorted_list)
set_pin_data(IC_OUTPUT, 1, sorted_list)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/random_selector
name = "random list selector"
desc = "Selects a random value from a list."
icon_state = "template"
complexity = 3
inputs = list(
"list" = IC_PINTYPE_LIST
)
outputs = list(
"selected value" = IC_PINTYPE_ANY,
"selected index" = IC_PINTYPE_NUMBER
)
activators = list(
"select" = IC_PINTYPE_PULSE_IN,
"selected" = IC_PINTYPE_PULSE_OUT,
"not selected" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/random_selector/do_work()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/list_length = islist(input_list) ? input_list.len : 0
if(!list_length)
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, null)
push_data()
activate_pin(3)
return
var/index = rand(1, list_length)
set_pin_data(IC_OUTPUT, 1, input_list[index])
set_pin_data(IC_OUTPUT, 2, index)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/list/weighted_random_selector
name = "weighted random selector"
desc = "Selects a random value from a list using a matching list of weights."
icon_state = "template"
complexity = 6
inputs = list(
"values" = IC_PINTYPE_LIST,
"weights" = IC_PINTYPE_LIST
)
outputs = list(
"selected value" = IC_PINTYPE_ANY,
"selected index" = IC_PINTYPE_NUMBER
)
activators = list(
"select" = IC_PINTYPE_PULSE_IN,
"selected" = IC_PINTYPE_PULSE_OUT,
"not selected" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/list/weighted_random_selector/do_work()
var/list/values = get_pin_data(IC_INPUT, 1)
var/list/weights = get_pin_data(IC_INPUT, 2)
if(!islist(values) || !islist(weights) || !length(values) || !length(weights))
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, null)
push_data()
activate_pin(3)
return
var/selection_count = min(values.len, weights.len)
var/total_weight = 0
for(var/i = 1 to selection_count)
var/weight = weights[i]
if(isnum(weight) && weight > 0)
total_weight += weight
if(total_weight <= 0)
push_data()
activate_pin(3)
return
var/roll = rand(1, total_weight)
var/current_weight = 0
for(var/i = 1 to selection_count)
var/weight = weights[i]
if(!isnum(weight) || weight <= 0)
continue
current_weight += weight
if(roll <= current_weight)
set_pin_data(IC_OUTPUT, 1, values[i])
set_pin_data(IC_OUTPUT, 2, i)
push_data()
activate_pin(2)
return
push_data()
activate_pin(3)
@@ -1,11 +1,16 @@
/*
* subtypes/logic.dm
* Logic and comparison circuits for boolean gates, equality checks, numeric comparisons, and conditional routing.
*/
/obj/item/integrated_circuit/logic
name = "logic gate"
desc = "This tiny chip will decide for you!"
extended_desc = "Logic circuits will treat a null, 0, and a \"\" string value as FALSE and anything else as TRUE. If inputs are of mismatching type, expect undocumented behaviour."
extended_desc = "Logic circuits treat null, 0, and empty text as FALSE. Non-zero numbers, non-empty text, valid refs, and populated lists are TRUE. Compare matching value types for reliable results."
complexity = 3
outputs = list("result" = IC_PINTYPE_BOOLEAN)
activators = list("compare" = IC_PINTYPE_PULSE_IN)
category_text = "Logic"
category_text = "LOGIC - Boolean Logic"
power_draw_per_use = 10
/obj/item/integrated_circuit/logic/binary
@@ -72,6 +77,7 @@
name = "equal gate"
desc = "This gate compares two values, and outputs the number one if both are the same."
icon_state = "equal"
category_text = "LOGIC - Comparisons"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/binary/equals/do_compare(A, B)
@@ -159,6 +165,7 @@
name = "not equal gate"
desc = "This gate compares two values, and outputs the number one if both are different."
icon_state = "not_equal"
category_text = "LOGIC - Comparisons"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/binary/not_equals/do_compare(A, B)
@@ -186,6 +193,7 @@
name = "less than gate"
desc = "This will output 'one' if the first input is less than the second input."
icon_state = "less_than"
category_text = "LOGIC - Comparisons"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/binary/less_than/do_compare(A, B)
@@ -195,6 +203,7 @@
name = "less than or equal gate"
desc = "This will output 'one' if the first input is less than, or equal to the second input."
icon_state = "less_than_or_equal"
category_text = "LOGIC - Comparisons"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/binary/less_than_or_equal/do_compare(A, B)
@@ -204,6 +213,7 @@
name = "greater than gate"
desc = "This will output 'one' if the first input is greater than the second input."
icon_state = "greater_than"
category_text = "LOGIC - Comparisons"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/binary/greater_than/do_compare(A, B)
@@ -213,6 +223,7 @@
name = "greater_than or equal gate"
desc = "This will output 'one' if the first input is greater than, or equal to the second input."
icon_state = "greater_than_or_equal"
category_text = "LOGIC - Comparisons"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/binary/greater_than_or_equal/do_compare(A, B)
@@ -227,3 +238,336 @@
/obj/item/integrated_circuit/logic/unary/not/do_check(A)
return !A
/obj/item/integrated_circuit/logic/edge_detector
name = "edge detector"
desc = "Turns a changing boolean signal into one-shot output pulses."
extended_desc = "Stores the previous signal state internally. It only pulses when the input changes, making it useful for preventing ticker-fed logic from repeatedly firing the same action."
icon_state = "template"
complexity = 2
inputs = list(
"signal" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"current state" = IC_PINTYPE_BOOLEAN,
"previous state" = IC_PINTYPE_BOOLEAN
)
activators = list(
"check" = IC_PINTYPE_PULSE_IN,
"on rising edge" = IC_PINTYPE_PULSE_OUT,
"on falling edge" = IC_PINTYPE_PULSE_OUT,
"on changed" = IC_PINTYPE_PULSE_OUT,
"on unchanged" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
var/previous_state = FALSE
var/has_previous_state = FALSE
/obj/item/integrated_circuit/logic/edge_detector/do_work()
var/current_state = get_pin_data(IC_INPUT, 1) ? TRUE : FALSE
var/old_state = previous_state
set_pin_data(IC_OUTPUT, 1, current_state)
set_pin_data(IC_OUTPUT, 2, old_state)
push_data()
if(!has_previous_state)
previous_state = current_state
has_previous_state = TRUE
activate_pin(5)
return
if(current_state == previous_state)
activate_pin(5)
return
previous_state = current_state
activate_pin(current_state ? 2 : 3)
activate_pin(4)
/obj/item/integrated_circuit/logic/threshold_comparator
name = "threshold comparator"
desc = "Checks whether a number is below, inside, or above a range."
icon_state = "comparator"
category_text = "LOGIC - Comparisons"
complexity = 3
inputs = list(
"value" = IC_PINTYPE_NUMBER,
"minimum" = IC_PINTYPE_NUMBER,
"maximum" = IC_PINTYPE_NUMBER
)
outputs = list(
"below range" = IC_PINTYPE_BOOLEAN,
"inside range" = IC_PINTYPE_BOOLEAN,
"above range" = IC_PINTYPE_BOOLEAN,
"status text" = IC_PINTYPE_STRING
)
activators = list(
"compare" = IC_PINTYPE_PULSE_IN,
"below" = IC_PINTYPE_PULSE_OUT,
"inside" = IC_PINTYPE_PULSE_OUT,
"above" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/threshold_comparator/do_work()
var/value = get_pin_data(IC_INPUT, 1)
var/minimum = get_pin_data(IC_INPUT, 2)
var/maximum = get_pin_data(IC_INPUT, 3)
if(!isnum(value) || !isnum(minimum) || !isnum(maximum))
return
var/below = value < minimum
var/above = value > maximum
var/inside = !below && !above
set_pin_data(IC_OUTPUT, 1, below)
set_pin_data(IC_OUTPUT, 2, inside)
set_pin_data(IC_OUTPUT, 3, above)
set_pin_data(IC_OUTPUT, 4, below ? "BELOW RANGE" : above ? "ABOVE RANGE" : "INSIDE RANGE")
push_data()
if(below)
activate_pin(2)
else if(inside)
activate_pin(3)
else
activate_pin(4)
/obj/item/integrated_circuit/logic/multi_threshold_status
name = "multi-threshold status"
desc = "Classifies a number as normal, warning, or danger using low and high thresholds."
icon_state = "comparator"
category_text = "LOGIC - Comparisons"
complexity = 5
inputs = list(
"value" = IC_PINTYPE_NUMBER,
"danger low" = IC_PINTYPE_NUMBER,
"warning low" = IC_PINTYPE_NUMBER,
"warning high" = IC_PINTYPE_NUMBER,
"danger high" = IC_PINTYPE_NUMBER
)
outputs = list(
"normal" = IC_PINTYPE_BOOLEAN,
"warning" = IC_PINTYPE_BOOLEAN,
"danger" = IC_PINTYPE_BOOLEAN,
"status text" = IC_PINTYPE_STRING
)
activators = list(
"check" = IC_PINTYPE_PULSE_IN,
"normal" = IC_PINTYPE_PULSE_OUT,
"warning" = IC_PINTYPE_PULSE_OUT,
"danger" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/logic/multi_threshold_status/do_work()
var/value = get_pin_data(IC_INPUT, 1)
var/danger_low = get_pin_data(IC_INPUT, 2)
var/warning_low = get_pin_data(IC_INPUT, 3)
var/warning_high = get_pin_data(IC_INPUT, 4)
var/danger_high = get_pin_data(IC_INPUT, 5)
if(!isnum(value) || !isnum(danger_low) || !isnum(warning_low) || !isnum(warning_high) || !isnum(danger_high))
return
var/normal = FALSE
var/warning = FALSE
var/danger = FALSE
var/status = "NORMAL"
if(value <= danger_low)
danger = TRUE
status = "LOW DANGER"
else if(value < warning_low)
warning = TRUE
status = "LOW WARNING"
else if(value >= danger_high)
danger = TRUE
status = "HIGH DANGER"
else if(value > warning_high)
warning = TRUE
status = "HIGH WARNING"
else
normal = TRUE
set_pin_data(IC_OUTPUT, 1, normal)
set_pin_data(IC_OUTPUT, 2, warning)
set_pin_data(IC_OUTPUT, 3, danger)
set_pin_data(IC_OUTPUT, 4, status)
push_data()
if(normal)
activate_pin(2)
else if(warning)
activate_pin(3)
else
activate_pin(4)
/obj/item/integrated_circuit/logic/cooldown_limiter
name = "cooldown limiter"
desc = "Allows a pulse through only when its cooldown has expired."
extended_desc = "Cooldown is clamped between 1 and 600 seconds. Disabled gates block incoming pulses without updating their cooldown."
icon_state = "template"
complexity = 4
inputs = list(
"cooldown seconds" = IC_PINTYPE_NUMBER,
"enabled" = IC_PINTYPE_BOOLEAN
)
inputs_default = list(
"1" = 2,
"2" = TRUE
)
outputs = list(
"ready" = IC_PINTYPE_BOOLEAN,
"remaining cooldown" = IC_PINTYPE_NUMBER
)
activators = list(
"pulse in" = IC_PINTYPE_PULSE_IN,
"pulse out" = IC_PINTYPE_PULSE_OUT,
"on blocked" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 20
var/next_allowed_time = 0
/obj/item/integrated_circuit/logic/cooldown_limiter/do_work()
var/cooldown_seconds = get_pin_data(IC_INPUT, 1)
var/enabled = get_pin_data(IC_INPUT, 2)
if(!isnum(cooldown_seconds))
cooldown_seconds = 2
cooldown_seconds = between(1, cooldown_seconds, 600)
if(!enabled)
set_pin_data(IC_OUTPUT, 1, FALSE)
set_pin_data(IC_OUTPUT, 2, 0)
push_data()
activate_pin(3)
return
if(world.time >= next_allowed_time)
next_allowed_time = world.time + cooldown_seconds SECONDS
set_pin_data(IC_OUTPUT, 1, TRUE)
set_pin_data(IC_OUTPUT, 2, 0)
push_data()
activate_pin(2)
return
var/remaining = max(round((next_allowed_time - world.time) / 10, 0.1), 0)
set_pin_data(IC_OUTPUT, 1, FALSE)
set_pin_data(IC_OUTPUT, 2, remaining)
push_data()
activate_pin(3)
/obj/item/integrated_circuit/logic/pulse_counter
name = "threshold pulse counter"
desc = "Counts incoming pulses and reports when a threshold has been reached."
extended_desc = "Count is clamped between -100000 and 100000. Use the reset pulse to set the count to the configured reset value."
icon_state = "counter"
complexity = 3
inputs = list(
"threshold" = IC_PINTYPE_NUMBER,
"reset value" = IC_PINTYPE_NUMBER
)
inputs_default = list(
"1" = 1,
"2" = 0
)
outputs = list(
"count" = IC_PINTYPE_NUMBER,
"threshold met" = IC_PINTYPE_BOOLEAN
)
activators = list(
"increment" = IC_PINTYPE_PULSE_IN,
"decrement" = IC_PINTYPE_PULSE_IN,
"reset" = IC_PINTYPE_PULSE_IN,
"on count changed" = IC_PINTYPE_PULSE_OUT,
"on threshold met" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 20
var/count = 0
/obj/item/integrated_circuit/logic/pulse_counter/do_work(activator_id)
var/active_pin = activator_id
if(istype(active_pin, /datum/integrated_io))
active_pin = activators.Find(active_pin)
if(!isnum(active_pin))
active_pin = 1
switch(active_pin)
if(2)
count--
if(3)
var/reset_value = get_pin_data(IC_INPUT, 2)
count = isnum(reset_value) ? round(reset_value) : 0
else
count++
count = clamp(count, -100000, 100000)
var/threshold = get_pin_data(IC_INPUT, 1)
if(!isnum(threshold))
threshold = 1
threshold = clamp(round(threshold), -100000, 100000)
var/threshold_met = count >= threshold
set_pin_data(IC_OUTPUT, 1, count)
set_pin_data(IC_OUTPUT, 2, threshold_met)
push_data()
activate_pin(4)
if(threshold_met)
activate_pin(5)
/obj/item/integrated_circuit/logic/pulse_sequencer
name = "pulse sequencer"
desc = "Cycles through four output pulses. If reset is true when pulsed, the sequence resets instead."
icon_state = "template"
complexity = 4
inputs = list(
"reset" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"current step" = IC_PINTYPE_NUMBER
)
activators = list(
"pulse" = IC_PINTYPE_PULSE_IN,
"step 1 pulse" = IC_PINTYPE_PULSE_OUT,
"step 2 pulse" = IC_PINTYPE_PULSE_OUT,
"step 3 pulse" = IC_PINTYPE_PULSE_OUT,
"step 4 pulse" = IC_PINTYPE_PULSE_OUT,
"on reset" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
var/current_step = 0
/obj/item/integrated_circuit/logic/pulse_sequencer/do_work()
if(get_pin_data(IC_INPUT, 1))
current_step = 0
set_pin_data(IC_OUTPUT, 1, current_step)
push_data()
activate_pin(6)
return
current_step++
if(current_step > 4)
current_step = 1
set_pin_data(IC_OUTPUT, 1, current_step)
push_data()
activate_pin(current_step + 1)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,8 @@
/*
* subtypes/memory.dm
* Memory circuits that store circuit values across pulses and expose saved values through output pins.
*/
/obj/item/integrated_circuit/memory
complexity = 1
category_text = "Memory"
@@ -40,6 +45,9 @@
/obj/item/integrated_circuit/memory/storage/do_work()
for(var/i = 1 to inputs.len)
var/data = get_pin_data(IC_INPUT, i)
if(islist(data))
var/list/list_data = data
data = list_data.Copy()
set_pin_data(IC_OUTPUT, i, data)
push_data()
activate_pin(2)
@@ -53,7 +61,7 @@
/obj/item/integrated_circuit/memory/storage/large
name = "large memory circuit"
desc = "This big circuit can hold eight pieces of data."
desc = "This circuit can store eight pieces of data."
icon_state = "memory8"
origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
power_draw_per_use = 40
@@ -61,7 +69,7 @@
/obj/item/integrated_circuit/memory/storage/huge
name = "large memory stick"
desc = "This stick of memory can hold up up to sixteen pieces of data."
desc = "This memory stick can store up to sixteen pieces of data."
icon_state = "memory16"
w_class = WEIGHT_CLASS_NORMAL
spawn_flags = IC_SPAWN_RESEARCH
@@ -71,7 +79,7 @@
/obj/item/integrated_circuit/memory/constant
name = "constant chip"
desc = "This tiny chip can store one piece of data, which cannot be overwritten without disassembly."
desc = "This chip stores one constant value. It cannot be changed without disassembly."
icon_state = "memory1"
inputs = list()
outputs = list("output pin" = IC_PINTYPE_ANY)
@@ -84,7 +92,11 @@
var/data
/obj/item/integrated_circuit/memory/constant/do_work()
set_pin_data(IC_OUTPUT, 1, data)
var/output_data = data
if(islist(output_data))
var/list/list_data = output_data
output_data = list_data.Copy()
set_pin_data(IC_OUTPUT, 1, output_data)
push_data()
activate_pin(2)
@@ -1,9 +1,14 @@
/*
* subtypes/output.dm
* Output circuits that speak, display, signal, pulse, transmit, or otherwise emit circuit results into the game world.
*/
/obj/item/integrated_circuit/output
category_text = "Output"
/obj/item/integrated_circuit/output/screen
name = "small screen"
desc = "This small screen can display a single piece of data, when the machine is examined closely."
desc = "Displays one value when the assembly is examined closely."
icon_state = "screen"
inputs = list("displayed data" = IC_PINTYPE_ANY)
outputs = list()
@@ -34,31 +39,130 @@
/obj/item/integrated_circuit/output/screen/medium
name = "screen"
desc = "This screen allows for people holding the device to see a piece of data."
desc = "Displays data to the person holding the device."
icon_state = "screen_medium"
power_draw_per_use = 200
/obj/item/integrated_circuit/output/screen/medium/do_work()
..()
var/list/nearby_things = range(0, get_turf(src))
var/turf/T = get_turf(src)
if(!T)
return
var/list/nearby_things = range(0, T)
var/list/viewing = viewers(T)
for(var/mob/M in nearby_things)
var/obj/O = assembly ? assembly : src
to_chat(M, SPAN_NOTICE("[icon2html(O, viewers(get_turf(src)))] [stuff_to_display]"))
to_chat(M, SPAN_NOTICE("[icon2html(O, viewing)] [stuff_to_display]"))
/obj/item/integrated_circuit/output/screen/large
name = "large screen"
desc = "This screen allows for people able to see the device to see a piece of data."
desc = "Displays data to nearby viewers who can see the device."
icon_state = "screen_large"
power_draw_per_use = 400
/obj/item/integrated_circuit/output/screen/large/do_work()
..()
var/obj/O = assembly ? loc : assembly
O.visible_message(SPAN_NOTICE("[icon2html(O, viewers(get_turf(src)))] [stuff_to_display]"))
var/obj/O = assembly ? assembly : src
var/turf/T = get_turf(src)
if(!O || !T)
return
O.visible_message(SPAN_NOTICE("[icon2html(O, viewers(T))] [stuff_to_display]"))
/obj/item/integrated_circuit/output/state_display
name = "state display"
desc = "Displays a compact label and value when the assembly is examined closely."
icon_state = "screen"
complexity = 2
inputs = list(
"label" = IC_PINTYPE_STRING,
"value" = IC_PINTYPE_ANY,
"enabled" = IC_PINTYPE_BOOLEAN
)
inputs_default = list(
"1" = "Status",
"3" = TRUE
)
outputs = list(
"displayed" = IC_PINTYPE_BOOLEAN
)
activators = list(
"update" = IC_PINTYPE_PULSE_IN,
"on updated" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 100
var/stuff_to_display = null
/obj/item/integrated_circuit/output/state_display/disconnect_all()
..()
stuff_to_display = null
/obj/item/integrated_circuit/output/state_display/any_examine(mob/user)
. = ..()
if(!isnull(stuff_to_display))
. += "There is a little status display reading '[stuff_to_display]'."
/obj/item/integrated_circuit/output/state_display/do_work()
if(!get_pin_data(IC_INPUT, 3))
stuff_to_display = null
set_pin_data(IC_OUTPUT, 1, FALSE)
push_data()
activate_pin(3)
return
var/label = get_pin_data(IC_INPUT, 1)
var/value = get_pin_data(IC_INPUT, 2, FALSE)
if(isnull(value))
stuff_to_display = null
set_pin_data(IC_OUTPUT, 1, FALSE)
push_data()
activate_pin(3)
return
stuff_to_display = sanitizeSafe("[ic_safe_text(label, "Status")]: [ic_display_value(value)]", MAX_MESSAGE_LEN)
set_pin_data(IC_OUTPUT, 1, TRUE)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/output/pulse_result
name = "pulse result router"
desc = "Routes a value into true, false, or invalid pulse outputs."
icon_state = "template"
complexity = 1
inputs = list(
"value" = IC_PINTYPE_ANY
)
outputs = list(
"value" = IC_PINTYPE_ANY
)
activators = list(
"evaluate" = IC_PINTYPE_PULSE_IN,
"on true" = IC_PINTYPE_PULSE_OUT,
"on false" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 10
/obj/item/integrated_circuit/output/pulse_result/do_work()
var/value = get_pin_data(IC_INPUT, 1, FALSE)
set_pin_data(IC_OUTPUT, 1, value)
push_data()
if(isnull(value))
activate_pin(4)
else if(ic_truthy(value))
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/output/light
name = "light"
desc = "This light can turn on and off on command."
desc = "Turns a light on or off when pulsed."
icon_state = "light"
complexity = 4
inputs = list()
@@ -112,7 +216,7 @@
/obj/item/integrated_circuit/output/light/advanced
name = "advanced light"
desc = "This light can turn on and off on command, in any color, and in various brightness levels."
desc = "Turns a configurable colored light on or off when pulsed."
icon_state = "light_adv"
complexity = 8
inputs = list(
@@ -128,7 +232,7 @@
/obj/item/integrated_circuit/output/sound
name = "speaker circuit"
desc = "A miniature speaker is attached to this component."
desc = "Plays sounds from the assembly."
icon_state = "speaker"
complexity = 8
cooldown_per_use = 1 SECOND
@@ -144,8 +248,8 @@
/obj/item/integrated_circuit/output/text_to_speech
name = "text-to-speech circuit"
desc = "A miniature speaker is attached to this component."
extended_desc = "This unit is more advanced than the plain speaker circuit, able to transpose any valid text to speech."
desc = "Plays sounds from the assembly."
extended_desc = "Converts valid text input into spoken audio."
icon_state = "speaker"
complexity = 12
cooldown_per_use = 1 SECOND
@@ -156,10 +260,30 @@
power_draw_per_use = 600
/obj/item/integrated_circuit/output/text_to_speech/do_work()
text = get_pin_data(IC_INPUT, 1)
if(!isnull(text))
var/obj/O = assembly ? loc : assembly
audible_message("[icon2html(O, viewers(get_turf(O)))] \The [O.name] states, \"[text]\"")
var/text = get_pin_data(IC_INPUT, 1)
if(isnull(text))
return
/*
* If this TTS circuit is inside an electronic radio headset,
* output privately to the headset wearer only.
*/
var/obj/item/electronic_assembly/clothing/A = loc
if(istype(A))
var/obj/item/radio/headset/circuitry/H = A.clothing
if(istype(H))
H.private_tts_output(text)
return
/*
* Fallback behavior for every TTS circuit outside the electronic radio headset.
*/
var/obj/O = loc ? loc : src
var/turf/T = get_turf(O)
audible_message("[icon2html(O, viewers(T))] \The [O.name] states, \"[text]\"")
/obj/item/integrated_circuit/output/sound/Initialize()
. = ..()
@@ -183,7 +307,7 @@
/obj/item/integrated_circuit/output/sound/beeper
name = "beeper circuit"
desc = "A miniature speaker is attached to this component. This is often used in the construction of motherboards, which use \
desc = "Plays sounds from the assembly. This is often used in the construction of motherboards, which use \
the speaker to tell the user if something goes very wrong when booting up. It can also do other similar synthetic sounds such \
as buzzing, pinging, chiming, and more."
sounds = list(
@@ -200,7 +324,7 @@
/obj/item/integrated_circuit/output/sound/beepsky
name = "securitron sound circuit"
desc = "A miniature speaker is attached to this component. Considered by some to be the essential component for a securitron."
desc = "A miniature speaker that plays alert tones or warning sounds."
sounds = list(
"creep" = 'sound/voice/bcreep.ogg',
"criminal" = 'sound/voice/bcriminal.ogg',
@@ -215,7 +339,7 @@
/obj/item/integrated_circuit/output/sound/medbot
name = "medibot sound circuit"
desc = "A miniature speaker is attached to this component, used to annoy patients while they get pricked by a medbot."
desc = "A miniature speaker that plays medical bot audio cues."
sounds = list(
"surgeon" = 'sound/voice/medbot/msurgeon.ogg',
"radar" = 'sound/voice/medbot/mradar.ogg',
@@ -237,8 +361,8 @@
/obj/item/integrated_circuit/output/video_camera
name = "video camera circuit"
desc = "This small camera allows a remote viewer to see what it sees."
extended_desc = "The camera is linked to the Research camera network by default, but can be changed."
desc = "Creates a camera feed from the assembly's position."
extended_desc = "Creates a camera feed. It uses the Research camera network by default, but the network can be changed."
icon_state = "video_camera"
w_class = WEIGHT_CLASS_SMALL
complexity = 10
@@ -295,8 +419,8 @@
/obj/item/integrated_circuit/output/led
name = "light-emitting diode"
desc = "This a LED that is lit whenever there is TRUE-equivalent data on its input."
extended_desc = "TRUE-equivalent values are: Non-empty strings, non-zero numbers, and valid refs."
desc = "This LED lights while its input contains TRUE-equivalent data."
extended_desc = "TRUE values include non-empty text, non-zero numbers, valid refs, and populated lists. FALSE values include null, 0, and empty text."
complexity = 0.1
icon_state = "led"
inputs = list("lit" = IC_PINTYPE_BOOLEAN)
@@ -375,7 +499,7 @@
/obj/item/integrated_circuit/output/printer
name = "printer"
desc = "This printer can print text to a sheet of paper. If the eject pin is set or the paper is full of text it will eject the paper after printing"
desc = "Prints text to paper. If the eject input is enabled, or if the paper is full, the paper is ejected after printing."
icon_state = "screen"
inputs = list("printed data" = IC_PINTYPE_ANY, "paper source" = IC_PINTYPE_REF, "eject sheet" = IC_PINTYPE_BOOLEAN)
outputs = list()
@@ -410,6 +534,8 @@
paper_sheet.set_content(null, copytext(stuff_to_print, 1, MAX_PAPER_MESSAGE_LEN))
stuff_to_print = copytext(stuff_to_print, MAX_PAPER_MESSAGE_LEN)
if(stuff_to_print || eject)
if(!using_tray)
return
paper_sheet = paper_source.get_item(TRUE)
audible_message(SPAN_NOTICE("\The [src] buzzes and spits out a sheet of paper."))
paper_sheet.forceMove(get_turf(src))
@@ -1,9 +1,14 @@
/*
* subtypes/power.dm
* Active power-management circuits that expose assembly charge, draw, and cell state to the circuit network.
*/
/obj/item/integrated_circuit/power/
category_text = "Power - Active"
/obj/item/integrated_circuit/power/transmitter
name = "power transmission circuit"
desc = "This can wirelessly transmit electricity from an assembly's battery towards a nearby machine."
desc = "Wirelessly transmits electricity from an assembly battery to a nearby machine."
icon_state = "power_transmitter"
extended_desc = "This circuit transmits 5 kJ of electricity every time the activator pin is pulsed. The input pin must be \
a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \
@@ -25,7 +30,7 @@
/obj/item/integrated_circuit/power/transmitter/large
name = "large power transmission circuit"
desc = "This can wirelessly transmit a lot of electricity from an assembly's battery towards a nearby machine. Warning: Do not operate in flammable enviroments."
desc = "Wirelessly transmits a large amount of electricity from an assembly battery to a nearby machine. Avoid use near flammable environments."
extended_desc = "This circuit transmits 20 kJ of electricity every time the activator pin is pulsed. The input pin must be \
a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \
inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \
@@ -1,3 +1,8 @@
/*
* subtypes/reagents.dm
* Reagent circuits for reading containers, transferring chemicals, and exposing reagent metadata.
*/
/obj/item/integrated_circuit/reagent
category_text = "Reagent"
var/volume = 0
@@ -10,7 +15,7 @@
/obj/item/integrated_circuit/reagent/injector
name = "integrated hypo-injector"
desc = "This scary looking thing is able to pump liquids into whatever it's pointed at."
desc = "Pumps reagents into the targeted adjacent container or target."
icon_state = "injector"
extended_desc = "This autoinjector can push reagents into another container or someone else outside of the machine. The target \
must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing."
@@ -19,7 +24,7 @@
cooldown_per_use = 6 SECONDS
inputs = list("target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER)
inputs_default = list("2" = 5)
outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF)
outputs = list("volume used" = IC_PINTYPE_NUMBER, "self reference" = IC_PINTYPE_REF)
activators = list("inject" = IC_PINTYPE_PULSE_IN, "on injected" = IC_PINTYPE_PULSE_OUT, "on fail" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
volume = 30
@@ -27,11 +32,13 @@
var/direc = 1
var/transfer_amount = 10
/obj/item/integrated_circuit/reagent/injector/interact(mob/user)
set_pin_data(IC_OUTPUT, 2, src)
push_data()
..()
/obj/item/integrated_circuit/reagent/injector/on_reagent_change()
set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
push_data()
@@ -39,94 +46,144 @@
/obj/item/integrated_circuit/reagent/injector/on_data_written()
var/new_amount = get_pin_data(IC_INPUT, 2)
if(!isnum(new_amount))
return
if(new_amount < 0)
new_amount = -new_amount
direc = 0
else
direc = 1
if(isnum(new_amount))
new_amount = clamp(new_amount, 0, volume)
transfer_amount = new_amount
new_amount = clamp(new_amount, 0, volume)
transfer_amount = new_amount
/obj/item/integrated_circuit/reagent/injector/do_work()
set waitfor = 0 // Don't sleep in a proc that is called by a processor without this set, otherwise it'll delay the entire thing
var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
if(!istype(AM)) //Invalid input
if(!istype(AM))
activate_pin(3)
return
if(direc == 1)
if(!reagents || !reagents.total_volume)
activate_pin(3)
return
if(!istype(AM)) //Invalid input
if(!AM.can_be_injected_by(src))
activate_pin(3)
return
if(!reagents.total_volume) // Empty
activate_pin(3)
return
if(AM.can_be_injected_by(src))
if(isliving(AM))
var/mob/living/L = AM
var/turf/T = get_turf(AM)
if(isliving(AM))
var/mob/living/L = AM
var/turf/T = get_turf(AM)
if(T)
T.visible_message(SPAN_WARNING("[assembly] is trying to inject [L]!"))
sleep(3 SECONDS)
if(!L.can_be_injected_by(src))
activate_pin(3)
return
var/contained = reagents.get_reagents()
var/trans = reagents.trans_to_mob(L, transfer_amount, CHEM_BLOOD)
message_admins("[assembly] injected \the [L] with [trans]u of [contained].")
to_chat(AM, SPAN_NOTICE("You feel a tiny prick!"))
visible_message(SPAN_WARNING("[assembly] injects [L]!"))
else
reagents.trans_to(AM, transfer_amount)
else
if(reagents.total_volume >= volume) // Full
addtimer(CALLBACK(src, PROC_REF(finish_living_injection), L), 3 SECONDS)
return
if(reagents.trans_to(AM, transfer_amount))
activate_pin(2)
else
activate_pin(3)
return
if(reagents.total_volume >= volume)
activate_pin(3)
return
var/obj/target = AM
if(!target.reagents)
activate_pin(3)
return
var/turf/TS = get_turf(src)
var/turf/TT = get_turf(AM)
if(!TS || !TT || !TS.Adjacent(TT))
activate_pin(3)
return
var/tramount = clamp(min(transfer_amount, REAGENTS_FREE_SPACE(reagents)), 0, reagents.maximum_volume)
if(ismob(target))
if(!istype(target, /mob/living/carbon))
activate_pin(3)
return
var/obj/target = AM
if(!target.reagents)
activate_pin(3)
return
var/turf/TS = get_turf(src)
var/turf/TT = get_turf(AM)
if(!TS.Adjacent(TT))
activate_pin(3)
return
var/tramount = clamp(min(transfer_amount, REAGENTS_FREE_SPACE(reagents)), 0, reagents.maximum_volume)
if(ismob(target))//Blood!
if(istype(target, /mob/living/carbon))
var/mob/living/carbon/T = target
if(!T.dna)
if(T.reagents.trans_to_obj(src, tramount))
activate_pin(2)
else
activate_pin(3)
return
if((T.mutations & NOCLONE)) //target done been et, no more blood in him
if(T.reagents.trans_to_obj(src, tramount))
activate_pin(2)
else
activate_pin(3)
return
T.take_blood(src,tramount)
visible_message( SPAN_NOTICE("[assembly] takes a blood sample from [target]."))
var/mob/living/carbon/T = target
if(!T.dna)
if(T.reagents.trans_to_obj(src, tramount))
activate_pin(2)
else
activate_pin(3)
return
return
else //if not mob
if(!target.reagents.total_volume)
visible_message( SPAN_NOTICE("[target] is empty."))
if(T.mutations & NOCLONE)
if(T.reagents.trans_to_obj(src, tramount))
activate_pin(2)
else
activate_pin(3)
return
target.reagents.trans_to_obj(src, tramount)
return
T.take_blood(src, tramount)
visible_message(SPAN_NOTICE("[assembly] takes a blood sample from [target]."))
activate_pin(2)
return
if(!target.reagents.total_volume)
visible_message(SPAN_NOTICE("[target] is empty."))
activate_pin(3)
return
target.reagents.trans_to_obj(src, tramount)
activate_pin(2)
/obj/item/integrated_circuit/reagent/injector/proc/finish_living_injection(mob/living/L)
if(QDELETED(src) || QDELETED(L))
return
if(!assembly)
activate_pin(3)
return
if(!reagents || !reagents.total_volume)
activate_pin(3)
return
if(!L.can_be_injected_by(src))
activate_pin(3)
return
var/turf/TS = get_turf(src)
var/turf/TL = get_turf(L)
if(!TS || !TL || !TS.Adjacent(TL))
activate_pin(3)
return
var/contained = reagents.get_reagents()
var/trans = reagents.trans_to_mob(L, transfer_amount, CHEM_BLOOD)
if(trans)
message_admins("[assembly] injected \the [L] with [trans]u of [contained].")
to_chat(L, SPAN_NOTICE("You feel a tiny prick!"))
visible_message(SPAN_WARNING("[assembly] injects [L]!"))
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/reagent/pump
name = "reagent pump"
desc = "Moves liquids safely inside a machine, or even nearby it."
desc = "Moves reagents inside the assembly or to nearby targets."
icon_state = "reagent_pump"
extended_desc = "This is a pump, which will move liquids from the source ref to the target ref. The third pin determines \
how much liquid is moved per pulse, between 0 and 50. The pump can move reagents to any open container inside the machine, or \
@@ -195,9 +252,9 @@
/obj/item/integrated_circuit/reagent/storage
name = "reagent storage"
desc = "Stores liquid inside, and away from electrical components. Can store up to 60u."
desc = "Stores reagents safely inside the assembly. Can store up to 60u."
icon_state = "reagent_storage"
extended_desc = "This is effectively an internal beaker."
extended_desc = "Functions as an internal beaker for the assembly."
atom_flags = ATOM_FLAG_OPEN_CONTAINER
complexity = 4
inputs = list()
@@ -218,9 +275,9 @@
/obj/item/integrated_circuit/reagent/storage/cryo
name = "cryo reagent storage"
desc = "Stores liquid inside, and away from electrical components. Can store up to 60u. This will also suppress reactions."
desc = "Stores reagents safely inside the assembly. Can store up to 60u. This will also suppress reactions."
icon_state = "reagent_storage_cryo"
extended_desc = "This is effectively an internal cryo beaker."
extended_desc = "Functions as an internal cryo beaker for the assembly."
atom_flags = ATOM_FLAG_OPEN_CONTAINER | ATOM_FLAG_NO_REACT
complexity = 8
spawn_flags = IC_SPAWN_RESEARCH
@@ -228,9 +285,9 @@
/obj/item/integrated_circuit/reagent/storage/big
name = "big reagent storage"
desc = "Stores liquid inside, and away from electrical components. Can store up to 180u."
desc = "Stores reagents safely inside the assembly. Can store up to 180u."
icon_state = "reagent_storage_big"
extended_desc = "This is effectively an internal beaker."
extended_desc = "Functions as an internal beaker for the assembly."
atom_flags = ATOM_FLAG_OPEN_CONTAINER
complexity = 16
volume = 180
@@ -239,9 +296,9 @@
/obj/item/integrated_circuit/reagent/storage/scan
name = "reagent scanner"
desc = "Stores liquid inside, and away from electrical components. Can store up to 60u. On pulse this beaker will send list of contained reagents, as well as analyse their taste."
desc = "Stores reagents safely inside the assembly. Can store up to 60u. On pulse, outputs a list of contained reagents and analyzes their taste."
icon_state = "reagent_scan"
extended_desc = "Mostly useful for reagent filters."
extended_desc = "Useful for reagent filters and other chemistry circuits that need a target reagent name."
atom_flags = ATOM_FLAG_OPEN_CONTAINER
complexity = 8
outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF,"list of reagents" = IC_PINTYPE_LIST,"taste" = IC_PINTYPE_STRING)
@@ -261,7 +318,7 @@
/obj/item/integrated_circuit/reagent/filter
name = "reagent filter"
desc = "Filtering liquids by list of desired or unwanted reagents."
desc = "Filters reagents using a configured list of desired or blocked reagent names."
icon_state = "reagent_filter"
extended_desc = "This is a filter which will move liquids from the source ref to the target ref. \
It will move all reagents, except list, given in fourth pin if amount value is positive.\
@@ -0,0 +1,878 @@
GLOBAL_LIST_EMPTY(ic_database_servers)
/proc/ic_normalize_database_id(database_id)
if(!istext(database_id) || !length(database_id))
return "main"
return lowertext(database_id)
/proc/ic_database_response(success = FALSE, response = null, status = "No response.")
return list(
"success" = success,
"response" = response,
"status" = status
)
/obj/item/integrated_circuit/server
category_text = "Data Server"
complexity = 2
power_draw_per_use = 300
spawn_flags = 0
activators = list(
"compute" = IC_PINTYPE_PULSE_IN,
"on computed" = IC_PINTYPE_PULSE_OUT
)
/obj/item/integrated_circuit/server/proc/build_record_from_pins(field_count)
var/list/record = list()
for(var/i = 1 to field_count)
var/key = get_pin_data(IC_INPUT, (i * 2) - 1)
if(istext(key) && length(key))
record[key] = get_pin_data(IC_INPUT, i * 2)
return record
/obj/item/integrated_circuit/server/database
name = "database server"
desc = "A keyed database server for integrated circuits."
extended_desc = "Stores keyed values for server client circuits. Commands: GET, SET, DELETE, CLEAR, HAS, KEYS, LEN, APPEND."
icon_state = "addition"
complexity = 8
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"server id" = IC_PINTYPE_STRING,
"command" = IC_PINTYPE_STRING,
"key" = IC_PINTYPE_STRING,
"value" = IC_PINTYPE_ANY
)
inputs_default = list(
"1" = "main",
"2" = "GET"
)
outputs = list(
"success" = IC_PINTYPE_BOOLEAN,
"response" = IC_PINTYPE_ANY,
"status" = IC_PINTYPE_STRING
)
var/database_id = "main"
var/list/storage = list()
/obj/item/integrated_circuit/server/database/Initialize()
. = ..()
register_database()
/obj/item/integrated_circuit/server/database/Destroy()
unregister_database()
storage = null
return ..()
/obj/item/integrated_circuit/server/database/proc/register_database()
database_id = ic_normalize_database_id(database_id)
GLOB.ic_database_servers[database_id] = src
/obj/item/integrated_circuit/server/database/proc/unregister_database()
if(database_id && GLOB.ic_database_servers[database_id] == src)
GLOB.ic_database_servers -= database_id
/obj/item/integrated_circuit/server/database/proc/set_database_id(new_id)
new_id = ic_normalize_database_id(new_id)
if(new_id == database_id)
return
unregister_database()
database_id = new_id
register_database()
/obj/item/integrated_circuit/server/database/proc/handle_request(command, key, value)
if(!istext(command))
return ic_database_response(FALSE, null, "Command must be text.")
command = uppertext(command)
switch(command)
if("GET")
if(!istext(key) || !length(key))
return ic_database_response(FALSE, null, "GET requires a text key.")
return ic_database_response(TRUE, storage[key], "Read complete.")
if("SET")
if(!istext(key) || !length(key))
return ic_database_response(FALSE, null, "SET requires a text key.")
storage[key] = value
return ic_database_response(TRUE, value, "Write complete.")
if("DELETE")
if(!istext(key) || !length(key))
return ic_database_response(FALSE, null, "DELETE requires a text key.")
var/existed = (key in storage)
storage -= key
if(existed)
return ic_database_response(TRUE, TRUE, "Deleted.")
return ic_database_response(FALSE, FALSE, "Key not found.")
if("CLEAR")
storage.Cut()
return ic_database_response(TRUE, TRUE, "Database cleared.")
if("HAS")
if(!istext(key) || !length(key))
return ic_database_response(FALSE, null, "HAS requires a text key.")
return ic_database_response(TRUE, (key in storage), "Check complete.")
if("KEYS")
var/list/keys = list()
for(var/K in storage)
keys.Add(K)
return ic_database_response(TRUE, keys, "Keys returned.")
if("LEN")
return ic_database_response(TRUE, storage.len, "Length returned.")
if("APPEND")
if(!istext(key) || !length(key))
return ic_database_response(FALSE, null, "APPEND requires a text key.")
var/list/current = storage[key]
if(!islist(current))
current = list()
current.Add(value)
storage[key] = current
return ic_database_response(TRUE, current, "Append complete.")
return ic_database_response(FALSE, null, "Unknown command.")
/obj/item/integrated_circuit/server/database/do_work()
pull_data()
var/new_id = get_pin_data(IC_INPUT, 1)
var/command = get_pin_data(IC_INPUT, 2)
var/key = get_pin_data(IC_INPUT, 3)
var/value = get_pin_data(IC_INPUT, 4)
set_database_id(new_id)
var/list/result = handle_request(command, key, value)
set_pin_data(IC_OUTPUT, 1, result["success"])
set_pin_data(IC_OUTPUT, 2, result["response"])
set_pin_data(IC_OUTPUT, 3, result["status"])
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/client
name = "database client"
desc = "Sends requests to a database server."
extended_desc = "Targets a database server by server id. Commands: GET, SET, DELETE, CLEAR, HAS, KEYS, LEN, APPEND."
icon_state = "addition"
complexity = 4
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"server id" = IC_PINTYPE_STRING,
"command" = IC_PINTYPE_STRING,
"key" = IC_PINTYPE_STRING,
"value" = IC_PINTYPE_ANY
)
inputs_default = list(
"1" = "main",
"2" = "GET"
)
outputs = list(
"success" = IC_PINTYPE_BOOLEAN,
"response" = IC_PINTYPE_ANY,
"status" = IC_PINTYPE_STRING
)
/obj/item/integrated_circuit/server/client/do_work()
pull_data()
var/database_id = ic_normalize_database_id(get_pin_data(IC_INPUT, 1))
var/command = get_pin_data(IC_INPUT, 2)
var/key = get_pin_data(IC_INPUT, 3)
var/value = get_pin_data(IC_INPUT, 4)
var/list/result = ic_database_response(FALSE, null, "Server not found.")
var/obj/item/integrated_circuit/server/database/S = GLOB.ic_database_servers[database_id]
if(istype(S))
result = S.handle_request(command, key, value)
set_pin_data(IC_OUTPUT, 1, result["success"])
set_pin_data(IC_OUTPUT, 2, result["response"])
set_pin_data(IC_OUTPUT, 3, result["status"])
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/scanner
name = "database scanner"
desc = "Outputs a list of active database server ids."
extended_desc = "Useful for debugging database wiring or discovering which database servers are available."
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list()
outputs = list(
"server ids" = IC_PINTYPE_LIST,
"server count" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/server/scanner/do_work()
var/list/server_ids = list()
for(var/database_id in GLOB.ic_database_servers)
var/obj/item/integrated_circuit/server/database/S = GLOB.ic_database_servers[database_id]
if(istype(S))
server_ids.Add(database_id)
set_pin_data(IC_OUTPUT, 1, server_ids)
set_pin_data(IC_OUTPUT, 2, server_ids.len)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/record_builder4
name = "4-field record builder"
desc = "Builds an associative list record from up to four key/value pairs."
extended_desc = "This is useful for making database records like pressure, oxygen, temperature, and status."
icon_state = "addition"
complexity = 3
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"key 1" = IC_PINTYPE_STRING,
"value 1" = IC_PINTYPE_ANY,
"key 2" = IC_PINTYPE_STRING,
"value 2" = IC_PINTYPE_ANY,
"key 3" = IC_PINTYPE_STRING,
"value 3" = IC_PINTYPE_ANY,
"key 4" = IC_PINTYPE_STRING,
"value 4" = IC_PINTYPE_ANY
)
outputs = list(
"record" = IC_PINTYPE_LIST
)
/obj/item/integrated_circuit/server/record_builder4/do_work()
pull_data()
set_pin_data(IC_OUTPUT, 1, build_record_from_pins(4))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/record_builder8
name = "8-field record builder"
desc = "Builds an associative list record from up to eight key/value pairs."
extended_desc = "This is useful for larger database records."
icon_state = "addition"
complexity = 5
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"key 1" = IC_PINTYPE_STRING,
"value 1" = IC_PINTYPE_ANY,
"key 2" = IC_PINTYPE_STRING,
"value 2" = IC_PINTYPE_ANY,
"key 3" = IC_PINTYPE_STRING,
"value 3" = IC_PINTYPE_ANY,
"key 4" = IC_PINTYPE_STRING,
"value 4" = IC_PINTYPE_ANY,
"key 5" = IC_PINTYPE_STRING,
"value 5" = IC_PINTYPE_ANY,
"key 6" = IC_PINTYPE_STRING,
"value 6" = IC_PINTYPE_ANY,
"key 7" = IC_PINTYPE_STRING,
"value 7" = IC_PINTYPE_ANY,
"key 8" = IC_PINTYPE_STRING,
"value 8" = IC_PINTYPE_ANY
)
outputs = list(
"record" = IC_PINTYPE_LIST
)
/obj/item/integrated_circuit/server/record_builder8/do_work()
pull_data()
set_pin_data(IC_OUTPUT, 1, build_record_from_pins(8))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/record_reader
name = "record reader"
desc = "Reads one field from an associative list record."
extended_desc = "Use this after a database GET returns a record list."
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"record" = IC_PINTYPE_LIST,
"field" = IC_PINTYPE_STRING
)
outputs = list(
"value" = IC_PINTYPE_ANY,
"found" = IC_PINTYPE_BOOLEAN
)
/obj/item/integrated_circuit/server/record_reader/do_work()
pull_data()
var/list/record = get_pin_data(IC_INPUT, 1)
var/field = get_pin_data(IC_INPUT, 2)
var/value = null
var/found = FALSE
if(islist(record) && istext(field) && length(field))
value = record[field]
found = !isnull(value) || (field in record)
set_pin_data(IC_OUTPUT, 1, value)
set_pin_data(IC_OUTPUT, 2, found)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/record_writer
name = "record writer"
desc = "Writes one field into an associative list record."
extended_desc = "Takes an existing record, writes a field, and outputs the edited record."
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"record" = IC_PINTYPE_LIST,
"field" = IC_PINTYPE_STRING,
"value" = IC_PINTYPE_ANY
)
outputs = list(
"record" = IC_PINTYPE_LIST,
"success" = IC_PINTYPE_BOOLEAN
)
/obj/item/integrated_circuit/server/record_writer/do_work()
pull_data()
var/list/input_record = get_pin_data(IC_INPUT, 1)
var/field = get_pin_data(IC_INPUT, 2)
var/value = get_pin_data(IC_INPUT, 3)
var/list/output_record = list()
var/success = FALSE
if(islist(input_record))
output_record = input_record.Copy()
if(istext(field) && length(field))
output_record[field] = value
success = TRUE
set_pin_data(IC_OUTPUT, 1, output_record)
set_pin_data(IC_OUTPUT, 2, success)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/record_keys
name = "record keys"
desc = "Outputs a list of keys from an associative list record."
extended_desc = "Useful for reading database records and checking their field names."
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"record" = IC_PINTYPE_LIST
)
outputs = list(
"keys" = IC_PINTYPE_LIST,
"length" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/server/record_keys/do_work()
pull_data()
var/list/record = get_pin_data(IC_INPUT, 1)
var/list/keys = list()
if(islist(record))
for(var/K in record)
keys.Add(K)
set_pin_data(IC_OUTPUT, 1, keys)
set_pin_data(IC_OUTPUT, 2, keys.len)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/text_contains
name = "text contains"
desc = "Checks if one text value contains another."
extended_desc = "Useful for filtering logs, messages, status strings, or command text."
icon_state = "textpad"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"text" = IC_PINTYPE_STRING,
"search" = IC_PINTYPE_STRING,
"case sensitive" = IC_PINTYPE_BOOLEAN
)
inputs_default = list(
"3" = FALSE
)
outputs = list(
"contains" = IC_PINTYPE_BOOLEAN,
"position" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/server/text_contains/do_work()
pull_data()
var/text = get_pin_data(IC_INPUT, 1)
var/search = get_pin_data(IC_INPUT, 2)
var/case_sensitive = get_pin_data(IC_INPUT, 3)
var/position = 0
if(istext(text) && istext(search) && length(search))
if(case_sensitive)
position = findtext(text, search)
else
position = findtext(lowertext(text), lowertext(search))
set_pin_data(IC_OUTPUT, 1, position > 0)
set_pin_data(IC_OUTPUT, 2, position)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/queue_push
name = "queue push"
desc = "Adds a value to the end of a queue list."
extended_desc = "Use this with database APPEND or with a retrieved list."
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"queue" = IC_PINTYPE_LIST,
"value" = IC_PINTYPE_ANY
)
outputs = list(
"queue" = IC_PINTYPE_LIST,
"length" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/server/queue_push/do_work()
pull_data()
var/list/input_queue = get_pin_data(IC_INPUT, 1)
var/value = get_pin_data(IC_INPUT, 2)
var/list/output_queue = list()
if(islist(input_queue))
output_queue = input_queue.Copy()
output_queue.Add(value)
set_pin_data(IC_OUTPUT, 1, output_queue)
set_pin_data(IC_OUTPUT, 2, output_queue.len)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/queue_pop
name = "queue pop"
desc = "Removes and outputs the first value from a queue list."
extended_desc = "Outputs the first list item separately, then outputs the remaining list as a new queue."
icon_state = "subtraction"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"queue" = IC_PINTYPE_LIST
)
outputs = list(
"value" = IC_PINTYPE_ANY,
"remaining queue" = IC_PINTYPE_LIST,
"had value" = IC_PINTYPE_BOOLEAN
)
/obj/item/integrated_circuit/server/queue_pop/do_work()
pull_data()
var/list/input_queue = get_pin_data(IC_INPUT, 1)
var/list/output_queue = list()
var/value = null
var/had_value = FALSE
if(islist(input_queue))
output_queue = input_queue.Copy()
if(output_queue.len)
value = output_queue[1]
output_queue.Cut(1, 2)
had_value = TRUE
set_pin_data(IC_OUTPUT, 1, value)
set_pin_data(IC_OUTPUT, 2, output_queue)
set_pin_data(IC_OUTPUT, 3, had_value)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/list_first_last
name = "list first/last"
desc = "Outputs the first and last values from a list."
extended_desc = "Useful for logs, queues, and message buffers."
icon_state = "addition"
complexity = 2
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"list" = IC_PINTYPE_LIST
)
outputs = list(
"first" = IC_PINTYPE_ANY,
"last" = IC_PINTYPE_ANY,
"length" = IC_PINTYPE_NUMBER,
"has values" = IC_PINTYPE_BOOLEAN
)
/obj/item/integrated_circuit/server/list_first_last/do_work()
pull_data()
var/list/input_list = get_pin_data(IC_INPUT, 1)
var/first = null
var/last = null
var/length = 0
var/has_values = FALSE
if(islist(input_list))
length = input_list.len
if(length)
first = input_list[1]
last = input_list[length]
has_values = TRUE
set_pin_data(IC_OUTPUT, 1, first)
set_pin_data(IC_OUTPUT, 2, last)
set_pin_data(IC_OUTPUT, 3, length)
set_pin_data(IC_OUTPUT, 4, has_values)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/database_counter
name = "database counter client"
desc = "Reads a numeric database value, changes it, and writes it back."
extended_desc = "Useful for scores, cycle counts, alarm counts, and quotas."
icon_state = "addition"
complexity = 5
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"server id" = IC_PINTYPE_STRING,
"key" = IC_PINTYPE_STRING,
"amount" = IC_PINTYPE_NUMBER
)
inputs_default = list(
"1" = "main",
"3" = 1
)
outputs = list(
"success" = IC_PINTYPE_BOOLEAN,
"new value" = IC_PINTYPE_NUMBER,
"status" = IC_PINTYPE_STRING
)
/obj/item/integrated_circuit/server/database_counter/do_work()
pull_data()
var/database_id = ic_normalize_database_id(get_pin_data(IC_INPUT, 1))
var/key = get_pin_data(IC_INPUT, 2)
var/amount = get_pin_data(IC_INPUT, 3)
var/success = FALSE
var/new_value = 0
var/status = "Server not found."
if(isnull(amount))
amount = 1
if(!istext(key) || !length(key))
status = "Counter requires a text key."
else
var/obj/item/integrated_circuit/server/database/S = GLOB.ic_database_servers[database_id]
if(istype(S))
var/current = S.storage[key]
if(isnum(current))
new_value = current
else
new_value = 0
new_value += amount
S.storage[key] = new_value
success = TRUE
status = "Counter updated."
set_pin_data(IC_OUTPUT, 1, success)
set_pin_data(IC_OUTPUT, 2, new_value)
set_pin_data(IC_OUTPUT, 3, status)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/database_compare_write
name = "database compare-write client"
desc = "Writes a database value only if the current value matches the expected value."
extended_desc = "Useful for locks, queues, safe counters, and avoiding accidental overwrites."
icon_state = "addition"
complexity = 6
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"server id" = IC_PINTYPE_STRING,
"key" = IC_PINTYPE_STRING,
"expected value" = IC_PINTYPE_ANY,
"new value" = IC_PINTYPE_ANY
)
inputs_default = list(
"1" = "main"
)
outputs = list(
"success" = IC_PINTYPE_BOOLEAN,
"current value" = IC_PINTYPE_ANY,
"status" = IC_PINTYPE_STRING
)
/obj/item/integrated_circuit/server/database_compare_write/do_work()
pull_data()
var/database_id = ic_normalize_database_id(get_pin_data(IC_INPUT, 1))
var/key = get_pin_data(IC_INPUT, 2)
var/expected_value = get_pin_data(IC_INPUT, 3)
var/new_value = get_pin_data(IC_INPUT, 4)
var/success = FALSE
var/current_value = null
var/status = "Server not found."
if(!istext(key) || !length(key))
status = "Compare-write requires a text key."
else
var/obj/item/integrated_circuit/server/database/S = GLOB.ic_database_servers[database_id]
if(istype(S))
current_value = S.storage[key]
if(current_value == expected_value)
S.storage[key] = new_value
current_value = new_value
success = TRUE
status = "Compare-write complete."
else
status = "Current value did not match expected value."
set_pin_data(IC_OUTPUT, 1, success)
set_pin_data(IC_OUTPUT, 2, current_value)
set_pin_data(IC_OUTPUT, 3, status)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/server/command_pad
name = "database command pad"
desc = "Parses typed text commands and sends them to a database server."
extended_desc = "Commands: READ key, WRITE key value, DELETE key, HAS key, KEYS, LEN, CLEAR, APPEND key value."
icon_state = "addition"
complexity = 6
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"server id" = IC_PINTYPE_STRING,
"command text" = IC_PINTYPE_STRING
)
inputs_default = list(
"1" = "research"
)
outputs = list(
"success" = IC_PINTYPE_BOOLEAN,
"response" = IC_PINTYPE_ANY,
"status" = IC_PINTYPE_STRING,
"parsed command" = IC_PINTYPE_STRING,
"parsed key" = IC_PINTYPE_STRING,
"parsed value" = IC_PINTYPE_STRING
)
activators = list(
"submit" = IC_PINTYPE_PULSE_IN,
"on accepted" = IC_PINTYPE_PULSE_OUT,
"on rejected" = IC_PINTYPE_PULSE_OUT
)
/obj/item/integrated_circuit/server/command_pad/do_work()
pull_data()
var/database_id = ic_normalize_database_id(get_pin_data(IC_INPUT, 1))
var/raw_text = get_pin_data(IC_INPUT, 2)
var/success = FALSE
var/response = null
var/status = "No command."
var/parsed_command = null
var/parsed_key = null
var/parsed_value = null
if(!istext(raw_text) || !length(raw_text))
set_pin_data(IC_OUTPUT, 1, FALSE)
set_pin_data(IC_OUTPUT, 2, null)
set_pin_data(IC_OUTPUT, 3, "Command pad requires text.")
set_pin_data(IC_OUTPUT, 4, null)
set_pin_data(IC_OUTPUT, 5, null)
set_pin_data(IC_OUTPUT, 6, null)
push_data()
activate_pin(3)
return
var/text = trim(raw_text)
var/first_space = findtext(text, " ")
if(first_space)
parsed_command = uppertext(trim(copytext(text, 1, first_space)))
var/remainder = trim(copytext(text, first_space + 1))
var/second_space = findtext(remainder, " ")
if(second_space)
parsed_key = trim(copytext(remainder, 1, second_space))
parsed_value = trim(copytext(remainder, second_space + 1))
else
parsed_key = remainder
else
parsed_command = uppertext(text)
switch(parsed_command)
if("READ")
parsed_command = "GET"
if("WRITE")
parsed_command = "SET"
if("REMOVE")
parsed_command = "DELETE"
if("ADD")
parsed_command = "APPEND"
var/obj/item/integrated_circuit/server/database/S = GLOB.ic_database_servers[database_id]
if(!istype(S))
status = "Server not found."
else
var/list/result = S.handle_request(parsed_command, parsed_key, parsed_value)
success = result["success"]
response = result["response"]
status = result["status"]
set_pin_data(IC_OUTPUT, 1, success)
set_pin_data(IC_OUTPUT, 2, response)
set_pin_data(IC_OUTPUT, 3, status)
set_pin_data(IC_OUTPUT, 4, parsed_command)
set_pin_data(IC_OUTPUT, 5, parsed_key)
set_pin_data(IC_OUTPUT, 6, parsed_value)
push_data()
if(success)
activate_pin(2)
else
activate_pin(3)
@@ -1,54 +1,86 @@
/*
* subtypes/smart.dm
* Smart utility circuits such as pathfinding and higher-level automation helpers.
*/
/obj/item/integrated_circuit/smart
category_text = "Smart"
spawn_flags = 0
/obj/item/integrated_circuit/smart/basic_pathfinder
name = "basic pathfinder"
desc = "This complex circuit is able to determine what direction a given target is."
extended_desc = "This circuit uses a miniturized integrated camera to determine where the target is. If the machine \
desc = "Outputs the direction of the selected target."
extended_desc = "This circuit uses a miniaturized integrated camera to determine where the target is. If the machine \
cannot see the target, it will not be able to calculate the correct direction."
icon_state = "numberpad"
complexity = 5
inputs = list("target" = IC_PINTYPE_REF,"ignore obstacles" = IC_PINTYPE_BOOLEAN)
outputs = list("dir" = IC_PINTYPE_DIR)
activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT)
inputs = list(
"target" = IC_PINTYPE_REF,
"ignore obstacles" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"dir" = IC_PINTYPE_DIR
)
activators = list(
"calculate dir" = IC_PINTYPE_PULSE_IN,
"on calculated" = IC_PINTYPE_PULSE_OUT,
"not calculated" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 400
/obj/item/integrated_circuit/smart/basic_pathfinder/do_work()
var/datum/integrated_io/I = inputs[1]
set_pin_data(IC_OUTPUT, 1, null)
if(!isweakref(I.data))
activate_pin(3)
return
var/atom/A = I.data.resolve()
var/datum/weakref/W = I.data
var/atom/A = W.resolve()
if(!A)
activate_pin(3)
return
if(!(A in view(get_turf(src))))
var/turf/start = get_turf(src)
var/turf/target_turf = get_turf(A)
if(!start || !target_turf || !(A in view(start)))
push_data()
activate_pin(3)
return // Can't see the target.
return
if(get_pin_data(IC_INPUT, 2))
set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A)))
set_pin_data(IC_OUTPUT, 1, get_dir(start, target_turf))
else
set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_step_towards2(get_turf(src),A)))
set_pin_data(IC_OUTPUT, 1, get_dir(start, get_step_towards2(start, A)))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/smart/coord_basic_pathfinder
name = "coordinate pathfinder"
desc = "This complex circuit is able to determine what direction a given target is."
extended_desc = "This circuit uses absolute coordinates to determine where the target is. If the machine \
cannot see the target, it will not be able to calculate the correct direction. \
desc = "Outputs the direction of the selected target."
extended_desc = "This circuit uses absolute coordinates to determine where the target is. \
This circuit will only work while inside an assembly."
icon_state = "numberpad"
complexity = 5
inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER,"ignore obstacles" = IC_PINTYPE_BOOLEAN)
outputs = list( "dir" = IC_PINTYPE_DIR,
"distance" = IC_PINTYPE_NUMBER
inputs = list(
"X" = IC_PINTYPE_NUMBER,
"Y" = IC_PINTYPE_NUMBER,
"ignore obstacles" = IC_PINTYPE_BOOLEAN
)
outputs = list(
"dir" = IC_PINTYPE_DIR,
"distance" = IC_PINTYPE_NUMBER
)
activators = list(
"calculate dir" = IC_PINTYPE_PULSE_IN,
"on calculated" = IC_PINTYPE_PULSE_OUT,
"not calculated" = IC_PINTYPE_PULSE_OUT
)
activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 400
@@ -56,18 +88,547 @@
if(!assembly)
activate_pin(3)
return
var/turf/T = get_turf(assembly)
var/target_x = clamp(get_pin_data(IC_INPUT, 1), 0, world.maxx)
var/target_y = clamp(get_pin_data(IC_INPUT, 2), 0, world.maxy)
var/turf/A = locate(target_x, target_y, T.z)
set_pin_data(IC_OUTPUT, 1, null)
if(!A||A==T)
if(!T)
activate_pin(3)
return
if(get_pin_data(IC_INPUT, 2))
set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A)))
var/target_x = round(clamp(get_pin_data(IC_INPUT, 1), 1, world.maxx))
var/target_y = round(clamp(get_pin_data(IC_INPUT, 2), 1, world.maxy))
var/turf/A = locate(target_x, target_y, T.z)
set_pin_data(IC_OUTPUT, 1, null)
if(!A || A == T)
push_data()
activate_pin(3)
return
if(get_pin_data(IC_INPUT, 3))
set_pin_data(IC_OUTPUT, 1, get_dir(T, A))
else
set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_step_towards2(get_turf(src),A)))
set_pin_data(IC_OUTPUT, 2, sqrt((A.x-T.x)*(A.x-T.x)+ (A.y-T.y)*(A.y-T.y)))
set_pin_data(IC_OUTPUT, 1, get_dir(T, get_step_towards2(T, A)))
set_pin_data(IC_OUTPUT, 2, sqrt((A.x - T.x) * (A.x - T.x) + (A.y - T.y) * (A.y - T.y)))
push_data()
activate_pin(2)
/obj/item/integrated_circuit/smart/relative_position
name = "relative position calculator"
desc = "Calculates relative coordinates between two supplied references."
extended_desc = "This does not scan, pathfind, or move anything. It only compares refs already supplied to the circuit and fails safely if either ref is invalid."
icon_state = "numberpad"
complexity = 5
inputs = list(
"origin ref" = IC_PINTYPE_REF,
"target ref" = IC_PINTYPE_REF
)
outputs = list(
"relative X" = IC_PINTYPE_NUMBER,
"relative Y" = IC_PINTYPE_NUMBER,
"relative Z" = IC_PINTYPE_NUMBER,
"distance" = IC_PINTYPE_NUMBER,
"valid" = IC_PINTYPE_BOOLEAN
)
activators = list(
"calculate" = IC_PINTYPE_PULSE_IN,
"on valid" = IC_PINTYPE_PULSE_OUT,
"on invalid" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 200
/obj/item/integrated_circuit/smart/relative_position/do_work()
var/origin = get_pin_data(IC_INPUT, 1)
var/target = get_pin_data(IC_INPUT, 2)
if(!istype(origin, /atom) || !istype(target, /atom))
set_relative_position_result(null, null, null, null, FALSE)
return
var/turf/origin_turf = get_turf(origin)
var/turf/target_turf = get_turf(target)
if(!origin_turf || !target_turf)
set_relative_position_result(null, null, null, null, FALSE)
return
var/dx = target_turf.x - origin_turf.x
var/dy = target_turf.y - origin_turf.y
var/dz = target_turf.z - origin_turf.z
var/distance = sqrt(dx * dx + dy * dy + dz * dz)
set_relative_position_result(dx, dy, dz, distance, TRUE)
/obj/item/integrated_circuit/smart/relative_position/proc/set_relative_position_result(dx, dy, dz, distance, valid)
set_pin_data(IC_OUTPUT, 1, dx)
set_pin_data(IC_OUTPUT, 2, dy)
set_pin_data(IC_OUTPUT, 3, dz)
set_pin_data(IC_OUTPUT, 4, distance)
set_pin_data(IC_OUTPUT, 5, valid)
push_data()
activate_pin(valid ? 2 : 3)
/obj/item/integrated_circuit/smart/coordinate_navigator
name = "coordinate navigator"
desc = "This complex circuit calculates a walkable path toward absolute coordinates."
extended_desc = "This circuit finds a walkable cardinal path toward absolute coordinates. It can route around walls, long barriers, and U-shaped obstructions within its search limit. It is intended for drones using a locomotion circuit."
icon_state = "numberpad"
complexity = 20
w_class = WEIGHT_CLASS_TINY
size = 3
power_draw_per_use = 800
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
var/list/current_path = list()
var/path_index = 1
var/last_target_x = null
var/last_target_y = null
var/max_search_nodes = 600
inputs = list(
"target X" = IC_PINTYPE_NUMBER,
"target Y" = IC_PINTYPE_NUMBER,
"recalculate if blocked" = IC_PINTYPE_BOOLEAN,
"max search nodes" = IC_PINTYPE_NUMBER
)
outputs = list(
"direction" = IC_PINTYPE_DIR,
"distance remaining" = IC_PINTYPE_NUMBER,
"path length" = IC_PINTYPE_NUMBER,
"status" = IC_PINTYPE_STRING
)
activators = list(
"calculate path" = IC_PINTYPE_PULSE_IN,
"step path" = IC_PINTYPE_PULSE_IN,
"clear path" = IC_PINTYPE_PULSE_IN,
"on step ready" = IC_PINTYPE_PULSE_OUT,
"on arrived" = IC_PINTYPE_PULSE_OUT,
"on failed" = IC_PINTYPE_PULSE_OUT
)
/obj/item/integrated_circuit/smart/coordinate_navigator/do_work(activator_id)
if(!assembly)
set_navigation_failure("Circuit is not installed in an assembly.")
return
var/active_pin = activator_id
if(istype(active_pin, /datum/integrated_io))
active_pin = activators.Find(active_pin)
if(istext(active_pin))
for(var/i = 1 to activators.len)
var/datum/integrated_io/A = activators[i]
if(A.name == active_pin)
active_pin = i
break
if(!isnum(active_pin))
active_pin = 1
switch(active_pin)
if(1)
calculate_path()
return
if(2)
step_path()
return
if(3)
clear_path()
return
set_navigation_failure("Unknown navigation activator: [activator_id].")
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/clear_path()
current_path = list()
path_index = 1
last_target_x = null
last_target_y = null
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, 0)
set_pin_data(IC_OUTPUT, 3, 0)
set_pin_data(IC_OUTPUT, 4, "Path cleared.")
push_data()
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/calculate_path()
var/turf/start = get_turf(assembly)
if(!start)
set_navigation_failure("No assembly turf.")
return
var/target_x = round(get_pin_data(IC_INPUT, 1))
var/target_y = round(get_pin_data(IC_INPUT, 2))
if(!isnum(target_x) || !isnum(target_y))
set_navigation_failure("Invalid target coordinates.")
return
target_x = clamp(target_x, 1, world.maxx)
target_y = clamp(target_y, 1, world.maxy)
var/turf/goal = locate(target_x, target_y, start.z)
if(!goal)
set_navigation_failure("Target turf does not exist.")
return
if(start == goal)
current_path = list(start)
path_index = 1
last_target_x = target_x
last_target_y = target_y
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, 0)
set_pin_data(IC_OUTPUT, 3, 1)
set_pin_data(IC_OUTPUT, 4, "Already at target.")
push_data()
activate_pin(5)
return
var/node_limit = round(get_pin_data(IC_INPUT, 4))
if(!isnum(node_limit) || node_limit <= 0)
node_limit = max_search_nodes
var/list/new_path = find_path_to_turf(start, goal, node_limit)
if(!new_path || !new_path.len)
set_navigation_failure("No path found within [node_limit] nodes.")
return
current_path = new_path
path_index = 2
last_target_x = target_x
last_target_y = target_y
output_next_step("Path calculated.")
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/step_path()
var/turf/start = get_turf(assembly)
if(!start)
set_navigation_failure("No assembly turf.")
return
var/target_x = round(get_pin_data(IC_INPUT, 1))
var/target_y = round(get_pin_data(IC_INPUT, 2))
if(!isnum(target_x) || !isnum(target_y))
set_navigation_failure("Invalid target coordinates.")
return
target_x = clamp(target_x, 1, world.maxx)
target_y = clamp(target_y, 1, world.maxy)
if(!current_path || !current_path.len)
calculate_path()
return
if(target_x != last_target_x || target_y != last_target_y)
calculate_path()
return
advance_path_index(start)
if(path_index > current_path.len)
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, 0)
set_pin_data(IC_OUTPUT, 3, current_path.len)
set_pin_data(IC_OUTPUT, 4, "Arrived.")
push_data()
activate_pin(5)
return
var/turf/next_turf = current_path[path_index]
if(!next_turf || turf_is_blocked_for_navigation(next_turf))
if(get_pin_data(IC_INPUT, 3))
calculate_path()
return
set_navigation_failure("Next path turf is blocked.")
return
output_next_step("Step ready.")
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/output_next_step(status)
var/turf/start = get_turf(assembly)
if(!start)
set_navigation_failure("No assembly turf.")
return
advance_path_index(start)
if(path_index > current_path.len)
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, 0)
set_pin_data(IC_OUTPUT, 3, current_path.len)
set_pin_data(IC_OUTPUT, 4, "Arrived.")
push_data()
activate_pin(5)
return
var/turf/next_turf = current_path[path_index]
if(!next_turf)
set_navigation_failure("Path index points to no turf.")
return
if(get_dist(start, next_turf) > 1)
if(get_pin_data(IC_INPUT, 3))
calculate_path()
return
set_navigation_failure("Path is stale.")
return
if(turf_is_blocked_for_navigation(next_turf))
if(get_pin_data(IC_INPUT, 3))
calculate_path()
return
set_navigation_failure("Next path turf is blocked.")
return
var/dir_to_next = get_dir(start, next_turf)
var/distance_remaining = max(0, current_path.len - path_index + 1)
set_pin_data(IC_OUTPUT, 1, dir_to_next)
set_pin_data(IC_OUTPUT, 2, distance_remaining)
set_pin_data(IC_OUTPUT, 3, current_path.len)
set_pin_data(IC_OUTPUT, 4, status)
push_data()
activate_pin(4)
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/advance_path_index(turf/current_turf)
while(path_index <= current_path.len && current_path[path_index] == current_turf)
path_index++
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/set_navigation_failure(status)
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, 0)
set_pin_data(IC_OUTPUT, 3, current_path ? current_path.len : 0)
set_pin_data(IC_OUTPUT, 4, status)
push_data()
activate_pin(6)
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/find_path_to_turf(turf/start, turf/goal, node_limit)
if(!start || !goal)
return null
if(turf_is_blocked_for_navigation(goal))
return null
var/list/queue = list(start)
var/list/visited = list()
var/list/parents = list()
visited[start] = TRUE
var/head = 1
var/nodes_checked = 0
var/found_goal = FALSE
while(head <= queue.len)
var/turf/current = queue[head]
head++
nodes_checked++
if(nodes_checked > node_limit)
break
if(current == goal)
found_goal = TRUE
break
for(var/check_dir in get_prioritized_dirs(current, goal))
var/turf/next_turf = get_step(current, check_dir)
if(!next_turf)
continue
if(visited[next_turf])
continue
if(turf_is_blocked_for_navigation(next_turf))
continue
visited[next_turf] = TRUE
parents[next_turf] = current
queue += next_turf
if(!found_goal)
return null
var/list/reversed_path = list()
var/turf/walk = goal
while(walk)
reversed_path += walk
if(walk == start)
break
walk = parents[walk]
if(!walk)
return null
var/list/final_path = list()
for(var/i = reversed_path.len, i >= 1, i--)
final_path += reversed_path[i]
return final_path
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/get_prioritized_dirs(turf/current, turf/goal)
var/list/dirs = list()
if(!current || !goal)
return GLOB.cardinals
var/dx = goal.x - current.x
var/dy = goal.y - current.y
if(abs(dx) >= abs(dy))
if(dx > 0)
dirs += EAST
else if(dx < 0)
dirs += WEST
if(dy > 0)
dirs += NORTH
else if(dy < 0)
dirs += SOUTH
else
if(dy > 0)
dirs += NORTH
else if(dy < 0)
dirs += SOUTH
if(dx > 0)
dirs += EAST
else if(dx < 0)
dirs += WEST
for(var/D in GLOB.cardinals)
if(!(D in dirs))
dirs += D
return dirs
/obj/item/integrated_circuit/smart/coordinate_navigator/proc/turf_is_blocked_for_navigation(turf/T)
if(!T)
return TRUE
if(T.density)
return TRUE
for(var/atom/movable/AM in T)
if(AM == assembly)
continue
if(AM.density)
return TRUE
return FALSE
/obj/item/integrated_circuit/smart/coordinate_command_parser
name = "coordinate command parser"
desc = "Parses simple coordinate commands into X and Y number outputs."
extended_desc = "This circuit parses text commands such as 'GOTO 25 56', '25 56', 'X 25 Y 56', or 'X=25 Y=56'. It outputs target X and Y coordinates for use with a coordinate navigator."
icon_state = "numberpad"
complexity = 6
w_class = WEIGHT_CLASS_TINY
size = 1
power_draw_per_use = 200
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"command" = IC_PINTYPE_STRING
)
outputs = list(
"target X" = IC_PINTYPE_NUMBER,
"target Y" = IC_PINTYPE_NUMBER,
"valid" = IC_PINTYPE_BOOLEAN,
"status" = IC_PINTYPE_STRING
)
activators = list(
"parse command" = IC_PINTYPE_PULSE_IN,
"on parsed" = IC_PINTYPE_PULSE_OUT,
"on failed" = IC_PINTYPE_PULSE_OUT
)
/obj/item/integrated_circuit/smart/coordinate_command_parser/do_work()
pull_data()
var/command = get_pin_data(IC_INPUT, 1)
if(!istext(command) || !length(command))
set_parse_result(null, null, FALSE, "No command.")
return
var/list/numbers = extract_numbers_from_command(command)
if(!numbers || numbers.len < 2)
set_parse_result(null, null, FALSE, "Command needs two numbers.")
return
var/target_x = round(numbers[1])
var/target_y = round(numbers[2])
target_x = clamp(target_x, 1, world.maxx)
target_y = clamp(target_y, 1, world.maxy)
set_parse_result(target_x, target_y, TRUE, "Parsed coordinates: [target_x], [target_y].")
/obj/item/integrated_circuit/smart/coordinate_command_parser/proc/set_parse_result(target_x, target_y, valid, status)
set_pin_data(IC_OUTPUT, 1, target_x)
set_pin_data(IC_OUTPUT, 2, target_y)
set_pin_data(IC_OUTPUT, 3, valid)
set_pin_data(IC_OUTPUT, 4, status)
push_data()
if(valid)
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/smart/coordinate_command_parser/proc/extract_numbers_from_command(command)
var/list/numbers = list()
var/current_number = ""
var/reading_number = FALSE
var/allow_negative = TRUE
for(var/i = 1 to length(command))
var/char = copytext(command, i, i + 1)
if(char >= "0" && char <= "9")
current_number += char
reading_number = TRUE
allow_negative = FALSE
continue
if(char == "-" && allow_negative)
current_number += char
reading_number = TRUE
allow_negative = FALSE
continue
if(reading_number)
var/parsed = text2num(current_number)
if(!isnull(parsed))
numbers += parsed
current_number = ""
reading_number = FALSE
allow_negative = TRUE
if(reading_number)
var/parsed = text2num(current_number)
if(!isnull(parsed))
numbers += parsed
return numbers
@@ -1,6 +1,11 @@
/*
* subtypes/time.dm
* Timing circuits: delays, clocks, pulses, cooldown-style behavior, and scheduled activation.
*/
/obj/item/integrated_circuit/time
name = "time circuit"
desc = "Now you can build your own clock!"
desc = "Outputs the current local time when pulsed."
complexity = 2
inputs = list()
outputs = list()
@@ -14,7 +19,7 @@
var/delay = 2 SECONDS
activators = list("incoming"= IC_PINTYPE_PULSE_IN,"outgoing" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 20
power_draw_per_use = 30
/obj/item/integrated_circuit/time/delay/do_work()
addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/integrated_circuit, activate_pin), 2), delay)
@@ -26,6 +31,7 @@
icon_state = "delay-50"
delay = 5 SECONDS
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 20
/obj/item/integrated_circuit/time/delay/one_sec
name = "one-sec delay circuit"
@@ -34,30 +40,37 @@
icon_state = "delay-10"
delay = 1 SECOND
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 40
/obj/item/integrated_circuit/time/delay/half_sec
name = "half-sec delay circuit"
desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
This circuit is set to send a pulse after a delay of half a second."
icon_state = "delay-5"
complexity = 4
delay = 5
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 60
/obj/item/integrated_circuit/time/delay/tenth_sec
name = "tenth-sec delay circuit"
desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
This circuit is set to send a pulse after a delay of 1/10th of a second."
icon_state = "delay-1"
complexity = 6
delay = 1
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 100
/obj/item/integrated_circuit/time/delay/custom
name = "custom delay circuit"
desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
This circuit's delay can be customized, between 1/10th of a second to one hour. The delay is updated upon receiving a pulse."
icon_state = "delay"
complexity = 6
inputs = list("delay time" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 100
/obj/item/integrated_circuit/time/delay/custom/on_data_written()
delay = between(1, get_pin_data(IC_INPUT, 1), 1 HOUR)
@@ -65,10 +78,10 @@
/obj/item/integrated_circuit/time/ticker
name = "ten second ticker"
desc = "This circuit sends an automatic pulse every ten seconds."
desc = "Sends an automatic pulse every ten seconds."
icon_state = "tick-m"
complexity = 8
var/seconds_to_pulse = 10 SECONDS
var/seconds_to_pulse = 10
var/ticks_completed = 0
var/is_running = FALSE
inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN)
@@ -85,33 +98,51 @@
if(do_tick && !is_running)
is_running = TRUE
START_PROCESSING(SSelectronics, src)
else if(is_running)
else if(!do_tick && is_running)
is_running = FALSE
STOP_PROCESSING(SSelectronics, src)
ticks_completed = 0
/obj/item/integrated_circuit/time/ticker/process()
var/process_ticks = SSelectronics.wait
var/pulse_ticks = SecondsToTicks(seconds_to_pulse)
ticks_completed += process_ticks
if(ticks_completed >= SecondsToTicks(seconds_to_pulse))
if(SecondsToTicks(seconds_to_pulse) >= process_ticks)
ticks_completed -= SecondsToTicks(seconds_to_pulse)
if(ticks_completed >= pulse_ticks)
if(pulse_ticks >= process_ticks)
ticks_completed -= pulse_ticks
else
ticks_completed = 0
if(!check_power())
power_fail()
return
activate_pin(1)
/obj/item/integrated_circuit/time/ticker/power_fail()
is_running = FALSE
STOP_PROCESSING(SSelectronics, src)
ticks_completed = 0
/obj/item/integrated_circuit/time/ticker/rapid
name = "one second ticker"
desc = "Sends a high-speed automatic pulse every second."
icon_state = "tick-f"
complexity = 18
seconds_to_pulse = 1
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 240
/obj/item/integrated_circuit/time/ticker/fast
name = "two second ticker"
desc = "This advanced circuit sends an automatic pulse every two seconds."
desc = "Sends an automatic pulse every two seconds."
icon_state = "tick-f"
complexity = 12
seconds_to_pulse = 2
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 80
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 120
/obj/item/integrated_circuit/time/ticker/slow
name = "thirty second ticker"
desc = "This simple circuit sends an automatic pulse every thirty seconds."
desc = "Sends an automatic pulse every thirty seconds."
icon_state = "tick-s"
complexity = 4
seconds_to_pulse = 30
@@ -120,7 +151,7 @@
/obj/item/integrated_circuit/time/ticker/very_slow
name = "five minute ticker"
desc = "This simple circuit sends an automatic pulse every five minutes (three hundred seconds)."
desc = "Sends an automatic pulse every five minutes."
icon_state = "tick-s"
complexity = 4
seconds_to_pulse = 300
@@ -129,12 +160,12 @@
/obj/item/integrated_circuit/time/ticker/custom
name = "custom ticker"
desc = "This advanced circuit sends an automatic pulse with a configurable interval between 2 and 600 seconds. Default: 60 seconds."
desc = "Sends automatic pulses at a configurable interval between 2 and 600 seconds. Default: 60 seconds."
icon_state = "tick-f"
complexity = 15
seconds_to_pulse = 60
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 80
power_draw_per_use = 120
inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN, "ticker time" = IC_PINTYPE_NUMBER)
/obj/item/integrated_circuit/time/ticker/custom/on_data_written()
@@ -143,7 +174,7 @@
/obj/item/integrated_circuit/time/clock
name = "integrated clock"
desc = "Tells you what the local time is, specific to your station, planet, or facility."
desc = "Outputs the local station, planet, or facility time."
icon_state = "clock"
inputs = list()
outputs = list(
@@ -164,3 +195,137 @@
push_data()
activate_pin(2)
/obj/item/integrated_circuit/timed/signal_burst_ticker
name = "signal burst ticker"
desc = "A ticker that starts when pulsed, ticks a fixed number of times, then stops."
extended_desc = "Useful when you want a signaler, scanner, button, or other pulse source to temporarily enable a repeating circuit chain. Pulse count is clamped from 1 to 20, and interval is clamped from 1 to 60 seconds."
icon_state = "clock"
complexity = 10
spawn_flags = IC_SPAWN_RESEARCH
category_text = "Time"
power_draw_per_use = 160
inputs = list(
"pulse count" = IC_PINTYPE_NUMBER,
"interval seconds" = IC_PINTYPE_NUMBER,
"enabled" = IC_PINTYPE_BOOLEAN
)
inputs_default = list(
"1" = 4,
"2" = 2,
"3" = TRUE
)
outputs = list(
"current pulse" = IC_PINTYPE_NUMBER,
"running" = IC_PINTYPE_BOOLEAN
)
activators = list(
"start" = IC_PINTYPE_PULSE_IN,
"stop" = IC_PINTYPE_PULSE_IN,
"on pulse" = IC_PINTYPE_PULSE_OUT,
"on finished" = IC_PINTYPE_PULSE_OUT
)
var/running = FALSE
var/current_tick = 0
var/max_ticks = 4
var/delay_time = 2 SECONDS
var/burst_id = 0
/obj/item/integrated_circuit/timed/signal_burst_ticker/do_work(activator_id)
var/active_pin = activator_id
if(istype(active_pin, /datum/integrated_io))
active_pin = activators.Find(active_pin)
if(!isnum(active_pin))
active_pin = 1
if(active_pin == 2)
stop_burst(FALSE)
return
if(running)
return
pull_data()
if(!get_pin_data(IC_INPUT, 3))
stop_burst(FALSE)
return
var/input_ticks = get_pin_data(IC_INPUT, 1)
var/input_delay = get_pin_data(IC_INPUT, 2)
if(isnum(input_delay))
input_delay = between(1, input_delay, 60)
else
input_delay = 2
if(isnum(input_ticks))
input_ticks = between(1, round(input_ticks), 20)
else
input_ticks = 4
delay_time = input_delay SECONDS
max_ticks = input_ticks
current_tick = 0
running = TRUE
burst_id++
set_pin_data(IC_OUTPUT, 1, current_tick)
set_pin_data(IC_OUTPUT, 2, running)
push_data()
// Timer IDs make old callbacks harmless after stop/restart or deletion.
addtimer(CALLBACK(src, PROC_REF(process_tick), burst_id), delay_time)
/obj/item/integrated_circuit/timed/signal_burst_ticker/proc/process_tick(expected_burst_id)
if(!running || expected_burst_id != burst_id)
return
if(!check_power())
power_fail()
return
current_tick++
set_pin_data(IC_OUTPUT, 1, current_tick)
set_pin_data(IC_OUTPUT, 2, running)
push_data()
activate_pin(3)
if(current_tick >= max_ticks)
stop_burst(TRUE)
return
addtimer(CALLBACK(src, PROC_REF(process_tick), expected_burst_id), delay_time)
/obj/item/integrated_circuit/timed/signal_burst_ticker/proc/stop_burst(finished)
if(!running && !finished)
set_pin_data(IC_OUTPUT, 2, FALSE)
push_data()
return
running = FALSE
burst_id++
set_pin_data(IC_OUTPUT, 2, running)
push_data()
if(finished)
activate_pin(4)
/obj/item/integrated_circuit/timed/signal_burst_ticker/Destroy()
running = FALSE
burst_id++
return ..()
/obj/item/integrated_circuit/timed/signal_burst_ticker/power_fail()
stop_burst(FALSE)
@@ -1,3 +1,8 @@
/*
* subtypes/trig.dm
* Trigonometry and coordinate math circuits.
*/
//These circuits do not-so-simple math.
/obj/item/integrated_circuit/trig
complexity = 1
@@ -16,7 +21,7 @@
"compute" = IC_PINTYPE_PULSE_IN,
"on computed" = IC_PINTYPE_PULSE_OUT
)
category_text = "Trig"
category_text = "MATH - Trigonometry"
extended_desc = "Input and output are in degrees."
power_draw_per_use = 10 // Still cheap math.
@@ -24,7 +29,7 @@
/obj/item/integrated_circuit/trig/sine
name = "sin circuit"
desc = "Has nothing to do with evil, unless you consider trigonometry to be evil. Outputs the sine of A."
desc = "Outputs the sine of A."
icon_state = "sine"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -64,7 +69,7 @@
/obj/item/integrated_circuit/trig/tangent
name = "tan circuit"
desc = "Outputs the tangent of A. Guaranteed to not go on a tangent about its existance."
desc = "Outputs the tangent of A."
icon_state = "tangent"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -105,7 +110,7 @@
/obj/item/integrated_circuit/trig/secant
name = "sec circuit"
desc = "Outputs the secant of A. Has nothing to do with the security department."
desc = "Outputs the secant of A."
icon_state = "secant"
inputs = list("A" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -1,3 +1,8 @@
/*
* ~defines/~defines.dm
* Undefines temporary macros after integrated electronics files finish compiling.
*/
#undef IC_INPUT
#undef IC_OUTPUT
#undef IC_ACTIVATOR
+25 -3
View File
@@ -17,7 +17,6 @@
name = "impassable substrate"
color = "#222222"
/// This is a global list so we can share the same list with all mineral turfs; it's the same for all of them anyways.
GLOBAL_LIST_INIT(mineral_can_smooth_with, list(
/turf/simulated/mineral,
/turf/simulated/wall,
@@ -395,6 +394,31 @@ GLOBAL_LIST_INIT(mineral_can_smooth_with, list(
new /obj/structure/sculpting_block(src)
GetDrilled(1)
/turf/simulated/mineral/proc/ic_precision_excavate(var/amount)
if(!finds?.len)
return "No xenoarch find in target rock."
var/datum/find/F = finds[1]
if(!F)
return "Invalid xenoarch find."
if(amount <= 0)
return "Invalid excavation amount."
if(excavation_level + amount > F.excavation_required)
return "Unsafe: excavation would strike past the find."
if(excavation_level + amount > F.excavation_required - F.clearance_range)
if(round(excavation_level + amount) == F.excavation_required)
excavation_level += amount
excavate_find(100, F)
return "Perfect extraction complete."
return "Unsafe: excavation would breach clearance zone."
excavation_level += amount
return "Excavation advanced."
/turf/simulated/mineral/proc/get_geodata()
if(!geologic_data)
geologic_data = new /datum/geosample(src)
@@ -742,8 +766,6 @@ GLOBAL_LIST_INIT(mineral_can_smooth_with, list(
roof_type = null
turf_flags = TURF_FLAG_BACKGROUND
/// Same as the other, this is a global so we don't have a lot of pointless lists floating around.
/// Basalt is explicitly omitted so ash will spill onto basalt turfs.
GLOBAL_LIST_INIT(asteroid_floor_smooth, list(
/turf/simulated/floor/exoplanet/asteroid/ash,
/turf/simulated/mineral,
@@ -56,13 +56,20 @@
/datum/design/item/integrated_electronics/custom_circuit_printer
name = "Portable Integrated Circuit Printer"
desc = "A portable(ish) printer for modular machines."
req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 4, TECH_DATA = 5)
req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 3)
materials = list(MATERIAL_STEEL = 10000)
build_path = /obj/item/integrated_circuit_printer
/datum/design/item/integrated_electronics/custom_circuit_printer_upgrade
name = "Integrated Circuit Printer Upgrade - Advanced Designs"
desc = "Allows the integrated circuit printer to create advanced circuits."
req_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4)
req_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 5)
materials = list(MATERIAL_STEEL = 2000)
build_path = /obj/item/disk/integrated_circuit/upgrade/advanced
/datum/design/item/integrated_electronics/custom_circuit_printer_upgrade/clone
name = "Integrated Circuit Printer Upgrade Disk - Circuit Cloner"
desc = "An upgrade disk that allows the integrated circuit printer to duplicate assemblies."
req_tech = list(TECH_ENGINEERING = 5, TECH_DATA = 6)
materials = list(DEFAULT_WALL_MATERIAL = 3000)
build_path = /obj/item/disk/integrated_circuit/upgrade/clone
@@ -0,0 +1,19 @@
author: mineymonkey
delete-after: True
changes:
- rscadd: "Added electronic radio headsets, wearable radios with internal integrated circuit assemblies, encryption key support, and private text-to-speech output."
- rscadd: "Added integrated circuit printer cloning and blueprint import/export support for assemblies and supported hosted assemblies."
- rscadd: "Added new integrated circuit tools for data storage, database commands, radio commands, references, coordinates, access checks, card/item slots, power cells, and item trays."
- rscadd: "Added expanded math circuits, including algebra, geometry, list math, statistics, vectors, matrices, calculus, and vector calculus."
- rscadd: "Added practical automation circuits, including edge detection, cooldown limiting, pulse counting, pulse routing, state display, list utilities, coordinate navigation, and command parsing."
- rscadd: "Added xenoarch precision excavator circuit support with anomaly depth, clearance, excavation, and target direction outputs."
- rscadd: "Added additional manipulation circuits and a low-complexity debug circuit for organizing circuit layouts."
- rscadd: "Made the circuit cloner upgrade disk printable in the protolathe."
- qol: "Reorganized integrated circuit printer categories, math subgroups, logic/comparison groups, circuit descriptions, and circuit box descriptions."
- qol: "Improved cloning preservation for circuit names, pin data, constant memory values, built-in circuit links, hosted assembly state, and electronic radio headset internals."
- qol: "Improved scanner, server, command pad, reference, coordinate, list, ticker, and printer workflows with clearer outputs, safer invalid-input handling, and bounded pulse behavior."
- bugfix: "Fixed imported electronic radio headset blueprints failing to recreate the headset wrapper item or correctly bind the internal assembly."
- bugfix: "Fixed large integrated circuit blueprint imports losing or corrupting JSON characters during chunked transfer."
- bugfix: "Fixed several integrated circuit edge cases involving invalid refs, empty or null lists, injector targets, radio command receiver declarations, and abstract math circuits appearing in printers."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+413 -15
View File
@@ -1,19 +1,40 @@
import {
Button,
Input,
LabeledList,
ProgressBar,
Section,
TextArea,
} from 'tgui-core/components';
import type { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
import { useBackend, useLocalState } from '../backend';
import { Window } from '../layouts';
const BLUEPRINT_IMPORT_CHUNK_DELAY = 250;
const sleep = (duration: number) =>
new Promise((resolve) => setTimeout(resolve, duration));
export type PrinterData = {
metal: number;
metal_max: number;
phoron: number;
phoron_max: number;
upgraded: BooleanLike;
can_clone: BooleanLike;
assembly_to_clone: string;
clone_cost: number;
clone_phoron_cost: number;
clone_print_time: number;
currently_printing: BooleanLike;
has_clone_loaded: BooleanLike;
has_live_clone_scan: BooleanLike;
has_clone_blueprint: BooleanLike;
has_clone_blueprint_export: BooleanLike;
clone_blueprint_export_visible: BooleanLike;
clone_blueprint_export: string;
blueprint_chunk_limit: number;
blueprint_buffer_limit: number;
circuits: Circuit[];
categories: string[];
};
@@ -26,11 +47,159 @@ type Circuit = {
category: string;
};
type CircuitSubgroup = {
key: string;
group: string;
subgroup: string;
circuits: Circuit[];
};
type CircuitGroup = {
group: string;
subgroups: CircuitSubgroup[];
};
const splitCircuitCategory = (category: string) => {
const separator = ' - ';
const subgroupedGroups = ['LOGIC', 'MATH'];
const separatorIndex = category.indexOf(separator);
const group = category.slice(0, separatorIndex);
if (separatorIndex < 0 || !subgroupedGroups.includes(group)) {
return {
group: category,
subgroup: category,
};
}
return {
group,
subgroup: category.slice(separatorIndex + separator.length),
};
};
export const CircuitPrinter = (props) => {
const { act, data } = useBackend<PrinterData>();
const [searchTerm, setSearchTerm] = useLocalState<string>('searchTerm', '');
const [blueprintText, setBlueprintText] = useLocalState<string>(
'blueprintText',
'',
);
const [showBlueprintImport, setShowBlueprintImport] = useLocalState<boolean>(
'showBlueprintImport',
false,
);
const [isImportingBlueprint, setIsImportingBlueprint] =
useLocalState<boolean>('isImportingBlueprint', false);
const [collapsedCategories, setCollapsedCategories] = useLocalState<
Record<string, boolean>
>('collapsedCategories', {});
const normalizedSearchTerm = searchTerm.toLowerCase();
const categories = [...data.categories].sort();
const categoryGroups = categories.reduce<CircuitGroup[]>(
(groups, category) => {
const { group, subgroup } = splitCircuitCategory(category);
const groupMatches = group.toLowerCase().includes(normalizedSearchTerm);
const subgroupMatches = subgroup
.toLowerCase()
.includes(normalizedSearchTerm);
const categoryMatches = category
.toLowerCase()
.includes(normalizedSearchTerm);
const circuits = data.circuits.filter((circuit) => {
if (circuit.category !== category) {
return false;
}
if (
!normalizedSearchTerm ||
groupMatches ||
subgroupMatches ||
categoryMatches
) {
return true;
}
return !!circuit.name?.toLowerCase().includes(normalizedSearchTerm);
});
if (!circuits.length) {
return groups;
}
let circuitGroup = groups.find((entry) => entry.group === group);
if (!circuitGroup) {
circuitGroup = {
group,
subgroups: [],
};
groups.push(circuitGroup);
}
circuitGroup.subgroups.push({
key: category,
group,
subgroup,
circuits,
});
return groups;
},
[],
);
categoryGroups.sort((a, b) => a.group.localeCompare(b.group));
categoryGroups.forEach((group) => {
group.subgroups.sort((a, b) => a.subgroup.localeCompare(b.subgroup));
});
const importPastedBlueprint = async () => {
const importText = blueprintText;
if (!importText || isImportingBlueprint) {
return;
}
setIsImportingBlueprint(true);
let offset = 0;
const chunkLimit = Math.max(1, data.blueprint_chunk_limit || 1000);
const bufferLimit = data.blueprint_buffer_limit || 500000;
if (importText.length > bufferLimit) {
act('reject_oversized_import_tgui', {
length: importText.length,
});
setIsImportingBlueprint(false);
return;
}
try {
await act('begin_import_buffer');
while (offset < importText.length) {
const chunk = importText.slice(offset, offset + chunkLimit);
await act('append_import_chunk_tgui', {
chunk: encodeURIComponent(chunk),
});
offset += chunk.length;
await sleep(BLUEPRINT_IMPORT_CHUNK_DELAY);
}
await act('finish_import_buffer_tgui');
} finally {
setIsImportingBlueprint(false);
}
};
return (
<Window>
<Window width={720} height={760}>
<Window.Content scrollable>
<Section title="Status">
<LabeledList>
@@ -48,27 +217,256 @@ export const CircuitPrinter = (props) => {
{data.metal} sheets
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Phoron">
<ProgressBar
ranges={{
good: [data.phoron_max * 0.75, data.phoron_max],
average: [data.phoron_max * 0.3, data.phoron_max * 0.75],
bad: [0, data.phoron_max * 0.3],
}}
value={data.phoron}
minValue={0}
maxValue={data.phoron_max}
>
{data.phoron} sheets
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Upgraded">
{data.upgraded ? 'Yes' : 'No'}
</LabeledList.Item>
<LabeledList.Item label="Cloner">
{data.can_clone ? 'Installed' : 'Not Installed'}
</LabeledList.Item>
<LabeledList.Item label="Printing">
{data.currently_printing ? 'Yes' : 'No'}
</LabeledList.Item>
</LabeledList>
</Section>
{data.categories.sort().map((category) => (
<Section title={category} key={category}>
{data.circuits.map((circuit) =>
circuit.category === category ? (
{!!data.can_clone && (
<Section title="Circuit Cloning">
<LabeledList>
<LabeledList.Item label="Loaded Blueprint">
{data.assembly_to_clone || 'None'}
</LabeledList.Item>
<LabeledList.Item label="Steel Cost">
{data.assembly_to_clone && data.assembly_to_clone !== 'None'
? `${data.clone_cost} sheets`
: 'N/A'}
</LabeledList.Item>
<LabeledList.Item label="Phoron Cost">
{data.assembly_to_clone && data.assembly_to_clone !== 'None'
? `${data.clone_phoron_cost} sheets`
: 'N/A'}
</LabeledList.Item>
<LabeledList.Item label="Print Time">
{data.assembly_to_clone && data.assembly_to_clone !== 'None'
? `${data.clone_print_time} seconds`
: 'N/A'}
</LabeledList.Item>
</LabeledList>
<Button
content="Clone Assembly"
icon="copy"
disabled={
!data.has_clone_loaded ||
data.currently_printing ||
data.metal < data.clone_cost ||
data.phoron < data.clone_phoron_cost
}
onClick={() => act('clone')}
/>
<Button
content="Clear Scan"
icon="times"
disabled={!data.has_live_clone_scan || data.currently_printing}
onClick={() => act('clear_clone')}
/>
<Button
content={
showBlueprintImport ? 'Cancel Import' : 'Import Blueprint'
}
icon="file-import"
disabled={data.currently_printing || isImportingBlueprint}
onClick={() => {
setShowBlueprintImport(!showBlueprintImport);
act('hide_clone_blueprint_export');
}}
/>
<Button
content={
data.clone_blueprint_export_visible
? 'Hide Export'
: 'Export Blueprint'
}
icon="file-export"
disabled={
!data.has_clone_blueprint_export ||
data.currently_printing ||
isImportingBlueprint
}
onClick={() => {
setShowBlueprintImport(false);
act(
data.clone_blueprint_export_visible
? 'hide_clone_blueprint_export'
: 'show_clone_blueprint_export',
);
}}
/>
<Button
content="Clear Imported Blueprint"
icon="trash"
disabled={!data.has_clone_blueprint || data.currently_printing}
onClick={() => act('clear_imported_clone')}
/>
{showBlueprintImport && (
<>
<TextArea
fluid
height="240px"
mt={0.5}
placeholder="Paste blueprint JSON"
value={blueprintText}
onChange={setBlueprintText}
style={{
fontFamily: 'monospace',
}}
/>
<Button
key={circuit.name}
onClick={() => act('build', { build: circuit.path })}
>
{circuit.name}
</Button>
) : (
''
),
mt={0.5}
content={
isImportingBlueprint
? 'Importing Blueprint'
: 'Import Pasted Blueprint'
}
icon="file-import"
disabled={
!blueprintText ||
data.currently_printing ||
isImportingBlueprint
}
onClick={importPastedBlueprint}
/>
</>
)}
{!!data.clone_blueprint_export_visible && (
<TextArea
fluid
height="240px"
mt={0.5}
value={data.clone_blueprint_export}
style={{
fontFamily: 'monospace',
}}
/>
)}
</Section>
))}
)}
<Section
title="Circuits"
buttons={
<Input
autoFocus
autoSelect
placeholder="Search categories or circuits"
maxLength={512}
onChange={setSearchTerm}
value={searchTerm}
/>
}
>
{categoryGroups.map(({ group, subgroups }) => {
const circuitCount = subgroups.reduce(
(count, subgroup) => count + subgroup.circuits.length,
0,
);
const hasSubgroups = ['LOGIC', 'MATH'].includes(group);
const groupCollapsed = !!collapsedCategories[group];
return (
<Section
title={`${group} (${circuitCount})`}
key={group}
buttons={
<Button
icon={groupCollapsed ? 'chevron-right' : 'chevron-down'}
tooltip={
groupCollapsed ? 'Expand category' : 'Collapse category'
}
onClick={() =>
setCollapsedCategories({
...collapsedCategories,
[group]: !collapsedCategories[group],
})
}
/>
}
>
{!groupCollapsed &&
!hasSubgroups &&
subgroups.flatMap(({ circuits }) =>
circuits.map((circuit) => (
<Button
key={circuit.path}
content={circuit.name}
tooltip={circuit.desc}
disabled={data.currently_printing}
onClick={() => act('build', { build: circuit.path })}
/>
)),
)}
{!groupCollapsed &&
hasSubgroups &&
subgroups.map(({ key, subgroup, circuits }) => {
const subgroupCollapsed = !!collapsedCategories[key];
return (
<Section
title={`${subgroup} (${circuits.length})`}
key={key}
buttons={
<Button
icon={
subgroupCollapsed
? 'chevron-right'
: 'chevron-down'
}
tooltip={
subgroupCollapsed
? 'Expand category'
: 'Collapse category'
}
onClick={() =>
setCollapsedCategories({
...collapsedCategories,
[key]: !collapsedCategories[key],
})
}
/>
}
>
{!subgroupCollapsed &&
circuits.map((circuit) => (
<Button
key={circuit.path}
content={circuit.name}
tooltip={circuit.desc}
disabled={data.currently_printing}
onClick={() =>
act('build', { build: circuit.path })
}
/>
))}
</Section>
);
})}
</Section>
);
})}
</Section>
</Window.Content>
</Window>
);