From d8847ea1673b36ad3e5c1778ae8cc65ffbb6fb9a Mon Sep 17 00:00:00 2001 From: arsserpentarium <32496644+arsserpentarium@users.noreply.github.com> Date: Tue, 14 Nov 2017 12:05:22 +0300 Subject: [PATCH] [READY]integrated circuitry port+upgrade. --- code/__DEFINES/integrated_electronics.dm | 41 + code/__DEFINES/logging.dm | 1 + code/__DEFINES/misc.dm | 3 + code/__DEFINES/subsystems.dm | 1 + code/__HELPERS/type2type.dm | 20 + code/__HELPERS/unsorted.dm | 41 + .../configuration/entries/game_options.dm | 2 + .../subsystem/processing/circuit.dm | 70 ++ code/datums/EPv2.dm | 113 +++ code/datums/weakrefs.dm | 6 +- code/game/machinery/camera/motion.dm | 3 +- code/game/machinery/exonet_node.dm | 106 +++ .../circuitboards/machine_circuitboards.dm | 13 + code/game/objects/items/devices/multitool.dm | 170 ++++ code/modules/admin/admin_investigate.dm | 3 +- .../integrated_electronics/core/analyzer.dm | 103 +++ .../integrated_electronics/core/assemblies.dm | 342 ++++++++ .../integrated_electronics/core/debugger.dm | 64 ++ .../integrated_electronics/core/helpers.dm | 136 +++ .../core/integrated_circuit.dm | 437 ++++++++++ .../integrated_electronics/core/pins.dm | 197 +++++ .../integrated_electronics/core/prefab.dm | 156 ++++ .../integrated_electronics/core/printer.dm | 413 +++++++++ .../core/special_pins/boolean_pin.dm | 26 + .../core/special_pins/char_pin.dm | 27 + .../core/special_pins/color_pin.dm | 49 ++ .../core/special_pins/dir_pin.dm | 31 + .../core/special_pins/list_pin.dm | 148 ++++ .../core/special_pins/number_pin.dm | 18 + .../core/special_pins/ref_pin.dm | 14 + .../core/special_pins/string_pin.dm | 27 + .../integrated_electronics/core/wirer.dm | 102 +++ .../integrated_electronics/passive/passive.dm | 7 + .../integrated_electronics/passive/power.dm | 126 +++ .../subtypes/arithmetic.dm | 319 +++++++ .../subtypes/converters.dm | 320 +++++++ .../subtypes/data_transfer.dm | 146 ++++ .../integrated_electronics/subtypes/input.dm | 806 ++++++++++++++++++ .../integrated_electronics/subtypes/lists.dm | 200 +++++ .../integrated_electronics/subtypes/logic.dm | 221 +++++ .../subtypes/manipulation.dm | 443 ++++++++++ .../integrated_electronics/subtypes/memory.dm | 124 +++ .../integrated_electronics/subtypes/output.dm | 359 ++++++++ .../integrated_electronics/subtypes/power.dm | 81 ++ .../subtypes/reagents.dm | 365 ++++++++ .../integrated_electronics/subtypes/smart.dm | 33 + .../integrated_electronics/subtypes/time.dm | 145 ++++ .../integrated_electronics/subtypes/trig.dm | 138 +++ .../reagents/reagent_containers/syringes.dm | 3 - code/modules/research/designs.dm | 29 + .../research/designs/telecomms_designs.dm | 8 + config/admins.txt | 130 ++- config/game_options.txt | 5 + icons/obj/abductor.dmi | Bin 56555 -> 56757 bytes icons/obj/electronic_assemblies.dmi | Bin 0 -> 41014 bytes icons/obj/electronic_assemblies2.dmi | Bin 0 -> 30302 bytes icons/obj/stationobjs.dmi | Bin 58072 -> 60472 bytes sound/machines/synth_no.ogg | Bin 0 -> 6771 bytes sound/machines/synth_yes.ogg | Bin 0 -> 6867 bytes tgstation.dme | 37 + tgui/src/interfaces/exonet_node.ract | 14 + 61 files changed, 6933 insertions(+), 9 deletions(-) create mode 100644 code/__DEFINES/integrated_electronics.dm create mode 100644 code/controllers/subsystem/processing/circuit.dm create mode 100644 code/datums/EPv2.dm create mode 100644 code/game/machinery/exonet_node.dm create mode 100644 code/modules/integrated_electronics/core/analyzer.dm create mode 100644 code/modules/integrated_electronics/core/assemblies.dm create mode 100644 code/modules/integrated_electronics/core/debugger.dm create mode 100644 code/modules/integrated_electronics/core/helpers.dm create mode 100644 code/modules/integrated_electronics/core/integrated_circuit.dm create mode 100644 code/modules/integrated_electronics/core/pins.dm create mode 100644 code/modules/integrated_electronics/core/prefab.dm create mode 100644 code/modules/integrated_electronics/core/printer.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/boolean_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/char_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/color_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/dir_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/list_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/number_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/ref_pin.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/string_pin.dm create mode 100644 code/modules/integrated_electronics/core/wirer.dm create mode 100644 code/modules/integrated_electronics/passive/passive.dm create mode 100644 code/modules/integrated_electronics/passive/power.dm create mode 100644 code/modules/integrated_electronics/subtypes/arithmetic.dm create mode 100644 code/modules/integrated_electronics/subtypes/converters.dm create mode 100644 code/modules/integrated_electronics/subtypes/data_transfer.dm create mode 100644 code/modules/integrated_electronics/subtypes/input.dm create mode 100644 code/modules/integrated_electronics/subtypes/lists.dm create mode 100644 code/modules/integrated_electronics/subtypes/logic.dm create mode 100644 code/modules/integrated_electronics/subtypes/manipulation.dm create mode 100644 code/modules/integrated_electronics/subtypes/memory.dm create mode 100644 code/modules/integrated_electronics/subtypes/output.dm create mode 100644 code/modules/integrated_electronics/subtypes/power.dm create mode 100644 code/modules/integrated_electronics/subtypes/reagents.dm create mode 100644 code/modules/integrated_electronics/subtypes/smart.dm create mode 100644 code/modules/integrated_electronics/subtypes/time.dm create mode 100644 code/modules/integrated_electronics/subtypes/trig.dm create mode 100644 icons/obj/electronic_assemblies.dmi create mode 100644 icons/obj/electronic_assemblies2.dmi create mode 100644 sound/machines/synth_no.ogg create mode 100644 sound/machines/synth_yes.ogg create mode 100644 tgui/src/interfaces/exonet_node.ract diff --git a/code/__DEFINES/integrated_electronics.dm b/code/__DEFINES/integrated_electronics.dm new file mode 100644 index 0000000000..e2467af268 --- /dev/null +++ b/code/__DEFINES/integrated_electronics.dm @@ -0,0 +1,41 @@ +#define IC_INPUT "input" +#define IC_OUTPUT "output" +#define IC_ACTIVATOR "activator" + +// Pin functionality. +#define DATA_CHANNEL "data channel" +#define PULSE_CHANNEL "pulse channel" + +// Methods of obtaining a circuit. +#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. + +// Displayed along with the pin name to show what type of pin it is. +#define IC_FORMAT_ANY "\" +#define IC_FORMAT_STRING "\" +#define IC_FORMAT_CHAR "\" +#define IC_FORMAT_COLOR "\" +#define IC_FORMAT_NUMBER "\" +#define IC_FORMAT_DIR "\" +#define IC_FORMAT_BOOLEAN "\" +#define IC_FORMAT_REF "\" +#define IC_FORMAT_LIST "\" + +#define IC_FORMAT_PULSE "\" + +// Used inside input/output list to tell the constructor what pin to make. +#define IC_PINTYPE_ANY /datum/integrated_io +#define IC_PINTYPE_STRING /datum/integrated_io/string +#define IC_PINTYPE_CHAR /datum/integrated_io/char +#define IC_PINTYPE_COLOR /datum/integrated_io/color +#define IC_PINTYPE_NUMBER /datum/integrated_io/number +#define IC_PINTYPE_DIR /datum/integrated_io/dir +#define IC_PINTYPE_BOOLEAN /datum/integrated_io/boolean +#define IC_PINTYPE_REF /datum/integrated_io/ref +#define IC_PINTYPE_LIST /datum/integrated_io/lists + +#define IC_PINTYPE_PULSE_IN /datum/integrated_io/activate +#define IC_PINTYPE_PULSE_OUT /datum/integrated_io/activate/out + +// Data limits. +#define IC_MAX_LIST_LENGTH 500 diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 787aee5408..f558511954 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -12,6 +12,7 @@ #define INVESTIGATE_PORTAL "portals" #define INVESTIGATE_HALLUCINATIONS "hallucinations" #define INVESTIGATE_RADIATION "radiation" +#define INVESTIGATE_EXONET "exonet" //Individual logging defines #define INDIVIDUAL_ATTACK_LOG "Attack log" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 634cfb8cda..d83557aaec 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -475,3 +475,6 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE #define MAX_PROC_DEPTH 195 // 200 proc calls deep and shit breaks, this is a bit lower to give some safety room #define DUMMY_HUMAN_SLOT_MANIFEST "dummy_manifest_generation" + +#define SYRINGE_DRAW 0 +#define SYRINGE_INJECT 1 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 247e1109f8..2f9e57b5e4 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -53,6 +53,7 @@ #define INIT_ORDER_ATOMS 11 #define INIT_ORDER_LANGUAGE 10 #define INIT_ORDER_MACHINES 9 +#define INIT_ORDER_CIRCUIT 8 #define INIT_ORDER_TIMER 1 #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_AIR -1 diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 75985f9c2e..64ea8de756 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -578,3 +578,23 @@ return "turf" else //regex everything else (works for /proc too) return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", "")) + +/proc/strtohex(str) + if(!istext(str)||!str) + return + var/r + var/c + for(var/i = 1 to length(str)) + c= text2ascii(str,i) + r+= num2hex(c) + return r + +/proc/hextostr(str) + if(!istext(str)||!str) + return + var/r + var/c + for(var/i = 1 to length(str)/2) + c= hex2num(copytext(str,i*2-1,i*2+1)) + r+= ascii2text(c) + return r \ No newline at end of file diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 4e2b793069..3622c6abbf 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -506,6 +506,7 @@ Turf and target are separate in case you want to teleport some distance from a t /* Gets all contents of contents and returns them all in a list. */ + /atom/proc/GetAllContents() var/list/processing_list = list(src) var/list/assembled = list() @@ -1425,6 +1426,46 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) /proc/turf_z_is_planet(turf/T) return GLOB.z_is_planet["[T.z]"] + +//same as do_mob except for movables and it allows both to drift and doesn't draw progressbar +/proc/do_atom(atom/movable/user , atom/movable/target, time = 30, uninterruptible = 0,datum/callback/extra_checks = null) + if(!user || !target) + return TRUE + var/user_loc = user.loc + + var/drifting = FALSE + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = TRUE + + var/target_drifting = FALSE + if(!target.Process_Spacemove(0) && target.inertia_dir) + target_drifting = TRUE + + var/target_loc = target.loc + + var/endtime = world.time+time +// var/starttime = world.time + . = TRUE + while (world.time < endtime) + stoplag(1) + if(QDELETED(user) || QDELETED(target)) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = FALSE + user_loc = user.loc + + if(target_drifting && !target.inertia_dir) + target_drifting = FALSE + target_loc = target.loc + + if((!drifting && user.loc != user_loc) || (!target_drifting && target.loc != target_loc) || (extra_checks && !extra_checks.Invoke())) + . = FALSE + break + //returns a GUID like identifier (using a mostly made up record format) //guids are not on their own suitable for access or security tokens, as most of their bits are predictable. // (But may make a nice salt to one) diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 29ace9eef3..1b58d86db2 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -262,3 +262,5 @@ CONFIG_DEF(number/emergency_shuttle_autocall_threshold) min_val = 0 max_val = 1 integer = FALSE + +CONFIG_DEF(flag/ic_printing) \ No newline at end of file diff --git a/code/controllers/subsystem/processing/circuit.dm b/code/controllers/subsystem/processing/circuit.dm new file mode 100644 index 0000000000..cb10503a80 --- /dev/null +++ b/code/controllers/subsystem/processing/circuit.dm @@ -0,0 +1,70 @@ +PROCESSING_SUBSYSTEM_DEF(circuit) + name = "Circuit" + stat_tag = "CIR" + init_order = INIT_ORDER_CIRCUIT + flags = NONE + + var/cipherkey + + var/list/all_exonet_connections = list() //Address = connection datum. + var/list/obj/machinery/exonet_node/all_exonet_nodes = list() + + var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs + var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs + +/datum/controller/subsystem/processing/circuit/Initialize(start_timeofday) + SScircuit.cipherkey = random_string(2000+rand(0,10), GLOB.alphabet) + circuits_init() + return ..() + +/datum/controller/subsystem/processing/circuit/proc/circuits_init() + //Cached lists for free performance + var/list/all_circuits = src.all_circuits + var/list/circuit_fabricator_recipe_list = src.circuit_fabricator_recipe_list + for(var/path in typesof(/obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/IC = path + var/name = initial(IC.name) + all_circuits[name] = IC // Populating the complete list + + if(!(initial(IC.spawn_flags) & (IC_SPAWN_DEFAULT | IC_SPAWN_RESEARCH))) + continue + + var/category = initial(IC.category_text) + if(!circuit_fabricator_recipe_list[category]) + circuit_fabricator_recipe_list[category] = list() + var/list/category_list = circuit_fabricator_recipe_list[category] + category_list += IC // Populating the fabricator categories + + circuit_fabricator_recipe_list["Assemblies"] = list( + /obj/item/device/electronic_assembly, + /obj/item/device/electronic_assembly/medium, + /obj/item/device/electronic_assembly/large, + /obj/item/device/electronic_assembly/drone + //new /obj/item/weapon/implant/integrated_circuit, + //new /obj/item/device/assembly/electronic_assembly + ) + + circuit_fabricator_recipe_list["Tools"] = list( + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/device/integrated_electronics/analyzer + ) + +/datum/controller/subsystem/processing/circuit/proc/get_exonet_node() + for(var/i in 1 to all_exonet_nodes.len) + var/obj/machinery/exonet_node/E = all_exonet_nodes[i] + if(E.is_operating()) + return E + +/datum/controller/subsystem/processing/circuit/proc/get_exonet_address(addr) + return all_exonet_connections[addr] + + +// Proc: get_atom_from_address() +// Parameters: 1 (target_address - the desired address to find) +// Description: Searches an address for the atom it is attached for, otherwise returns null. + +/datum/controller/subsystem/processing/circuit/proc/get_atom_from_address(var/target_address) + var/datum/exonet_protocol/exonet = SScircuit.get_exonet_address(target_address) + if(exonet) + return exonet.holder \ No newline at end of file diff --git a/code/datums/EPv2.dm b/code/datums/EPv2.dm new file mode 100644 index 0000000000..85088ff903 --- /dev/null +++ b/code/datums/EPv2.dm @@ -0,0 +1,113 @@ +/* +Exonet Protocol Version 2 + +This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages +between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message. +Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future. + +Version 1 never existed. + +*Setting up* + +To set up the exonet link, define a variable on your desired atom it is like this; + var/datum/exonet_protocol/exonet = null +Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP. +The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first. + +When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address() +Destroy() also automatically calls remove_address(). + +*Sending messages* + +To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your +intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you +ping yourself. + +*Receiving messages* +You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits +your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder. +You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received. +The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself. +It's suggested to start with an if or switch statement for the message, to determine what to do. +*/ + +/datum/exonet_protocol + var/address = "" //Resembles IPv6, but with only five 'groups', e.g. XXXX:XXXX:XXXX:XXXX:XXXX + var/atom/holder + +/datum/exonet_protocol/New(var/atom/H) + holder = H + +/datum/exonet_protocol/Destroy() + remove_address() + holder = null + return ..() + +// Proc: make_address() +// Parameters: 1 (string - used to make into a hash that will be part of the new address) +// Description: Allocates a new address based on the string supplied. It results in consistant addresses for each round assuming it is not already taken.. +/datum/exonet_protocol/proc/make_address(var/string) + if(!string) + return + var/hex = copytext(md5(string),1,25) + if(!hex) + return + var/addr_1 = copytext(hex,1,5) + var/addr_2 = copytext(hex,5,9) + var/addr_3 = copytext(hex,9,13) + var/addr_4 = copytext(hex,13,17) + address = "fc00:[addr_1]:[addr_2]:[addr_3]:[addr_4]" + if(SScircuit.all_exonet_connections[address]) + stack_trace("WARNING: Exonet address collision in make_address. Holder type if applicable is [holder? holder.type : "NO HOLDER"]!") + SScircuit.all_exonet_connections[address] = src + + +// Proc: make_arbitrary_address() +// Parameters: 1 (new_address - the desired address) +// Description: Allocates that specific address, if it is available. +/datum/exonet_protocol/proc/make_arbitrary_address(var/new_address) + if(new_address) + if(new_address == SScircuit.get_exonet_address(new_address) ) //Collision test. + return FALSE + address = new_address + SScircuit.all_exonet_connections[address] = src + return TRUE + + +// Proc: remove_address() +// Parameters: None +// Description: Deallocates the address, freeing it for use. +/datum/exonet_protocol/proc/remove_address() + SScircuit.all_exonet_connections -= address + address = "" + + + +// Proc: send_message() +// Parameters: 3 (target_address - the desired address to send the message to, data_type - text stating what the content is meant to be used for, +// content - the actual 'message' being sent to the address) +// Description: Sends the message to target_address, by calling receive_message() on the desired datum. Returns true if the message is recieved. +/datum/exonet_protocol/proc/send_message(var/target_address, var/data_type, var/content) + if(!address) + return FALSE + var/obj/machinery/exonet_node/node = SScircuit.get_exonet_node() + if(!node) // Telecomms went boom, ion storm, etc. + return FALSE + var/datum/exonet_protocol/exonet = SScircuit.get_exonet_address(target_address) + if(exonet) + node.write_log(address, target_address, data_type, content) + return exonet.receive_message(holder, address, data_type, content) + +// Proc: receive_message() +// Parameters: 4 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, +// data_type - text stating what the content is meant to be used for, content - the actual 'message' being sent from origin_atom) +// Description: Called when send_message() successfully reaches the intended datum. By default, calls receive_exonet_message() on the holder atom. +/datum/exonet_protocol/proc/receive_message(var/atom/origin_atom, var/origin_address, var/data_type, var/content) + holder.receive_exonet_message(origin_atom, origin_address, data_type, content) + return TRUE // for send_message() + +// Proc: receive_exonet_message() +// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent) +// Description: Override this to make your atom do something when a message is received. +/atom/proc/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message, var/text) + return diff --git a/code/datums/weakrefs.dm b/code/datums/weakrefs.dm index 09193aea57..6b47f3521d 100644 --- a/code/datums/weakrefs.dm +++ b/code/datums/weakrefs.dm @@ -1,4 +1,3 @@ - /proc/WEAKREF(datum/input) if(istype(input) && !QDELETED(input)) if(!input.weak_reference) @@ -16,4 +15,9 @@ /datum/weakref/proc/resolve() var/datum/D = locate(reference) +<<<<<<< HEAD return D.weak_reference == src? D : null +======= + return (!QDELETED(D) && D.weak_reference == src) ? D : null + +>>>>>>> bdfbafd... [READY]integrated circuitry port+upgrade. (#32481) diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 75d2ffaea2..e3104c71e6 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -69,5 +69,4 @@ // Motion cameras outside of an "ai monitored" area will use this to detect stuff. if (!area_motion) if(isliving(AM)) - newTarget(AM) - + newTarget(AM) \ No newline at end of file diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm new file mode 100644 index 0000000000..b521f404ce --- /dev/null +++ b/code/game/machinery/exonet_node.dm @@ -0,0 +1,106 @@ +/obj/machinery/exonet_node + name = "exonet node" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "exonet_node" + idle_power_usage = 25 + var/on = TRUE + var/toggle = TRUE + density = TRUE + anchored = TRUE + circuit = /obj/item/circuitboard/machine/exonet_node + max_integrity = 300 + integrity_failure = 100 + armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70) + desc = "This machine is exonet node." + var/list/logs = list() // Gets written to by exonet's send_message() function. + var/opened = FALSE + +/obj/machinery/exonet_node/Initialize() + . = ..() + SScircuit.all_exonet_nodes += src + +/obj/machinery/exonet_node/Destroy() + SScircuit.all_exonet_nodes -= src + return ..() + +/obj/machinery/exonet_node/proc/is_operating() + return on && !stat + +// Proc: update_icon() +// Parameters: None +// Description: Self explanatory. +/obj/machinery/exonet_node/update_icon() + icon_state = "[initial(icon_state)][on? "" : "_off"]" + +// Proc: update_power() +// Parameters: None +// Description: Sets the device on/off and adjusts power draw based on stat and toggle variables. +/obj/machinery/exonet_node/proc/update_power() + on = is_operational() && toggle + use_power = on + update_icon() + +// Proc: emp_act() +// Parameters: 1 (severity - how strong the EMP is, with lower numbers being stronger) +// Description: Shuts off the machine for awhile if an EMP hits it. Ion anomalies also call this to turn it off. +/obj/machinery/exonet_node/emp_act(severity) + if(!(stat & EMPED)) + stat |= EMPED + var/duration = (300 * 10)/severity + addtimer(CALLBACK(src, /obj/machinery/exonet_node/proc/unemp_act), rand(duration - 20, duration + 20)) + update_icon() + ..() + +/obj/machinery/exonet_node/proc/unemp_act(severity) + stat &= ~EMPED + +// Proc: attackby() +// Parameters: 2 (I - the item being whacked against the machine, user - the person doing the whacking) +// Description: Handles deconstruction. +/obj/machinery/exonet_node/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/screwdriver)) + default_deconstruction_screwdriver(user, I) + else if(istype(I, /obj/item/crowbar)) + default_deconstruction_crowbar(user, I) + else + return ..() + +// Proc: attack_ai() +// Parameters: 1 (user - the AI clicking on the machine) +// Description: Redirects to attack_hand() +/obj/machinery/exonet_node/attack_ai(mob/user) + ui_interact(user) + + +/obj/machinery/exonet_node/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, var/force_open = 1,datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "exonet_node", name, 600, 300, master_ui, state) + ui.open() + +/obj/machinery/exonet_node/ui_data(mob/user) + var/list/data = list() + data["toggle"] = toggle + data["logs"] = logs + return data + +/obj/machinery/exonet_node/ui_act(action, params) + if(..()) + return + switch(action) + if("toggle_power") + toggle = !toggle + update_power() + if(!toggle) + investigate_log("has been turned off by [key_name(usr)].", INVESTIGATE_EXONET) + . = TRUE + update_icon() + add_fingerprint(usr) + +// Proc: get_exonet_node() +// Parameters: None +// Description: Helper proc to get a reference to an Exonet node. + +/obj/machinery/exonet_node/proc/write_log(var/origin_address, var/target_address, var/data_type, var/content) + var/msg = "[time2text(world.time, "hh:mm:ss")] | FROM [origin_address] TO [target_address] | TYPE: [data_type] | CONTENT: [content]" + logs.Add(msg) diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 7576824747..0e816a6634 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -193,6 +193,19 @@ /obj/item/stack/cable_coil = 1, /obj/item/stock_parts/subspace/filter = 1) +/obj/item/circuitboard/machine/exonet_node + name = "Exonet Node(machine board)" + build_path = /obj/machinery/exonet_node + origin_tech = "programming=3;engineering=4;bluespace=3;materials=3" + req_components = list( + /obj/item/stock_parts/subspace/ansible = 1, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/subspace/crystal = 1, + /obj/item/stock_parts/subspace/treatment = 2, + /obj/item/stack/cable_coil = 2) + /obj/item/circuitboard/machine/teleporter_hub name = "Teleporter Hub (Machine Board)" build_path = /obj/machinery/teleport/hub diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index c72d6f1cca..3c7ed62311 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -1,3 +1,4 @@ +<<<<<<< HEAD #define PROXIMITY_NONE "" #define PROXIMITY_ON_SCREEN "_red" #define PROXIMITY_NEAR "_yellow" @@ -99,3 +100,172 @@ icon_state = "multitool" toolspeed = 0.1 origin_tech = "magnets=5;engineering=5;abductor=3" +======= +#define PROXIMITY_NONE "" +#define PROXIMITY_ON_SCREEN "_red" +#define PROXIMITY_NEAR "_yellow" + +/** + * Multitool -- A multitool is used for hacking electronic devices. + * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. + * + */ + + + + +/obj/item/device/multitool + name = "multitool" + desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." + icon_state = "multitool" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + force = 5 + w_class = WEIGHT_CLASS_SMALL + throwforce = 0 + throw_range = 7 + throw_speed = 3 + materials = list(MAT_METAL=50, MAT_GLASS=20) + origin_tech = "magnets=1;engineering=2" + var/obj/machinery/buffer // simple machine buffer for device linkage + hitsound = 'sound/weapons/tap.ogg' + toolspeed = 1 + var/datum/integrated_io/selected_io = null //functional for integrated circuits. + var/mode = 0 + +/obj/item/device/multitool/attack_self(mob/user) + if(selected_io) + selected_io = null + to_chat(user, "You clear the wired connection from the multitool.") + else + ..() + update_icon() + +/obj/item/device/multitool/update_icon() + if(selected_io) + icon_state = "multitool_red" + else + icon_state = "multitool" + +/obj/item/device/multitool/proc/wire(var/datum/integrated_io/io, mob/user) + if(!io.holder.assembly) + to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.") + return + + if(selected_io) + if(io == selected_io) + to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") + return + if(io.io_type != selected_io.io_type) + to_chat(user, "Those two types of channels are incompatable. The first is a [selected_io.io_type], \ + while the second is a [io.io_type].") + return + if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) + to_chat(user, "Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.") + return + selected_io.linked |= io + io.linked |= selected_io + + to_chat(user, "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].") + selected_io.holder.interact(user) // This is to update the UI. + selected_io = null + + else + selected_io = io + to_chat(user, "You link \the multitool to \the [selected_io.holder]'s [selected_io.name] data channel.") + + update_icon() + + +/obj/item/device/multitool/proc/unwire(var/datum/integrated_io/io1, var/datum/integrated_io/io2, mob/user) + if(!io1.linked.len || !io2.linked.len) + to_chat(user, "There is nothing connected to the data channel.") + return + + if(!(io1 in io2.linked) || !(io2 in io1.linked) ) + to_chat(user, "These data pins aren't connected!") + return + else + io1.linked.Remove(io2) + io2.linked.Remove(io1) + to_chat(user, "You clip the data connection between the [io1.holder.displayed_name]'s \ + [io1.name] and the [io2.holder.displayed_name]'s [io2.name].") + io1.holder.interact(user) // This is to update the UI. + update_icon() + + + +// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby. + + +/obj/item/device/multitool/ai_detect + var/track_cooldown = 0 + var/track_delay = 10 //How often it checks for proximity + var/detect_state = PROXIMITY_NONE + var/rangealert = 8 //Glows red when inside + var/rangewarning = 20 //Glows yellow when inside + origin_tech = "magnets=1;engineering=2;syndicate=1" + +/obj/item/device/multitool/ai_detect/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/device/multitool/ai_detect/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/device/multitool/ai_detect/process() + if(track_cooldown > world.time) + return + detect_state = PROXIMITY_NONE + multitool_detect() + icon_state = "[initial(icon_state)][detect_state]" + track_cooldown = world.time + track_delay + +/obj/item/device/multitool/ai_detect/proc/multitool_detect() + var/turf/our_turf = get_turf(src) + for(var/mob/living/silicon/ai/AI in GLOB.ai_list) + if(AI.cameraFollow == src) + detect_state = PROXIMITY_ON_SCREEN + break + + if(!detect_state && GLOB.cameranet.chunkGenerated(our_turf.x, our_turf.y, our_turf.z)) + var/datum/camerachunk/chunk = GLOB.cameranet.getCameraChunk(our_turf.x, our_turf.y, our_turf.z) + if(chunk) + if(chunk.seenby.len) + for(var/mob/camera/aiEye/A in chunk.seenby) + var/turf/detect_turf = get_turf(A) + if(get_dist(our_turf, detect_turf) < rangealert) + detect_state = PROXIMITY_ON_SCREEN + break + if(get_dist(our_turf, detect_turf) < rangewarning) + detect_state = PROXIMITY_NEAR + break + +/obj/item/device/multitool/ai_detect/admin + desc = "Used for pulsing wires to test which to cut. Not recommended by doctors. Has a strange tag that says 'Grief in Safety'." //What else should I say for a meme item? + track_delay = 5 + +/obj/item/device/multitool/ai_detect/admin/multitool_detect() + var/turf/our_turf = get_turf(src) + for(var/mob/J in urange(rangewarning,our_turf)) + if(GLOB.admin_datums[J.ckey]) + detect_state = PROXIMITY_NEAR + var/turf/detect_turf = get_turf(J) + if(get_dist(our_turf, detect_turf) < rangealert) + detect_state = PROXIMITY_ON_SCREEN + break + +/obj/item/device/multitool/cyborg + name = "multitool" + desc = "Optimised and stripped-down version of a regular multitool." + toolspeed = 0.5 + +/obj/item/device/multitool/abductor + name = "alien multitool" + desc = "An omni-technological interface." + icon = 'icons/obj/abductor.dmi' + icon_state = "multitool" + toolspeed = 0.1 + origin_tech = "magnets=5;engineering=5;abductor=3" +>>>>>>> bdfbafd... [READY]integrated circuitry port+upgrade. (#32481) diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 994109675d..e169f656c4 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -4,8 +4,7 @@ var/F = file("[GLOB.log_directory]/[subject].html") WRITE_FILE(F, "[time_stamp()] [REF(src)] ([x],[y],[z]) || [src] [message]
") - -/client/proc/investigate_show(subject in list("hrefs","notes, memos, watchlist", INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION) ) +/client/proc/investigate_show(subject in list("hrefs","notes, memos, watchlist", INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION) ) set name = "Investigate" set category = "Admin" if(!holder) diff --git a/code/modules/integrated_electronics/core/analyzer.dm b/code/modules/integrated_electronics/core/analyzer.dm new file mode 100644 index 0000000000..fb48bb5ae6 --- /dev/null +++ b/code/modules/integrated_electronics/core/analyzer.dm @@ -0,0 +1,103 @@ + + +/obj/item/device/integrated_electronics/analyzer + name = "circuit analyzer" + desc = "This tool can scan an assembly and generate code necessary to recreate it in a circuit printer." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "analyzer" + flags_1 = CONDUCT_1 + w_class = WEIGHT_CLASS_SMALL + var/list/circuit_list = list() + var/list/assembly_list = list(/obj/item/device/electronic_assembly, + /obj/item/device/electronic_assembly/medium, + /obj/item/device/electronic_assembly/large, + /obj/item/device/electronic_assembly/drone) + +/obj/item/device/integrated_electronics/analyzer/afterattack(var/atom/A, var/mob/living/user) + visible_message( "attempt to scan") + if(ispath(A.type,/obj/item/device/electronic_assembly)) + var/i = 0 + var/j = 0 + var/HTML ="start.assembly{{*}}" //1-st in chapters.1-st block is just to secure start of program from excess symbols.{{*}} is delimeter for chapters. + visible_message( "start of scan") + for(var/ix in 1 to assembly_list.len) + var/obj/item/I = assembly_list[ix] + if( A.type == I ) + HTML += initial(I.name) +"=-="+A.name //2-nd block.assembly type and name. Maybe in future there will also be color and accesories. + break + /* + If(I.name == "electronic implant") + var/obj/item/weapon/implant/integrated_circuit/PI = PA //now it can't recreate electronic implants.and devices maybe I'll fix it later. + var/obj/item/device/electronic_assembly/implant/PIC = PI.IC + A = PIC + */ + HTML += "{{*}}components" //3-rd block.components. First element is useless.delimeter for elements is ^%^.In element first circuit's default name.Second is user given name.delimiter is =-= + + for(var/obj/item/integrated_circuit/IC in A.contents) + i =i + 1 + HTML += "^%^"+IC.name+"=-="+IC.displayed_name + if(i == 0) + return + HTML += "{{*}}values" //4-th block.values. First element is useless.delimeter for elements is ^%^.In element first i/o id.Second is data type.third is value.delimiter is :+: + + i = 0 + var/val + var/list/inp=list() + var/list/out=list() + var/list/act=list() + var/list/ioa=list() + for(var/obj/item/integrated_circuit/IC in A.contents) + i += 1 + if(IC.inputs && IC.inputs.len) + for(j in 1 to IC.inputs.len) + var/datum/integrated_io/IN =IC.inputs[j] + inp[IN] = "[i]i[j]" + if(islist(IN.data)) + val = list2params(IN.data) + HTML += "^%^"+"[i]i[j]:+:list:+:[val]" + else if(isnum(IN.data)) + val= IN.data + HTML += "^%^"+"[i]i[j]:+:num:+:[val]" + else if(istext(IN.data)) + val = IN.data + HTML += "^%^"+"[i]i[j]:+:text:+:[val]" + if(IC.outputs && IC.outputs.len) + for(j in 1 to IC.outputs.len) //Also this block uses for setting all i/o id's + var/datum/integrated_io/OUT = IC.outputs[j] + out[OUT] = "[i]o[j]" + if(IC.activators && IC.activators.len) + for(j in 1 to IC.activators.len) + var/datum/integrated_io/ACT = IC.activators[j] + act[ACT] = "[i]a[j]" + ioa.Add(inp) + ioa.Add(out) + ioa.Add(act) + HTML += "{{*}}wires" + if(inp && inp.len) + for(i in 1 to inp.len) //5-th block.wires. First element is useless.delimeter for elements is ^%^.In element first i/o id.Second too.delimiter is =-= + var/datum/integrated_io/P = inp[i] + for(j in 1 to P.linked.len) + var/datum/integrated_io/C = P.linked[j] + HTML += "^%^"+inp[P]+"=-="+ioa[C] + if(out && out.len) + for(i in 1 to out.len) //5-th block.wires. First element is useless.delimeter for elements is ^%^.In element first i/o id.Second too.delimiter is =-= + var/datum/integrated_io/P = out[i] + for(j in 1 to P.linked.len) + var/datum/integrated_io/C = P.linked[j] + HTML += "^%^"+out[P]+"=-="+ioa[C] + if(act && act.len) + for(i in 1 to act.len) //5-th block.wires. First element is useless.delimeter for elements is ^%^.In element first i/o id.Second too.delimiter is =-= + var/datum/integrated_io/P = act[i] + for(j in 1 to P.linked.len) + var/datum/integrated_io/C = P.linked[j] + HTML += "^%^"+act[P]+"=-="+ioa[C] + + HTML += "{{*}}end" //6 block.like 1. + visible_message( "[A] has been scanned,") + user << browse(jointext(HTML, null), "window=analyzer;size=[500]x[600];border=1;can_resize=1;can_close=1;can_minimize=1") + else + ..() + + + + diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm new file mode 100644 index 0000000000..d992718bc7 --- /dev/null +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -0,0 +1,342 @@ +#define IC_MAX_SIZE_BASE 25 +#define IC_COMPLEXITY_BASE 75 + +/obj/item/device/electronic_assembly + name = "electronic assembly" + desc = "It's a case, for building small electronics with." + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "setup_small" + flags_1 = NOBLUDGEON_1 + var/max_components = IC_MAX_SIZE_BASE + var/max_complexity = IC_COMPLEXITY_BASE + var/opened = FALSE + var/obj/item/stock_parts/cell/battery // Internal cell which most circuits need to work. + var/cell_type = /obj/item/stock_parts/cell + var/can_charge = TRUE //Can it be charged in a recharger? + var/charge_sections = 4 + var/charge_tick = FALSE + var/charge_delay = 4 + var/use_cyborg_cell = TRUE + +/obj/item/device/electronic_assembly/proc/check_interactivity(mob/user) + return user.canUseTopic(src,be_close = TRUE) + + +/obj/item/device/electronic_assembly/medium + name = "electronic mechanism" + icon_state = "setup_medium" + desc = "It's a case, for building medium-sized electronics with." + w_class = WEIGHT_CLASS_NORMAL + max_components = IC_MAX_SIZE_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + +/obj/item/device/electronic_assembly/large + name = "electronic machine" + icon_state = "setup_large" + desc = "It's a case, for building large electronics with." + w_class = WEIGHT_CLASS_BULKY + max_components = IC_MAX_SIZE_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + anchored = FALSE + +/obj/item/device/electronic_assembly/large/attackby(var/obj/item/O, var/mob/user) + if(default_unfasten_wrench(user, O, 20)) + return + ..() + +/obj/item/device/electronic_assembly/large/attack_tk(mob/user) + if(anchored) + return + ..() + +/obj/item/device/electronic_assembly/large/attack_hand(mob/user) + if(anchored) + attack_self(user) + return + ..() + +/obj/item/device/electronic_assembly/drone + name = "electronic drone" + icon_state = "setup_drone" + desc = "It's a case, for building mobile electronics with." + w_class = WEIGHT_CLASS_SMALL + max_components = IC_MAX_SIZE_BASE * 3 + max_complexity = IC_COMPLEXITY_BASE * 3 + + + +/obj/item/device/electronic_assembly/Initialize() + .=..() + START_PROCESSING(SScircuit, src) + +/obj/item/device/electronic_assembly/Destroy() + STOP_PROCESSING(SScircuit, src) + return ..() + +/obj/item/device/electronic_assembly/process() + handle_idle_power() + +/obj/item/device/electronic_assembly/proc/handle_idle_power() + // First we generate power. + for(var/obj/item/integrated_circuit/passive/power/P in contents) + P.make_energy() + + // Now spend it. + for(var/obj/item/integrated_circuit/IC in contents) + if(IC.power_draw_idle) + if(!draw_power(IC.power_draw_idle)) + IC.power_fail() + + +/obj/item/device/electronic_assembly/interact(mob/user) + if(!check_interactivity(user)) + return + + var/total_part_size = return_total_size() + var/total_complexity = return_total_complexity() + var/HTML = list() + + HTML += "[name]" + HTML += "
\[Refresh\] | " + HTML += "\[Rename\]
" + HTML += "[total_part_size]/[max_components] ([round((total_part_size / max_components) * 100, 0.1)]%) space taken up in the assembly.
" + HTML += "[total_complexity]/[max_complexity] ([round((total_complexity / max_complexity) * 100, 0.1)]%) maximum complexity.
" + if(battery) + HTML += "[round(battery.charge, 0.1)]/[battery.maxcharge] ([round(battery.percent(), 0.1)]%) cell charge. \[Remove\]" + else + HTML += "No powercell detected!" + HTML += "

" + HTML += "Components:
" + HTML += "Built in:
" + + +//Put removable circuits in separate categories from non-removable + for(var/obj/item/integrated_circuit/circuit in contents) + if(!circuit.removable) + HTML += "[circuit.displayed_name] | " + HTML += "\[Rename\] | " + HTML += "\[Scan with Debugger\] | " + HTML += "\[Move to Bottom\]" + HTML += "
" + + HTML += "
" + HTML += "Removable:
" + + for(var/obj/item/integrated_circuit/circuit in contents) + if(circuit.removable) + HTML += "[circuit.displayed_name] | " + HTML += "\[Rename\] | " + HTML += "\[Scan with Debugger\] | " + HTML += "\[Remove\] | " + HTML += "\[Move to Bottom\]" + HTML += "
" + + HTML += "" + user << browse(jointext(HTML,null), "window=assembly-\[REF(src)];size=600x350;border=1;can_resize=1;can_close=1;can_minimize=1") + +/obj/item/device/electronic_assembly/Topic(href, href_list[]) + if(..()) + return 1 + + if(href_list["rename"]) + rename(usr) + + if(href_list["remove_cell"]) + if(!battery) + to_chat(usr, "There's no power cell to remove from \the [src].") + else + var/turf/T = get_turf(src) + battery.forceMove(T) + playsound(T, 'sound/items/Crowbar.ogg', 50, 1) + to_chat(usr, "You pull \the [battery] out of \the [src]'s power supplier.") + battery = null + + interact(usr) // To refresh the UI. + +/obj/item/device/electronic_assembly/proc/rename() + + var/mob/M = usr + if(!check_interactivity(M)) + return + + var/input = reject_bad_name(input("What do you want to name this?", "Rename", src.name) as null|text,1) + if(!check_interactivity(M)) + return + if(src && input) + to_chat(M, "The machine now has a label reading '[input]'.") + name = input + +/obj/item/device/electronic_assembly/proc/can_move() + return FALSE + +/obj/item/device/electronic_assembly/drone/can_move() + return TRUE + +/obj/item/device/electronic_assembly/update_icon() + if(opened) + icon_state = initial(icon_state) + "-open" + else + icon_state = initial(icon_state) + +/obj/item/device/electronic_assembly/examine(mob/user) + ..() + for(var/obj/item/integrated_circuit/IC in contents) + IC.external_examine(user) + if(istype(IC,/obj/item/integrated_circuit/output/screen)) + var/obj/item/integrated_circuit/output/screen/S + if(S.stuff_to_display) + to_chat(user, "There's a little screen labeled '[S]', which displays '[S.stuff_to_display]'.") + if(opened) + interact(user) + +/obj/item/device/electronic_assembly/proc/return_total_complexity() + . = 0 + for(var/obj/item/integrated_circuit/part in contents) + . += part.complexity + +/obj/item/device/electronic_assembly/proc/return_total_size() + . = 0 + for(var/obj/item/integrated_circuit/part in contents) + . += part.size + +// Returns true if the circuit made it inside. +/obj/item/device/electronic_assembly/proc/add_circuit(var/obj/item/integrated_circuit/IC, var/mob/user) + if(!opened) + to_chat(user, "\The [src] isn't opened, so you can't put anything inside. Try using a crowbar.") + return FALSE + + if(IC.w_class > w_class) + to_chat(user, "\The [IC] is way too big to fit into \the [src].") + return FALSE + + var/total_part_size = return_total_size() + var/total_complexity = return_total_complexity() + + if((total_part_size + IC.size) > max_components) + to_chat(user, "You can't seem to add the '[IC]', as there's insufficient space.") + return FALSE + if((total_complexity + IC.complexity) > max_complexity) + to_chat(user, "You can't seem to add the '[IC]', since this setup's too complicated for the case.") + return FALSE + + if(!user.transferItemToLoc(IC, src)) + return FALSE + + IC.assembly = src + + return TRUE + +/obj/item/device/electronic_assembly/afterattack(atom/target, mob/user, proximity) + for(var/obj/item/integrated_circuit/input/sensor/S in contents) + if(!proximity) + if(istype(S,/obj/item/integrated_circuit/input/sensor/ranged)||(!user)) + if(user.client) + if(!(target in view(user.client))) + continue + else + if(!(target in view(user))) + continue + else + continue + S.set_pin_data(IC_OUTPUT, 1, WEAKREF(target)) + S.check_then_do_work() + S.scan(target) + + visible_message(" [user] waves [src] around [target].") + +/obj/item/device/electronic_assembly/attackby(var/obj/item/I, var/mob/user) + if(istype(I, /obj/item/integrated_circuit)) + if(!user.canUnEquip(I)) + return FALSE + if(add_circuit(I, user)) + to_chat(user, "You slide [I] inside [src].") + playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) + interact(user) + return TRUE + else if(istype(I, /obj/item/crowbar)) + playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) + opened = !opened + to_chat(user, "You [opened ? "opened" : "closed"] [src].") + update_icon() + return TRUE + else if(istype(I, /obj/item/device/integrated_electronics/wirer) || istype(I, /obj/item/device/integrated_electronics/debugger) || istype(I, /obj/item/screwdriver)) + if(opened) + interact(user) + else + to_chat(user, " [src] isn't opened, so you can't fiddle with the internal components. \ + Try using a crowbar.") + else if(istype(I, /obj/item/stock_parts/cell)) + if(!opened) + to_chat(user, " [src] isn't opened, so you can't put anything inside. Try using a crowbar.") + return FALSE + if(battery) + to_chat(user, " [src] already has \a [battery] inside. Remove it first if you want to replace it.") + return FALSE + var/obj/item/stock_parts/cell = I + user.transferItemToLoc(I, loc) + cell.forceMove(src) + battery = cell + playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You slot \the [cell] inside \the [src]'s power supplier.") + interact(user) + return TRUE + else + return ..() + +/obj/item/device/electronic_assembly/attack_self(mob/user) + if(!check_interactivity(user)) + return + if(opened) + interact(user) + + var/list/input_selection = list() + var/list/available_inputs = list() + for(var/obj/item/integrated_circuit/input/input in contents) + if(input.can_be_asked_input) + available_inputs.Add(input) + var/i = 0 + for(var/obj/item/integrated_circuit/s in available_inputs) + if(s.name == input.name && s.displayed_name == input.displayed_name && s != input) + i++ + var/disp_name= "[input.displayed_name] \[[input]\]" + if(i) + disp_name += " ([i+1])" + input_selection.Add(disp_name) + + var/obj/item/integrated_circuit/input/choice + if(available_inputs) + if(available_inputs.len ==1) + choice = available_inputs[1] + else + var/selection = input(user, "What do you want to interact with?", "Interaction") as null|anything in input_selection + if(!check_interactivity(user)) + return + if(selection) + var/index = input_selection.Find(selection) + choice = available_inputs[index] + + if(choice) + choice.ask_for_input(user) + +/obj/item/device/electronic_assembly/emp_act(severity) + ..() + for(var/i in 1 to contents.len) + var/atom/movable/AM = contents[i] + AM.emp_act(severity) + +// Returns true if power was successfully drawn. +/obj/item/device/electronic_assembly/proc/draw_power(amount) + if(battery && battery.use(amount * GLOB.CELLRATE)) + return TRUE + return FALSE + +// Ditto for giving. +/obj/item/device/electronic_assembly/proc/give_power(amount) + if(battery && battery.give(amount * GLOB.CELLRATE)) + return TRUE + return FALSE + +/obj/item/device/electronic_assembly/Moved(oldLoc, dir) + for(var/obj/item/integrated_circuit/IC in contents) + IC.ext_moved(oldLoc, dir) diff --git a/code/modules/integrated_electronics/core/debugger.dm b/code/modules/integrated_electronics/core/debugger.dm new file mode 100644 index 0000000000..77deaa94db --- /dev/null +++ b/code/modules/integrated_electronics/core/debugger.dm @@ -0,0 +1,64 @@ + + +/obj/item/device/integrated_electronics/debugger + name = "circuit debugger" + desc = "This small tool allows one working with custom machinery to directly set data to a specific pin, useful for writing \ + settings to specific circuits, or for debugging purposes. It can also pulse activation pins." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "debugger" + flags_1 = CONDUCT_1 | NOBLUDGEON_1 + w_class = WEIGHT_CLASS_SMALL + var/data_to_write = null + var/accepting_refs = FALSE + +/obj/item/device/integrated_electronics/debugger/attack_self(mob/user) + var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") + if(!user.IsAdvancedToolUser()) + return + + var/new_data = null + switch(type_to_use) + if("string") + accepting_refs = FALSE + new_data = input("Now type in a string.","[src] string writing") as null|text + if(istext(new_data) && user.IsAdvancedToolUser()) + data_to_write = new_data + to_chat(user, "You set \the [src]'s memory to \"[new_data]\".") + if("number") + accepting_refs = FALSE + new_data = input("Now type in a number.","[src] number writing") as null|num + if(isnum(new_data) && user.IsAdvancedToolUser()) + data_to_write = new_data + to_chat(user, "You set \the [src]'s memory to [new_data].") + if("ref") + accepting_refs = TRUE + to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ + an object for a ref of that object to save it in memory.") + if("null") + data_to_write = null + to_chat(user, "You set \the [src]'s memory to absolutely nothing.") + +/obj/item/device/integrated_electronics/debugger/afterattack(atom/target, mob/living/user, proximity) + if(accepting_refs && proximity) + data_to_write = WEAKREF(target) + visible_message("[user] slides \a [src]'s over \the [target].") + to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ + now off.") + accepting_refs = FALSE + else + return ..() + +/obj/item/device/integrated_electronics/debugger/proc/write_data(var/datum/integrated_io/io, mob/user) + if(io.io_type == DATA_CHANNEL) + io.write_data_to_pin(data_to_write) + var/data_to_show = data_to_write + if(isweakref(data_to_write)) + var/datum/weakref/w = data_to_write + var/atom/A = w.resolve() + data_to_show = A.name + to_chat(user, "You write '[data_to_write ? data_to_show : "NULL"]' to the '[io]' pin of \the [io.holder].") + else if(io.io_type == PULSE_CHANNEL) + io.holder.check_then_do_work(ignore_power = TRUE) + to_chat(user, "You pulse \the [io.holder]'s [io].") + + io.holder.interact(user) // This is to update the UI. diff --git a/code/modules/integrated_electronics/core/helpers.dm b/code/modules/integrated_electronics/core/helpers.dm new file mode 100644 index 0000000000..e2fed297e6 --- /dev/null +++ b/code/modules/integrated_electronics/core/helpers.dm @@ -0,0 +1,136 @@ +/obj/item/integrated_circuit/proc/setup_io(var/list/io_list, var/io_type, var/list/io_default_list) + var/list/io_list_copy = io_list.Copy() + io_list.Cut() + for(var/i in 1 to io_list_copy.len) + var/io_entry = io_list_copy[i] + var/default_data = null + var/io_type_override = null + // Override the default data. + if(io_default_list && io_default_list.len) // 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. + // Override the pin type. + if(io_list_copy[io_entry]) + io_type_override = io_list_copy[io_entry] + + if(io_type_override) + // world << "io_type_override is now [io_type_override] on [src]." + io_list.Add(new io_type_override(src, io_entry, default_data)) + else + io_list.Add(new io_type(src, io_entry, default_data)) + + +/obj/item/integrated_circuit/proc/set_pin_data(var/pin_type, var/pin_number, datum/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) + return pin.write_data_to_pin(new_data) + +/obj/item/integrated_circuit/proc/get_pin_data(var/pin_type, var/pin_number) + var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number) + return pin.get_data() + +/obj/item/integrated_circuit/proc/get_pin_data_as_type(var/pin_type, var/pin_number, var/as_type) + var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number) + return pin.data_as_type(as_type) + +/obj/item/integrated_circuit/proc/activate_pin(var/pin_number) + var/datum/integrated_io/activate/A = activators[pin_number] + A.push_data() + +/datum/integrated_io/proc/get_data() + if(isweakref(data)) + return data.resolve() + return data + +/obj/item/integrated_circuit/proc/get_pin_ref(var/pin_type, var/pin_number) + switch(pin_type) + if(IC_INPUT) + if(pin_number > inputs.len) + return + return inputs[pin_number] + if(IC_OUTPUT) + if(pin_number > outputs.len) + return + return outputs[pin_number] + if(IC_ACTIVATOR) + if(pin_number > activators.len) + return + return activators[pin_number] + return + +/obj/item/integrated_circuit/proc/handle_wire(var/datum/integrated_io/pin, var/obj/item/device/integrated_electronics/tool) + if(istype(tool, /obj/item/device/integrated_electronics/wirer)) + var/obj/item/device/integrated_electronics/wirer/wirer = tool + if(pin) + wirer.wire(pin, usr) + return TRUE + + else if(istype(tool, /obj/item/device/integrated_electronics/debugger)) + var/obj/item/device/integrated_electronics/debugger/debugger = tool + if(pin) + debugger.write_data(pin, usr) + return TRUE + return FALSE + + +/obj/item/integrated_circuit/proc/asc2b64(var/S) + var/static/list/b64 = list( + "A"=0,"B"=1,"C"=2,"D"=3, + "E"=4,"F"=5,"G"=6,"H"=7, + "I"=8,"J"=9,"K"=10,"L"=11, + "M"=12,"N"=13,"O"=14,"P"=15, + "Q"=16,"R"=17,"S"=18,"T"=19, + "U"=20,"V"=21,"W"=22,"X"=23, + "Y"=24,"Z"=25,"a"=26,"b"=27, + "c"=28,"d"=29,"e"=30,"f"=31, + "g"=32,"h"=33,"i"=34,"j"=35, + "k"=36,"l"=37,"m"=38,"n"=39, + "o"=40,"p"=41,"q"=42,"r"=43, + "s"=44,"t"=45,"u"=46,"v"=47, + "w"=48,"x"=49,"y"=50,"z"=51, + "0"=52,"1"=53,"2"=54,"3"=55, + "4"=56,"5"=57,"6"=58,"7"=59, + "8"=60,"9"=61,","=62,"."=63 + ) + var/ls = lentext(S) + var/c + var/i=1 + while(i <= ls) + var/sb1=text2ascii(S,i) + var/sb2=text2ascii(S,i+1) + var/sb3=text2ascii(S,i+2) + var/cb1 = (sb1 & 252)>>2 + var/cb2 = ((sb1 & 3)<<6 | (sb2 & 240)>>2)>>2 + var/cb3 = (sb2 & 15)<<2 | (sb3 & 192)>>6 + var/cb4 = (sb3 & 63) + c=c+b64[cb1+1]+b64[cb2+1]+b64[cb3+1]+b64[cb4+1] + i=i+3 + return c + +/obj/item/integrated_circuit/proc/b642asc(var/S) + var/static/list/b64 = list("A"=1,"B"=2,"C"=3,"D"=4,"E"=5,"F"=6,"G"=7,"H"=8,"I"=9,"J"=10,"K"=11,"L"=12,"M"=13,"N"=14,"O"=15,"P"=16,"Q"=17,"R"=18, + "S"=19,"T"=20,"U"=21,"V"=22,"W"=23,"X"=24,"Y"=25,"Z"=26,"a"=27,"b"=28,"c"=29,"d"=30,"e"=31,"f"=32,"g"=33,"h"=34,"i"=35,"j"=36,"k"=37,"l"=38,"m"=39,"n"=40,"o"=41, + "p"=42,"q"=43,"r"=44,"s"=45,"t"=46,"u"=47,"v"=48,"w"=49,"x"=50,"y"=51,"z"=52,"0"=53,"1"=54,"2"=55,"3"=56,"4"=57,"5"=58,"6"=59,"7"=60,"8"=61,"9"=62,","=63,"."=64) + var/ls = lentext(S) + var/c + var/i=1 + while(i<=ls) + var/cb1=b64[copytext(S,i,i+1)]-1 + var/cb2=b64[copytext(S,i+1,i+2)]-1 + var/cb3=b64[copytext(S,i+2,i+3)]-1 + var/cb4=b64[copytext(S,i+3,i+4)]-1 + var/sb1=cb1<<2 | (cb2 & 48)>>4 + var/sb2=(cb2 & 15) <<4 | (cb3 & 60)>>2 + var/sb3=(cb3 & 3)<<6 | cb4 + c=c+ascii2text(sb1)+ascii2text(sb2)+ascii2text(sb3) + i=i+4 + return c + +/proc/XorEncrypt(string,key) + if(!string || !key ||!istext(string)||!istext(key)) + return + var/r + for(var/i = 1 to length(string)) + r += ascii2text(text2ascii(string,i) ^ text2ascii(key,(i-1)%length(string)+1)) + return r + diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm new file mode 100644 index 0000000000..b8fe3e3cbb --- /dev/null +++ b/code/modules/integrated_electronics/core/integrated_circuit.dm @@ -0,0 +1,437 @@ +/obj/item/integrated_circuit + name = "integrated circuit" + desc = "It's a tiny chip! This one doesn't seem to do much, however." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "template" + w_class = WEIGHT_CLASS_TINY + var/obj/item/device/electronic_assembly/assembly // Reference to the assembly holding this circuit, if any. + var/extended_desc + var/list/inputs = list() + var/list/inputs_default = list()// Assoc list which will fill a pin with data upon creation. e.g. "2" = 0 will set input pin 2 to equal 0 instead of null. + var/list/outputs = list() + var/list/outputs_default =list()// Ditto, for output. + var/list/activators = list() + var/next_use = 0 // Uses world.time + var/complexity = 1 // This acts as a limitation on building machines, more resource-intensive components cost more 'space'. + var/size = 1 // This acts as a limitation on building machines, bigger components cost more 'space'. -1 for size 0 + var/cooldown_per_use = 9 // Circuits are limited in how many times they can be work()'d by this variable. + var/power_draw_per_use = 0 // How much power is drawn when work()'d. + 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/removable = TRUE // Determines if a circuit is removable from the assembly. + var/displayed_name = "" + var/allow_multitool = TRUE // Allows additional multitool functionality + // Used as a global var, (Do not set manually in children). + +/* + Integrated circuits are essentially modular machines. Each circuit has a specific function, and combining them inside Electronic Assemblies allows +a creative player the means to solve many problems. Circuits are held inside an electronic assembly, and are wired using special tools. +*/ + +/obj/item/integrated_circuit/examine(mob/user) + interact(user) + external_examine(user) + . = ..() + +// This should be used when someone is examining while the case is opened. +/obj/item/integrated_circuit/proc/internal_examine(mob/user) + to_chat(user, "This board has [inputs.len] input pin\s, [outputs.len] output pin\s and [activators.len] activation pin\s.") + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + if(I.linked.len) + to_chat(user, "The '[I]' is connected to [I.get_linked_to_desc()].") + for(var/k in 1 to outputs.len) + var/datum/integrated_io/O = outputs[k] + if(O.linked.len) + to_chat(user, "The '[O]' is connected to [O.get_linked_to_desc()].") + for(var/k in 1 to activators.len) + var/datum/integrated_io/activate/A = activators[k] + if(A.linked.len) + to_chat(user, "The '[A]' is connected to [A.get_linked_to_desc()].") + any_examine(user) + interact(user) + +// This should be used when someone is examining from an 'outside' perspective, e.g. reading a screen or LED. +/obj/item/integrated_circuit/proc/external_examine(mob/user) + any_examine(user) + +/obj/item/integrated_circuit/proc/any_examine(mob/user) + return + +/obj/item/integrated_circuit/proc/check_interactivity(mob/user) + if(assembly) + return assembly.check_interactivity(user) + else + return user.canUseTopic(src,be_close = TRUE) + +/obj/item/integrated_circuit/Initialize() + displayed_name = name + setup_io(inputs, /datum/integrated_io, inputs_default) + setup_io(outputs, /datum/integrated_io, outputs_default) + setup_io(activators, /datum/integrated_io/activate) + ..() + +/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/Destroy() + QDEL_LIST(inputs) + QDEL_LIST(outputs) + QDEL_LIST(activators) + . = ..() +/* +/obj/item/integrated_circuit/nano_host() + if(istype(src.loc, /obj/item/device/electronic_assembly)) + var/obj/item/device/electronic_assembly/assembly = loc + return assembly.resolve_nano_host() + return ..() +*/ +/obj/item/integrated_circuit/emp_act(severity) + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.scramble() + for(var/k in 1 to outputs.len) + var/datum/integrated_io/O = outputs[k] + O.scramble() + for(var/k in 1 to activators.len) + var/datum/integrated_io/activate/A = activators[k] + A.scramble() + + +/obj/item/integrated_circuit/verb/rename_component() + set name = "Rename Circuit" + set category = "Object" + set desc = "Rename your circuit, useful to stay organized." + + var/mob/M = usr + if(!check_interactivity(M)) + return + + var/input = reject_bad_name(input("What do you want to name this?", "Rename", src.name) as null|text,1) + if(src && input && check_interactivity(M)) + to_chat(M, "The circuit '[src.name]' is now labeled '[input]'.") + displayed_name = input + +/obj/item/integrated_circuit/interact(mob/user) + if(!check_interactivity(user)) + return +// if(!assembly) +// return + + var/window_height = 350 + var/window_width = 600 + + //var/table_edge_width = "[(window_width - window_width * 0.1) / 4]px" + //var/table_middle_width = "[(window_width - window_width * 0.1) - (table_edge_width * 2)]px" + var/table_edge_width = "30%" + var/table_middle_width = "40%" + + var/HTML = list() + HTML += "[src.displayed_name]" + HTML += "
" + HTML += "" + + HTML += "
\[Return to Assembly\]" + + HTML += "
\[Refresh\] | " + HTML += "\[Rename\] | " + HTML += "\[Scan with Device\] | " + if(src.removable) + HTML += "\[Remove\]
" + + HTML += "" + HTML += "" + HTML += "" + HTML += "" + HTML += "" + + var/column_width = 3 + var/row_height = max(inputs.len, outputs.len, 1) + + for(var/i = 1 to row_height) + HTML += "" + for(var/j = 1 to column_width) + var/datum/integrated_io/io = null + var/words = list() + var/height = 1 + switch(j) + if(1) + io = get_pin_ref(IC_INPUT, i) + if(io) + words += "[io.display_pin_type()] [io.name] [io.display_data(io.data)]
" + if(io.linked.len) + for(var/k in 1 to io.linked.len) + var/datum/integrated_io/linked = io.linked[k] +// words += "\[[linked]\] + words += "[linked] \ + @ [linked.holder.displayed_name]
" + + if(outputs.len > inputs.len) + height = 1 + if(2) + if(i == 1) + words += "[src.displayed_name]
[src.name != src.displayed_name ? "([src.name])":""]
[src.desc]" + height = row_height + else + continue + if(3) + io = get_pin_ref(IC_OUTPUT, i) + if(io) + words += "[io.display_pin_type()] [io.name] [io.display_data(io.data)]
" + if(io.linked.len) + for(var/k in 1 to io.linked.len) + var/datum/integrated_io/linked = io.linked[k] +// words += "\[[linked]\] + words += "[linked] \ + @ [linked.holder.displayed_name]
" + + if(inputs.len > outputs.len) + height = 1 + HTML += "" + HTML += "" + + for(var/activator in activators) + var/datum/integrated_io/io = activator + var/words = list() + + words += "[io] [io.data?"\":"\"]
" + if(io.linked.len) + for(var/k in 1 to io.linked.len) + var/datum/integrated_io/linked = io.linked[k] +// words += "\[[linked]\] + words += "[linked] \ + @ [linked.holder.displayed_name]
" + + HTML += "" + HTML += "" + HTML += "" + + HTML += "
[jointext(words, null)]
[jointext(words, null)]
" + HTML += "
" + +// HTML += "
Meta Variables;" // If more meta vars get introduced, uncomment this. +// HTML += "
" + + HTML += "
Complexity: [complexity]" + if(power_draw_idle) + HTML += "
Power Draw: [power_draw_idle] W (Idle)" + if(power_draw_per_use) + HTML += "
Power Draw: [power_draw_per_use] W (Active)" // Borgcode says that powercells' checked_use() takes joules as input. + HTML += "
[extended_desc]" + + HTML += "" + if(src.assembly) + user << browse(jointext(HTML, null), "window=assembly-[REF(src.assembly)];size=[window_width]x[window_height];border=1;can_resize=1;can_close=1;can_minimize=1") + else + user << browse(jointext(HTML, null), "window=circuit-[REF(src)];size=[window_width]x[window_height];border=1;can_resize=1;can_close=1;can_minimize=1") + + onclose(user, "assembly-[REF(src.assembly)]") + +/obj/item/integrated_circuit/Topic(href, href_list) + if(!check_interactivity(usr)) + return + if(..()) + return TRUE + + var/update = TRUE + var/obj/item/device/electronic_assembly/A = src.assembly + var/update_to_assembly = FALSE + var/datum/integrated_io/pin = locate(href_list["pin"]) in inputs + outputs + activators + var/datum/integrated_io/linked = null + if(href_list["link"]) + linked = locate(href_list["link"]) in pin.linked + + var/obj/held_item = usr.get_active_held_item() + + if(href_list["rename"]) + rename_component(usr) + if(href_list["from_assembly"]) + update = FALSE + var/obj/item/device/electronic_assembly/ea = loc + if(istype(ea)) + ea.interact(usr) + + if(href_list["pin_name"]) + if (!istype(held_item, /obj/item/device/multitool) || !allow_multitool) + href_list["wire"] = TRUE + else + var/obj/item/device/multitool/M = held_item + M.wire(pin,usr) + + + + if(href_list["pin_data"]) + if (!istype(held_item, /obj/item/device/multitool) || !allow_multitool) + href_list["wire"] = TRUE + + else + var/datum/integrated_io/io = pin + io.ask_for_pin_data(usr) // The pins themselves will determine how to ask for data, and will validate the data. + /* + if(io.io_type == DATA_CHANNEL) + + var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number", "null") + if(!check_interactivity(usr)) + return + + var/new_data = null + switch(type_to_use) + if("string") + new_data = input("Now type in a string.","[src] string writing") as null|text + to_chat(usr, "You input [new_data] into the pin.") + //to_chat(user, "You write '[new_data]' to the '[io]' pin of \the [io.holder].") + if("number") + new_data = input("Now type in a number.","[src] number writing") as null|num + if(isnum(new_data) && check_interactivity(usr) ) + to_chat(usr, "You input [new_data] into the pin.") + if("null") + if(check_interactivity(usr)) + to_chat(usr, "You clear the pin's memory.") + + io.write_data_to_pin(new_data) + + else if(io.io_type == PULSE_CHANNEL) + io.holder.check_then_do_work(ignore_power = TRUE) + to_chat(usr, "You pulse \the [io.holder]'s [io] pin.") + */ + + + if(href_list["pin_unwire"]) + if (!istype(held_item, /obj/item/device/multitool) || !allow_multitool) + href_list["wire"] = TRUE + else + var/obj/item/device/multitool/M = held_item + M.unwire(pin, linked, usr) + + if(href_list["wire"]) + if(istype(held_item, /obj/item/device/integrated_electronics/wirer)) + var/obj/item/device/integrated_electronics/wirer/wirer = held_item + if(linked) + wirer.wire(linked, usr) + else if(pin) + wirer.wire(pin, usr) + + else if(istype(held_item, /obj/item/device/integrated_electronics/debugger)) + var/obj/item/device/integrated_electronics/debugger/debugger = held_item + if(pin) + debugger.write_data(pin, usr) + else + to_chat(usr, "You can't do a whole lot without the proper tools.") + + if(href_list["examine"]) + var/obj/item/integrated_circuit/examined + if(href_list["examined"]) + examined = href_list["examined"] + else + examined = src + examined.interact(usr) + update = FALSE + + if(href_list["bottom"]) + var/obj/item/integrated_circuit/circuit = locate(href_list["bottom"]) in src.assembly.contents + var/assy = circuit.assembly + if(!circuit) + return + circuit.loc = null + circuit.loc = assy + . = TRUE + update_to_assembly = TRUE + + if(href_list["scan"]) + if(istype(held_item, /obj/item/device/integrated_electronics/debugger)) + var/obj/item/device/integrated_electronics/debugger/D = held_item + if(D.accepting_refs) + D.afterattack(src, usr, TRUE) + else + to_chat(usr, "The Debugger's 'ref scanner' needs to be on.") + else + to_chat(usr, "You need a multitool/debugger set to 'ref' mode to do that.") + + if(href_list["return"]) + if(A) + update_to_assembly = TRUE + usr << browse(null, "window=circuit-[REF(src)];border=1;can_resize=1;can_close=1;can_minimize=1") + else + to_chat(usr, "This circuit is not in an assembly!") + + + if(href_list["remove"]) + if(!A) + to_chat(usr, "This circuit is not in an assembly!") + return + if(!removable) + to_chat(usr, "\The [src] seems to be permanently attached to the case.") + return + var/obj/item/device/electronic_assembly/ea = loc + disconnect_all() + var/turf/T = get_turf(src) + forceMove(T) + assembly = null + playsound(T, 'sound/items/Crowbar.ogg', 50, 1) + to_chat(usr, "You pop \the [src] out of the case, and slide it out.") + + if(istype(ea)) + ea.interact(usr) + update = FALSE + return + + if(update) + if(A && istype(A) && update_to_assembly) + A.interact(usr) + else + interact(usr) // To refresh the UI. + +/obj/item/integrated_circuit/proc/push_data() + for(var/k in 1 to outputs.len) + var/datum/integrated_io/O = outputs[k] + O.push_data() + +/obj/item/integrated_circuit/proc/pull_data() + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.push_data() + +/obj/item/integrated_circuit/proc/draw_idle_power() + if(assembly) + return assembly.draw_power(power_draw_idle) + +// Override this for special behaviour when there's no power left. +/obj/item/integrated_circuit/proc/power_fail() + return + +// Returns true if there's enough power to work(). +/obj/item/integrated_circuit/proc/check_power() + if(!assembly) + return FALSE // Not in an assembly, therefore no power. + if(assembly.draw_power(power_draw_per_use)) + return TRUE // Battery has enough. + return FALSE // Not enough power. + +/obj/item/integrated_circuit/proc/check_then_do_work(var/ignore_power = FALSE) + if(world.time < next_use) // All intergrated circuits have an internal cooldown, to protect from spam. + return + if(power_draw_per_use && !ignore_power) + if(!check_power()) + power_fail() + return + next_use = world.time + cooldown_per_use + do_work() + +/obj/item/integrated_circuit/proc/do_work() + return + +/obj/item/integrated_circuit/proc/disconnect_all() + + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.disconnect() + for(var/k in 1 to outputs.len) + var/datum/integrated_io/O = outputs[k] + O.disconnect() + for(var/k in 1 to activators.len) + var/datum/integrated_io/activate/A = activators[k] + A.disconnect() + +/obj/item/integrated_circuit/proc/ext_moved(oldLoc, dir) + return diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm new file mode 100644 index 0000000000..ba0dbdcf31 --- /dev/null +++ b/code/modules/integrated_electronics/core/pins.dm @@ -0,0 +1,197 @@ +/* + 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. + + +A visualization of how pins work is below. Imagine the below image involves an addition circuit. +When the bottom pin, the activator, receives a pulse, all the numbers on the left (input) get added, and the answer goes on the right side (output). + +Inputs Outputs + +A [2]\ /[8] result +B [1]-\|++|/ +C [4]-/|++| +D [1]/ || + || + Activator + + + +*/ +/datum/integrated_io + var/name = "input/output" + var/obj/item/integrated_circuit/holder + var/datum/weakref/data // This is a weakref, to reduce typecasts. Note that oftentimes numbers and text may also occupy this. + var/list/linked = list() + var/io_type = DATA_CHANNEL + +/datum/integrated_io/New(newloc, name1, new_data) + name = name1 + if(new_data) + data = new_data + holder = newloc + if(!istype(holder)) + message_admins("ERROR: An integrated_io ([name]) spawned without a valid holder! This is a bug.") + +/datum/integrated_io/Destroy() + disconnect() + data = null + holder = null + return ..() +/* +/datum/integrated_io/nano_host() + return holder.nano_host() +*/ + +/datum/integrated_io/proc/data_as_type(var/as_type) + if(!isweakref(data)) + return + var/datum/weakref/w = data + var/output = w.resolve() + return istype(output, as_type) ? output : null + +/datum/integrated_io/proc/display_data(var/input) + if(isnull(input)) + return "(null)" // Empty data means nothing to show. + + if(istext(input)) + return "(\"[input]\")" // Wraps the 'string' in escaped quotes, so that people know it's a 'string'. + +/* +list[]( + "A", + "B", + "C" +) +*/ + + if(islist(input)) + var/list/my_list = input + var/result = "list\[[my_list.len]\](" + if(my_list.len) + result += "
" + var/pos = 0 + for(var/line in my_list) + result += "[display_data(line)]" + pos++ + if(pos != my_list.len) + result += ",
" + result += "
" + result += ")" + return result + + if(isweakref(input)) + var/datum/weakref/w = input + var/atom/A = w.resolve() + return A ? "([A.name] \[Ref\])" : "(null)" // For refs, we want just the name displayed. + //return A ? "([REF(A)] \[Ref\])" : "(null)" + + return "([input])" // Nothing special needed for numbers or other stuff. + +/datum/integrated_io/activate/display_data() + return "(\[pulse\])" + +/datum/integrated_io/proc/display_pin_type() + return IC_FORMAT_ANY + +/datum/integrated_io/activate/display_pin_type() + return IC_FORMAT_PULSE + +/datum/integrated_io/proc/scramble() + if(isnull(data)) + return + if(isnum(data)) + write_data_to_pin(rand(-10000, 10000)) + if(istext(data)) + write_data_to_pin("ERROR") + push_data() + +/datum/integrated_io/activate/scramble() + push_data() + +/datum/integrated_io/proc/write_data_to_pin(var/new_data) + if(isnull(new_data) || isnum(new_data) || istext(new_data) || isweakref(new_data)) + data = new_data + holder.on_data_written() + else if(islist(new_data)) + var/list/new_list = new_data + data = new_list.Copy(1,min( IC_MAX_LIST_LENGTH+1, new_list.len )) + holder.on_data_written() + +/datum/integrated_io/proc/push_data() + for(var/k in 1 to linked.len) + var/datum/integrated_io/io = linked[k] + io.write_data_to_pin(data) + +/datum/integrated_io/activate/push_data() + for(var/k in 1 to linked.len) + var/datum/integrated_io/io = linked[k] + io.holder.check_then_do_work() + +/datum/integrated_io/proc/pull_data() + for(var/k in 1 to linked.len) + var/datum/integrated_io/io = linked[k] + write_data_to_pin(io.data) + +/datum/integrated_io/proc/get_linked_to_desc() + if(linked.len) + return "the [english_list(linked)]" + return "nothing" + +/datum/integrated_io/proc/disconnect() + //First we iterate over everything we are linked to. + if(linked && linked.len) + for(var/i in 1 to linked.len) + var/datum/integrated_io/their_io = linked[i] + //While doing that, we iterate them as well, and disconnect ourselves from them. + if(their_io.linked.len && their_io.linked) + for(var/j in 1 to their_io.linked.len) + var/datum/integrated_io/their_linked_io = their_io.linked[j] + if(their_linked_io == src) + their_io.linked.Remove(src) + else + continue + //Now that we're removed from them, we gotta remove them from us. + linked.Remove(their_io) + +/datum/integrated_io/proc/ask_for_data_type(mob/user, var/default, var/list/allowed_data_types = list("string","number","null")) + var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in allowed_data_types + if(!holder.check_interactivity(user)) + return + + var/new_data = null + switch(type_to_use) + if("string") + new_data = input("Now type in a string.","[src] string writing", istext(default) ? default : null) as null|text + if(istext(new_data) && holder.check_interactivity(user) ) + to_chat(user, "You input "+new_data+" into the pin.") + return new_data + if("number") + new_data = input("Now type in a number.","[src] number writing", isnum(default) ? default : null) as null|num + if(isnum(new_data) && holder.check_interactivity(user) ) + to_chat(user, "You input [new_data] into the pin.") + return new_data + if("null") + if(holder.check_interactivity(user)) + to_chat(user, "You clear the pin's memory.") + return new_data + +// Basically a null check +/datum/integrated_io/proc/is_valid() + return !isnull(data) + +// This proc asks for the data to write, then writes it. +/datum/integrated_io/proc/ask_for_pin_data(mob/user) + var/new_data = ask_for_data_type(user) + write_data_to_pin(new_data) + +/datum/integrated_io/activate/ask_for_pin_data(mob/user) // This just pulses the pin. + holder.check_then_do_work(ignore_power = TRUE) + to_chat(user, "You pulse \the [holder]'s [src] pin.") + +/datum/integrated_io/activate + name = "activation pin" + io_type = PULSE_CHANNEL + +/datum/integrated_io/activate/out // All this does is just make the UI say 'out' instead of 'in' + data = 1 diff --git a/code/modules/integrated_electronics/core/prefab.dm b/code/modules/integrated_electronics/core/prefab.dm new file mode 100644 index 0000000000..3dc3f2d6ef --- /dev/null +++ b/code/modules/integrated_electronics/core/prefab.dm @@ -0,0 +1,156 @@ +/obj/item/device/integrated_electronics/prefab + var/debug = FALSE + name = "prefab" + desc = "new machine in package" + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "box_template" + w_class = WEIGHT_CLASS_BULKY + var/program="blank" + var/list/as_names = list() + +/obj/item/device/integrated_electronics/prefab/attack_self(var/mob/user) + if(program && program != "blank") + assemble(program) + else + return ..() + +/obj/item/device/integrated_electronics/prefab/Initialize() + . = ..() + var/list/assembly_list = list( + /obj/item/device/electronic_assembly, + /obj/item/device/electronic_assembly/medium, + /obj/item/device/electronic_assembly/large, + /obj/item/device/electronic_assembly/drone, + ) + for(var/k in 1 to assembly_list.len) + var/obj/item/I = assembly_list[k] + as_names[initial(I.name)] = I + addtimer(CALLBACK(src, .proc/attack_self), 2) //IDK, why it's need dely,but otherwise it doesn't work. + +/obj/item/device/integrated_electronics/prefab/proc/assemble(var/program) + var/list/all_circuits = SScircuit.all_circuits //cached lists = free performance + var/list/chap = splittext( program ,"{{*}}") + var/list/elements = list() + var/list/elements_input = list() + var/list/element = list() + var/obj/item/AS + var/PA + var/i = 0 + + var/list/ioa = list() + var/datum/integrated_io/IO + var/datum/integrated_io/IO2 + if(debug) + visible_message( "started successful") + if(chap[2] != "") + if(debug) + visible_message( "assembly") + element = splittext( chap[2] ,"=-=") + PA = as_names[element[1]] + AS = new PA(null) + AS.loc = src + AS.name = element[2] + else + return //what's the point if there is no assembly? + if(chap[3] != "components") //if there is only one word,there is no components. + elements_input = splittext( chap[3] ,"^%^") + if(debug) + visible_message( "components[elements_input.len]") + i = 0 + elements = list() + for(var/elem in elements_input) + i=i+1 + if(i>1) + elements.Add(elem) //I don't know,why Cut or copy don't works. If somebody can fix it, it should be fixed. + if(debug) + visible_message( "components[elements.len]") + if(!length(elements_input)) + return + if(debug) + visible_message( "inserting components[elements.len]") + var/obj/item/integrated_circuit/comp + i=0 + for(var/E in elements) + i=i+1 + element = splittext( E ,"=-=") + if(debug) + visible_message( "[E]") + var/path_to_use = all_circuits[element[1]] + comp = new path_to_use(null) + comp.loc = AS + comp.displayed_name = element[2] + comp.assembly = AS + if(comp.inputs && comp.inputs.len) + for(var/j in 1 to comp.inputs.len) + var/datum/integrated_io/IN = comp.inputs[j] + ioa["[i]i[j]"] = IN + if(debug) + visible_message( "[i]i[j]") + if(comp.outputs && comp.outputs.len) + for(var/j in 1 to comp.outputs.len) //Also this block uses for setting all i/o id's + var/datum/integrated_io/OUT = comp.outputs[j] + ioa["[i]o[j]"] = OUT + if(debug) + visible_message( "[i]o[j]") + if(comp.activators && comp.activators.len) + for(var/j in 1 to comp.activators.len) + var/datum/integrated_io/ACT = comp.activators[j] + ioa["[i]a[j]"] = ACT + if(debug) + visible_message( "[i]a[j]") + + else + return + if(!AS.contents.len) + return + if(chap[4] != "values") //if there is only one word,there is no values + elements_input = splittext( chap[4] ,"^%^") + if(debug) + visible_message( "values[elements_input.len]") + i=0 + elements = list() + for(var/elem in elements_input) + i=i+1 + if(i>1) + elements.Add(elem) + if(debug) + visible_message( "values[elements.len]") + if(elements.len>0) + if(debug) + visible_message( "setting values[elements.len]") + for(var/E in elements) + element = splittext( E ,":+:") + if(debug) + visible_message( "[E]") + IO = ioa[element[1]] + if(element[2]=="text") + IO.write_data_to_pin(element[3]) + else if(element[2]=="num") + IO.write_data_to_pin(text2num(element[3])) + else if(element[2]=="list") + IO.write_data_to_pin(params2list(element[3])) + if(chap[5] != "wires") //if there is only one word,there is no wires + elements_input = splittext( chap[5] ,"^%^") + i=0 + elements = list() + if(debug) + visible_message( "wires[elements_input.len]") + for(var/elem in elements_input) + i=i+1 + if(i>1) + elements.Add(elem) + if(debug) + visible_message( "wires[elements.len]") + if(elements.len>0) + if(debug) + visible_message( "setting wires[elements.len]") + for(var/E in elements) + element = splittext( E ,"=-=") + if(debug) + visible_message( "[E]") + IO = ioa[element[1]] + IO2 = ioa[element[2]] + IO.linked |= IO2 + + AS.forceMove(loc) + qdel(src) diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm new file mode 100644 index 0000000000..d2c9d1d3ae --- /dev/null +++ b/code/modules/integrated_electronics/core/printer.dm @@ -0,0 +1,413 @@ +/obj/item/device/integrated_circuit_printer + name = "integrated circuit printer" + desc = "A portable(ish) machine made to print tiny modular circuitry out of metal." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "circuit_printer" + w_class = WEIGHT_CLASS_BULKY + var/metal = 0 + var/init_max_metal = 100 + var/max_metal = 100 + var/metal_per_sheet = 10 // One sheet equals this much metal. + var/debug = FALSE + var/upgraded = FALSE // When hit with an upgrade disk, will turn true, allowing it to print the higher tier circuits. + var/can_clone = FALSE // Same for above, but will allow the printer to duplicate a specific assembly. + var/static/list/recipe_list //category = list(paths in category) + var/current_category = null + var/as_printing = FALSE + var/as_needs = 0 + var/program ="blank" + var/obj/item/device/integrated_electronics/prefab/PR = null + +/obj/item/device/integrated_circuit_printer/proc/check_interactivity(mob/user) + return user.canUseTopic(src,be_close = TRUE) + +/obj/item/device/integrated_circuit_printer/upgraded + upgraded = TRUE + can_clone = TRUE + +/obj/item/device/integrated_circuit_printer/Initialize() + . = ..() + if(!recipe_list) + recipe_list = SScircuit.circuit_fabricator_recipe_list + +/obj/item/device/integrated_circuit_printer/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/stack/sheet/metal)) + var/obj/item/stack/sheet/metal/stack = O + var/num = min((max_metal - metal) / metal_per_sheet, stack.amount) + if(num < 1) + to_chat(user, "\The [src] is too full to add more metal.") + return + if(stack.use(num)) + to_chat(user, "You add [num] sheet\s to \the [src].") + metal += num * metal_per_sheet + if(as_printing) + if(as_needs <= metal) + PR = new/obj/item/device/integrated_electronics/prefab(get_turf(loc)) + PR.program = program + metal = metal - as_needs + to_chat(user, "Assembly has been printed.") + as_printing = FALSE + as_needs = 0 + max_metal = init_max_metal + else + to_chat(user, "Please insert [(as_needs-metal)/10] more metal!") + interact(user) + return TRUE + + if(istype(O,/obj/item/integrated_circuit)) + to_chat(user, "You insert the circuit into \the [src]. ") + user.temporarilyRemoveItemFromInventory(O) + metal = min(metal + O.w_class, max_metal) + qdel(O) + interact(user) + return TRUE + + if(istype(O,/obj/item/disk/integrated_circuit/upgrade/advanced)) + if(upgraded) + to_chat(user, "\The [src] already has this upgrade. ") + return TRUE + to_chat(user, "You install \the [O] into \the [src]. ") + upgraded = TRUE + interact(user) + return TRUE + + if(istype(O,/obj/item/disk/integrated_circuit/upgrade/clone)) + if(can_clone) + to_chat(user, "\The [src] already has this upgrade. ") + return TRUE + to_chat(user, "You install \the [O] into \the [src]. ") + can_clone = TRUE + interact(user) + return TRUE + + return ..() + +/obj/item/device/integrated_circuit_printer/attack_self(var/mob/user) + interact(user) + +/obj/item/device/integrated_circuit_printer/interact(mob/user) + var/window_height = 600 + var/window_width = 500 + + if(isnull(current_category)) + current_category = recipe_list[1] + + var/HTML = "

Integrated Circuit Printer


" + HTML += "Metal: [metal/metal_per_sheet]/[max_metal/metal_per_sheet] sheets.
" + HTML += "Circuits available: [upgraded ? "Advanced":"Regular"]." + HTML += "Assembly Cloning: [can_clone ? "Available": "Unavailable"]." + HTML += "Crossed out circuits mean that the printer is not sufficiently upgraded to create that circuit.
" + HTML += "
" + if(can_clone) + HTML += "Here you can load script for your assembly.
" + if(as_printing) + HTML += " {Load Program} " + else + HTML += " {Load Program} " + if(program == "blank") + HTML += " {Check Program} " + else + HTML += " {Check Program} " + if((program == "blank")|as_printing) + HTML += " {Print assembly} " + else + HTML += " {Print assembly} " + if(as_printing) + HTML += "
printing in process. Please insert more metal. " + HTML += "

" + HTML += "Categories:" + for(var/category in recipe_list) + if(category != current_category) + HTML += " \[[category]\] " + else // Bold the button if it's already selected. + HTML += " \[[category]\] " + HTML += "
" + HTML += "

[current_category]

" + + var/list/current_list = recipe_list[current_category] + for(var/k in 1 to current_list.len) + var/obj/O = current_list[k] + var/can_build = TRUE + if(istype(O, /obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/IC = current_list[k] + if((initial(IC.spawn_flags) & IC_SPAWN_RESEARCH) && (!(initial(IC.spawn_flags) & IC_SPAWN_DEFAULT)) && !upgraded) + can_build = FALSE + if(can_build) + HTML += "\[[initial(O.name)]\]: [initial(O.desc)]
" + else + HTML += "\[[initial(O.name)]\]: [initial(O.desc)]
" + + user << browse(jointext(HTML, null), "window=integrated_printer;size=[window_width]x[window_height];border=1;can_resize=1;can_close=1;can_minimize=1") + +/obj/item/device/integrated_circuit_printer/Topic(href, href_list) + if(!check_interactivity(usr)) + return + if(..()) + return TRUE + var/sc = 0 + add_fingerprint(usr) + + if(href_list["category"]) + current_category = href_list["category"] + + if(href_list["build"]) + var/build_type = text2path(href_list["build"]) + if(!build_type || !ispath(build_type)) + return TRUE + + var/cost = 1 + if(ispath(build_type, /obj/item/device/electronic_assembly)) + var/obj/item/device/electronic_assembly/E = build_type + cost = round( (initial(E.max_complexity) + initial(E.max_components) ) / 4) + else if(ispath(build_type, /obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/IC = build_type + cost = initial(IC.w_class) + + if(metal - cost < 0) + to_chat(usr, "You need [cost] metal to build that!.") + return TRUE + metal -= cost + new build_type(get_turf(loc)) + + if(href_list["print"]) + if(!CONFIG_GET(flag/ic_printing)) + to_chat(usr, "CentCom has disabled printing of custom circuitry due to recent allegations of copyright infringement.") + return + switch(href_list["print"]) + if("load") + program = input("Put your code there:", "loading", null, null) + if("check") + sc = sanity_check(program,usr) + if(sc == 0) + to_chat(usr, "Invalid program.") + else if(sc == -1) + to_chat(usr, "Unknown circuits found. Upgrades required to process this design.") + else if(sc == null) + to_chat(usr, "Invalid program.") + else + to_chat(usr, "Program is correct.You'll need [sc/10] sheets of metal") + if("print") + sc = sanity_check(program,usr) + if(sc == 0 || sc == null) + to_chat(usr, "Invalid program.") + else if(sc == -1) + to_chat(usr, "Unknown circuits found. Upgrades required to process this design.") + else + as_printing = TRUE + if(sc <= metal) + PR = new/obj/item/device/integrated_electronics/prefab(get_turf(loc)) + PR.program = program + metal = metal - sc + to_chat(usr, "Assembly has been printed.") + as_printing = FALSE + as_needs = 0 + max_metal=init_max_metal + else + max_metal = sc + metal_per_sheet + as_needs = sc + to_chat(usr, "Please insert [(as_needs-metal)/10] more metal!") + interact(usr) + +// FUKKEN UPGRADE DISKS +/obj/item/disk/integrated_circuit/upgrade + name = "integrated circuit printer upgrade disk" + desc = "Install this into your integrated circuit printer to enhance it." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "upgrade_disk" + item_state = "card-id" + w_class = WEIGHT_CLASS_SMALL + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4) + +/obj/item/disk/integrated_circuit/upgrade/advanced + name = "integrated circuit printer upgrade disk - advanced designs" + desc = "Install this into your integrated circuit printer to enhance it. This one adds new, advanced designs to the printer." + +// To be implemented later. +/obj/item/disk/integrated_circuit/upgrade/clone + name = "integrated circuit printer upgrade disk - circuit cloner" + desc = "Install this into your integrated circuit printer to enhance it. This one allows the printer to duplicate assemblies." + icon_state = "upgrade_disk_clone" + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 5) + +/obj/item/device/integrated_circuit_printer/proc/sanity_check(var/program,var/mob/user) + var/list/chap = splittext( program ,"{{*}}") + if(chap.len != 6) + return 0 //splitting incorrect + var/list/elements = list() + var/list/elements_input = list() + var/list/element = list() + var/obj/item/PA + var/obj/item/device/electronic_assembly/PF + var/datum/integrated_io/IO + var/datum/integrated_io/IO2 + var/i = 0 + var/obj/item/integrated_circuit/comp + var/list/ioa = list() + var/list/as_samp = list() + var/list/all_circuits = SScircuit.all_circuits // It's free. Performance. We're giving you cpu time. It's free. We're giving you time. It's performance, free. It's free cpu time for you jim! + var/list/assembly_list = list( + /obj/item/device/electronic_assembly, + /obj/item/device/electronic_assembly/medium, + /obj/item/device/electronic_assembly/large, + /obj/item/device/electronic_assembly/drone, + ) + var/compl = 0 + var/maxcomp = 0 + var/cap = 0 + var/maxcap = 0 + var/metalcost = 0 + for(var/k in 1 to assembly_list.len) + var/obj/item/I = assembly_list[k] + as_samp[initial(I.name)] = I + if(debug) + visible_message( "started successful") + if(chap[2] != "") + if(debug) + visible_message( "assembly") + element = splittext( chap[2] ,"=-=") + PA = as_samp[element[1]] + if(ispath(PA,/obj/item/device/electronic_assembly)) + PF = PA + maxcap = initial(PF.max_components) + maxcomp = initial(PF.max_complexity) + metalcost = metalcost + round( (initial(PF.max_complexity) + initial(PF.max_components) ) / 4) + if(debug) + visible_message( "maxcap[maxcap]maxcomp[maxcomp]") + else + return 0 + to_chat(usr, "This is program for [element[2]]") + /* + else if(istype(PA,/obj/item/weapon/implant/integrated_circuit)) + var/obj/item/weapon/implant/integrated_circuit/PI = PA + var/obj/item/device/electronic_assembly/implant/PIC = PI.IC + maxcap = PIC.max_components + maxcomp = PIC.max_complexity + metalcost = metalcost + round( (initial(PIC.max_complexity) + initial(PIC.max_components) ) / 4)*/ + else + return 0 //what's the point if there is no assembly? + if(chap[3] != "components") //if there is only one word,there is no components. + elements_input = splittext( chap[3] ,"^%^") + if(debug) + visible_message( "components[elements_input.len]") + i = 0 + elements = list() + for(var/elem in elements_input) + i=i+1 + if(i>1) + elements.Add(elem) + if(debug) + visible_message( "components[elements.len]") + if(elements_input.len<1) + return 0 + if(debug) + visible_message( "inserting components[elements.len]") + i=0 + for(var/E in elements) + i=i+1 + element = splittext( E ,"=-=") + if(debug) + visible_message( "[E]") + comp = all_circuits[element[1]] + if(!comp) + break + if(!upgraded) + var/obj/item/integrated_circuit/IC = comp + if(!(initial(IC.spawn_flags) & IC_SPAWN_DEFAULT)) + return -1 + compl = compl + initial(comp.complexity) + cap = cap + initial(comp.size) + metalcost = metalcost + initial(initial(comp.w_class)) + var/obj/item/integrated_circuit/circuit = new comp + var/list/ini = circuit.inputs + if(length(ini)) + for(var/j in 1 to ini.len) + var/datum/integrated_io/IN = ini[j] + ioa["[i]i[j]"] = IN + if(debug) + visible_message( "[i]i[j]") + ini = circuit.outputs + if(length(ini)) + for(var/j in 1 to ini.len) //Also this block uses for setting all i/o id's + var/datum/integrated_io/OUT = ini[j] + ioa["[i]o[j]"] = OUT + if(debug) + visible_message( "[i]o[j]") + ini = circuit.activators + if(length(ini)) + for(var/j in 1 to ini.len) + var/datum/integrated_io/ACT = ini.[j] + ioa["[i]a[j]"] = ACT + if(debug) + visible_message( "[i]a[j]") + if(icap[cap]compl[compl]maxcompl[maxcomp]maxcap[maxcap]") + if(cap == 0) + return 0 + if(cap>maxcap) + return 0 + if(compl>maxcomp) + return 0 + if(chap[4] != "values") //if there is only one word,there is no values + elements_input = splittext( chap[4] ,"^%^") + if(debug) + visible_message( "values[elements_input.len]") + i=0 + elements = list() + for(var/elem in elements_input) + i=i+1 + if(i>1) + elements.Add(elem) + if(debug) + visible_message( "values[elements.len]") + if(elements.len>0) + if(debug) + visible_message( "setting values[elements.len]") + for(var/E in elements) + element = splittext( E ,":+:") + if(debug) + visible_message( "[E]") + if(!ioa[element[1]]) + return 0 + if(element[2]=="text") + continue + else if(element[2]=="num") + continue + else if(element[2]=="list") + continue + else + return 0 + + if(chap[5] != "wires") //if there is only one word,there is no wires + elements_input = splittext( chap[5] ,"^%^") + i=0 + elements = list() + if(debug) + visible_message( "wires[elements_input.len]") + for(var/elem in elements_input) + i=i+1 + if(i>1) + elements.Add(elem) + if(debug) + visible_message( "wires[elements.len]") + if(elements.len>0) + if(debug) + visible_message( "setting wires[elements.len]") + for(var/E in elements) + element = splittext( E ,"=-=") + if(debug) + visible_message( "[E]") + IO = ioa[element[1]] + IO2 = ioa[element[2]] + if(!((element[2]+"=-="+element[1]) in elements)) + return 0 + if(!IO) + return 0 + if(!IO2) + return 0 + if(initial(IO.io_type) != initial(IO2.io_type)) + return 0 + return metalcost \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm new file mode 100644 index 0000000000..46b1f4c759 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm @@ -0,0 +1,26 @@ +// These pins only contain 0 or 1. Null is not allowed. +/datum/integrated_io/boolean + name = "boolean pin" + data = FALSE + +/datum/integrated_io/boolean/ask_for_pin_data(mob/user) // 'Ask' is a bit misleading, acts more like a toggle. + var/new_data = !data + to_chat(user, "You switch the data bit to [data? "true" : "false"].") + write_data_to_pin(new_data) + +/datum/integrated_io/boolean/write_data_to_pin(var/new_data) + if(new_data == FALSE || new_data == TRUE) + data = new_data + holder.on_data_written() + +/datum/integrated_io/boolean/scramble() + write_data_to_pin(rand(FALSE,TRUE)) + push_data() + +/datum/integrated_io/boolean/display_pin_type() + return IC_FORMAT_BOOLEAN + +/datum/integrated_io/boolean/display_data(var/input) + if(data == TRUE) + return "(True)" + return "(False)" \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/char_pin.dm b/code/modules/integrated_electronics/core/special_pins/char_pin.dm new file mode 100644 index 0000000000..5c6a42590b --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/char_pin.dm @@ -0,0 +1,27 @@ +// These pins can only contain a 1 character string or null. +/datum/integrated_io/char + name = "char pin" + +/datum/integrated_io/char/ask_for_pin_data(mob/user) + var/new_data = input("Please type in one character.","[src] char writing") as null|text + if(holder.check_interactivity(user) ) + to_chat(user, "You input [new_data ? "new_data" : "NULL"] into the pin.") + write_data_to_pin(new_data) + +/datum/integrated_io/char/write_data_to_pin(var/new_data) + if(isnull(new_data) || istext(new_data)) + if(length(new_data) > 1) + return + data = new_data + holder.on_data_written() + +// This makes the text go from "A" to "%". +/datum/integrated_io/char/scramble() + if(!is_valid()) + return + var/list/options = list("!","@","#","$","%","^","&","*","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") + data = pick(options) + push_data() + +/datum/integrated_io/char/display_pin_type() + return IC_FORMAT_CHAR \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/color_pin.dm b/code/modules/integrated_electronics/core/special_pins/color_pin.dm new file mode 100644 index 0000000000..703afe5825 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/color_pin.dm @@ -0,0 +1,49 @@ +// These pins can only contain a color (in the form of #FFFFFF) or null. +/datum/integrated_io/color + name = "color pin" + +/datum/integrated_io/color/ask_for_pin_data(mob/user) + var/new_data = input("Please select a color.","[src] color writing") as null|color + if(holder.check_interactivity(user) ) + to_chat(user, "You input a new color into the pin.") + write_data_to_pin(new_data) + +/datum/integrated_io/color/write_data_to_pin(var/new_data) + // Since this is storing the color as a string hex color code, we need to make sure it's actually one. + 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 + 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, + safety_dance++ + + data = new_data // And we can hex + holder.on_data_written() + +// This randomizes the color. +/datum/integrated_io/color/scramble() + if(!is_valid()) + return + var/new_data + for(var/i=1;i<=3;i++) + var/temp_col = "[num2hex(rand(0,255))]" + if(length(temp_col )<2) + temp_col = "0[temp_col]" + new_data += temp_col + data = new_data + push_data() + +/datum/integrated_io/color/display_pin_type() + return IC_FORMAT_COLOR + +/datum/integrated_io/color/display_data(var/input) + if(!isnull(data)) + return "([data])" + return ..() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/dir_pin.dm b/code/modules/integrated_electronics/core/special_pins/dir_pin.dm new file mode 100644 index 0000000000..4ce5597af8 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/dir_pin.dm @@ -0,0 +1,31 @@ +// These pins can only contain directions (1,2,4,8...) or null. +/datum/integrated_io/dir + name = "dir pin" + +/datum/integrated_io/dir/ask_for_pin_data(mob/user) + var/new_data = input("Please type in a valid dir number. \ + Valid dirs are;\n\ + North/Fore = [NORTH],\n\ + South/Aft = [SOUTH],\n\ + East/Starboard = [EAST],\n\ + West/Port = [WEST],\n\ + Northeast = [NORTHEAST],\n\ + Northwest = [NORTHWEST],\n\ + Southeast = [SOUTHEAST],\n\ + Southwest = [SOUTHWEST]","[src] dir writing") as null|num + if(isnum(new_data) && holder.check_interactivity(user) ) + to_chat(user, "You input [new_data] into the pin.") + write_data_to_pin(new_data) + +/datum/integrated_io/dir/write_data_to_pin(var/new_data) + if(isnull(new_data) || new_data in list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)/* + list(UP, DOWN)*/) + data = new_data + holder.on_data_written() + +/datum/integrated_io/dir/display_pin_type() + return IC_FORMAT_DIR + +/datum/integrated_io/dir/display_data(var/input) + if(!isnull(data)) + return "([dir2text(data)])" + return ..() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/list_pin.dm b/code/modules/integrated_electronics/core/special_pins/list_pin.dm new file mode 100644 index 0000000000..f4ad34f4d7 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/list_pin.dm @@ -0,0 +1,148 @@ +// These pins contain a list. Null is not allowed. +/datum/integrated_io/lists + name = "list pin" + data = list() + +/datum/integrated_io/lists/ask_for_pin_data(mob/user) + interact(user) + +/datum/integrated_io/lists/proc/interact(mob/user) + var/list/my_list = data + var/t = "

[src]


" + t += "List length: [my_list.len]
" + t += "\[Refresh\] | " + t += "\[Add\] | " + t += "\[Swap\] | " + t += "\[Clear\]
" + t += "
" + var/i = 0 + for(var/line in my_list) + i++ + t += "#[i] | [display_data(line)] | " + t += "\[Edit\] | " + t += "\[Remove\]
" + user << browse(t, "window=list_pin_[REF(src)];size=500x400") + +/datum/integrated_io/lists/proc/add_to_list(mob/user, var/new_entry) + if(!new_entry && user) + new_entry = ask_for_data_type(user) + if(is_valid(new_entry)) + Add(new_entry) + +/datum/integrated_io/lists/proc/Add(var/new_entry) + var/list/my_list = data + if(my_list.len > IC_MAX_LIST_LENGTH) + my_list.Cut(Start=1,End=2) + my_list.Add(new_entry) + +/datum/integrated_io/lists/proc/remove_from_list_by_position(mob/user, var/position) + var/list/my_list = data + if(!my_list.len) + to_chat(user, "The list is empty, there's nothing to remove.") + return + if(!position) + return + var/target_entry = my_list.Find(position) + if(target_entry) + my_list.Remove(target_entry) + +/datum/integrated_io/lists/proc/remove_from_list(mob/user, var/target_entry) + var/list/my_list = data + if(!my_list.len) + to_chat(user, "The list is empty, there's nothing to remove.") + return + if(!target_entry) + target_entry = input(user, "Which piece of data do you want to remove?", "Remove") as null|anything in my_list + if(holder.check_interactivity(user) && target_entry) + my_list.Remove(target_entry) + +/datum/integrated_io/lists/proc/edit_in_list(mob/user, var/target_entry) + var/list/my_list = data + if(!my_list.len) + to_chat(user, "The list is empty, there's nothing to modify.") + return + if(!target_entry) + target_entry = input(user, "Which piece of data do you want to edit?", "Edit") as null|anything in my_list + if(holder.check_interactivity(user) && target_entry) + var/edited_entry = ask_for_data_type(user, target_entry) + if(edited_entry) + target_entry = edited_entry + +/datum/integrated_io/lists/proc/edit_in_list_by_position(mob/user, var/position) + var/list/my_list = data + if(!my_list.len) + to_chat(user, "The list is empty, there's nothing to modify.") + return + if(!position) + return + var/target_entry = my_list.Find(position) + if(target_entry) + var/edited_entry = ask_for_data_type(user, target_entry) + if(edited_entry) + target_entry = edited_entry + +/datum/integrated_io/lists/proc/swap_inside_list(mob/user, var/first_target, var/second_target) + var/list/my_list = data + if(my_list.len <= 1) + to_chat(user, "The list is empty, or too small to do any meaningful swapping.") + return + if(!first_target) + first_target = input(user, "Which piece of data do you want to swap? (1)", "Swap") as null|anything in my_list + + if(holder.check_interactivity(user) && first_target) + if(!second_target) + second_target = input(user, "Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target + + if(holder.check_interactivity(user) && 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) + +/datum/integrated_io/lists/proc/clear_list(mob/user) + var/list/my_list = data + my_list.Cut() + +/datum/integrated_io/lists/scramble() + var/list/my_list = data + my_list = shuffle(my_list) + push_data() + +/datum/integrated_io/lists/write_data_to_pin(var/new_data) + if(islist(new_data)) + var/list/new_list = new_data + data = new_list.Copy(1,min( IC_MAX_LIST_LENGTH+1, new_list.len )) + holder.on_data_written() + +/datum/integrated_io/lists/display_pin_type() + return IC_FORMAT_LIST + +/datum/integrated_io/lists/Topic(href, href_list) + if(!holder.check_interactivity(usr)) + return + if(..()) + return TRUE + + if(href_list["add"]) + add_to_list(usr) + + if(href_list["swap"]) + swap_inside_list(usr) + + if(href_list["clear"]) + clear_list(usr) + + if(href_list["remove"]) + if(href_list["pos"]) + remove_from_list_by_position(usr, text2num(href_list["pos"])) + else + remove_from_list(usr) + + if(href_list["edit"]) + if(href_list["pos"]) + edit_in_list_by_position(usr, text2num(href_list["pos"])) + else + edit_in_list(usr) + + holder.interact(usr) // Refresh the main UI, + interact(usr) // and the list UI. + diff --git a/code/modules/integrated_electronics/core/special_pins/number_pin.dm b/code/modules/integrated_electronics/core/special_pins/number_pin.dm new file mode 100644 index 0000000000..319ac2de06 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/number_pin.dm @@ -0,0 +1,18 @@ +// These pins can only contain numbers (int and floating point) or null. +/datum/integrated_io/number + name = "number pin" +// data = 0 + +/datum/integrated_io/number/ask_for_pin_data(mob/user) + var/new_data = input("Please type in a number.","[src] number writing") as null|num + if(isnum(new_data) && holder.check_interactivity(user) ) + to_chat(user, "You input [new_data] into the pin.") + write_data_to_pin(new_data) + +/datum/integrated_io/number/write_data_to_pin(var/new_data) + if(isnull(new_data) || isnum(new_data)) + data = new_data + holder.on_data_written() + +/datum/integrated_io/number/display_pin_type() + return IC_FORMAT_NUMBER \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm new file mode 100644 index 0000000000..461965f254 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm @@ -0,0 +1,14 @@ +// These pins only contain weakrefs or null. +/datum/integrated_io/ref + name = "ref pin" + +/datum/integrated_io/ref/ask_for_pin_data(mob/user) // This clears the pin. + write_data_to_pin(null) + +/datum/integrated_io/ref/write_data_to_pin(var/new_data) + if(isnull(new_data) || isweakref(new_data)) + data = new_data + holder.on_data_written() + +/datum/integrated_io/ref/display_pin_type() + return IC_FORMAT_REF \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/special_pins/string_pin.dm b/code/modules/integrated_electronics/core/special_pins/string_pin.dm new file mode 100644 index 0000000000..ee0398413e --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/string_pin.dm @@ -0,0 +1,27 @@ +// These pins can only contain text and null. +/datum/integrated_io/string + name = "string pin" + +/datum/integrated_io/string/ask_for_pin_data(mob/user) + var/new_data = input("Please type in a string.","[src] string writing") as null|text + if(holder.check_interactivity(user) ) + to_chat(user, "You input [new_data ? "[new_data]" : "NULL"] into the pin.") + write_data_to_pin(new_data) + +/datum/integrated_io/string/write_data_to_pin(var/new_data) + if(isnull(new_data) || istext(new_data)) + data = new_data + holder.on_data_written() + +// This makes the text go "from this" to "#G&*!HD$%L" +/datum/integrated_io/string/scramble() + if(!is_valid()) + return + var/list/options = list("!","@","#","$","%","^","&","*","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") + var/new_data = "" + for(var/i in 1 to length(data)) + new_data += pick(options) + push_data() + +/datum/integrated_io/string/display_pin_type() + return IC_FORMAT_STRING diff --git a/code/modules/integrated_electronics/core/wirer.dm b/code/modules/integrated_electronics/core/wirer.dm new file mode 100644 index 0000000000..c1caba00cd --- /dev/null +++ b/code/modules/integrated_electronics/core/wirer.dm @@ -0,0 +1,102 @@ +#define WIRE "wire" +#define WIRING "wiring" +#define UNWIRE "unwire" +#define UNWIRING "unwiring" + +/obj/item/device/integrated_electronics/wirer + name = "circuit wirer" + desc = "It's a small wiring tool, with a wire roll, electric soldering iron, wire cutter, and more in one package. \ + The wires used are generally useful for small electronics, such as circuitboards and breadboards, as opposed to larger wires \ + used for power or data transmission." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "wirer-wire" + flags_1 = CONDUCT_1 + w_class = WEIGHT_CLASS_SMALL + var/datum/integrated_io/selected_io = null + var/mode = WIRE + +/obj/item/device/integrated_electronics/wirer/update_icon() + icon_state = "wirer-[mode]" + +/obj/item/device/integrated_electronics/wirer/proc/wire(var/datum/integrated_io/io, mob/user) + if(!io.holder.assembly) + to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.") + return + if(mode == WIRE) + selected_io = io + to_chat(user, "You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.") + mode = WIRING + update_icon() + else if(mode == WIRING) + if(io == selected_io) + to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") + return + if(io.io_type != selected_io.io_type) + to_chat(user, "Those two types of channels are incompatable. The first is a [selected_io.io_type], \ + while the second is a [io.io_type].") + return + if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) + to_chat(user, "Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.") + return + selected_io.linked |= io + io.linked |= selected_io + + to_chat(user, "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].") + mode = WIRE + update_icon() + selected_io.holder.interact(user) // This is to update the UI. + selected_io = null + + else if(mode == UNWIRE) + selected_io = io + if(!io.linked.len) + to_chat(user, "There is nothing connected to \the [selected_io] data channel.") + selected_io = null + return + to_chat(user, "You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel.") + mode = UNWIRING + update_icon() + return + + else if(mode == UNWIRING) + if(io == selected_io) + to_chat(user, "You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \ + the same pin is rather moot.") + return + if(selected_io in io.linked) + io.linked.Remove(selected_io) + selected_io.linked.Remove(io) + to_chat(user, "You disconnect \the [selected_io.holder]'s [selected_io.name] from \ + \the [io.holder]'s [io.name].") + selected_io.holder.interact(user) // This is to update the UI. + selected_io = null + mode = UNWIRE + update_icon() + else + to_chat(user, "\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \ + [io.name] are not connected.") + return + +/obj/item/device/integrated_electronics/wirer/attack_self(mob/user) + switch(mode) + if(WIRE) + mode = UNWIRE + if(WIRING) + if(selected_io) + to_chat(user, "You decide not to wire the data channel.") + selected_io = null + mode = WIRE + if(UNWIRE) + mode = WIRE + if(UNWIRING) + if(selected_io) + to_chat(user, "You decide not to disconnect the data channel.") + selected_io = null + mode = UNWIRE + update_icon() + to_chat(user, "You set \the [src] to [mode].") + +#undef WIRE +#undef WIRING +#undef UNWIRE +#undef UNWIRING \ No newline at end of file diff --git a/code/modules/integrated_electronics/passive/passive.dm b/code/modules/integrated_electronics/passive/passive.dm new file mode 100644 index 0000000000..02f03d48d7 --- /dev/null +++ b/code/modules/integrated_electronics/passive/passive.dm @@ -0,0 +1,7 @@ +// '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() + outputs = list() + activators = list() + power_draw_idle = 0 + power_draw_per_use = 0 \ No newline at end of file diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm new file mode 100644 index 0000000000..f99912a149 --- /dev/null +++ b/code/modules/integrated_electronics/passive/power.dm @@ -0,0 +1,126 @@ + +/obj/item/integrated_circuit/passive/power + name = "power thingy" + desc = "Does power stuff." + complexity = 5 + origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2, TECH_DATA = 2) + category_text = "Power - Passive" + +/obj/item/integrated_circuit/passive/power/proc/make_energy() + return + +// 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." + extended_desc = "The cell generates 1W of energy per second 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 + var/max_power = 30 + +/obj/item/integrated_circuit/passive/power/solar_cell/make_energy() + var/turf/T = get_turf(src) + var/light_amount = T ? T.get_lumcount() : 0 + var/adjusted_power = max(max_power * light_amount, 0) + adjusted_power = round(adjusted_power, 0.1) + if(adjusted_power) + if(assembly) + assembly.give_power(adjusted_power) + +/obj/item/integrated_circuit/passive/power/starter + name = "starter" + desc = "This tiny circuit will send pulse right after device is turned on. Or when power is restored." + icon_state = "led" + complexity = 1 + activators = list("pulse out" = IC_PINTYPE_PULSE_OUT) + origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_DATA = 2) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/is_charge = FALSE + +/obj/item/integrated_circuit/passive/power/starter/make_energy() + if(assembly.battery) + if(assembly.battery.charge) + if(!is_charge) + activate_pin(1) + is_charge = TRUE + else + is_charge = FALSE + else + is_charge=FALSE + return FALSE + +// 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." + w_class = WEIGHT_CLASS_SMALL + extended_desc = "The siphon generates 250W 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." + icon_state = "power_relay" + complexity = 7 + origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_DATA = 2) + spawn_flags = IC_SPAWN_RESEARCH + var/power_amount = 150 +//fuel cell + +/obj/item/integrated_circuit/passive/power/chemical_cell + 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,\ + ethanol, nutriments and blood , in order of decreasing efficiency. It will consume fuel only if the battery can take more energy." + container_type = OPENCONTAINER_1 + complexity = 4 + inputs = list() + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF) + activators = list() + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + var/volume = 60 + var/list/fuel = list("plasma" = 10000, "welding_fuel" = 3000, "carbon" = 2000, "ethanol"= 2000, "nutriment" =1600, "blood" = 1000) + +/obj/item/integrated_circuit/passive/power/chemical_cell/New() + ..() + create_reagents(volume) + +/obj/item/integrated_circuit/passive/power/chemical_cell/interact(mob/user) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + ..() + +/obj/item/integrated_circuit/passive/power/chemical_cell/on_reagent_change() + set_pin_data(IC_OUTPUT, 1, reagents.total_volume) + push_data() + +/obj/item/integrated_circuit/passive/power/chemical_cell/make_energy() + if(assembly) + if(assembly.battery) + for(var/I in fuel) + if((assembly.battery.maxcharge-assembly.battery.charge) / GLOB.CELLRATE > fuel[I]) + if(reagents.remove_reagent(I, 1)) + assembly.give_power(fuel[I]) + + +// 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!" + w_class = WEIGHT_CLASS_BULKY + extended_desc = "The siphon generates 2 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." + icon_state = "power_relay" + complexity = 15 + origin_tech = list(TECH_POWER = 6, TECH_ENGINEERING = 5, TECH_DATA = 4) + spawn_flags = IC_SPAWN_RESEARCH + power_amount = 1000 + +/obj/item/integrated_circuit/passive/power/relay/make_energy() + if(!assembly) + return + var/area/A = get_area(src) + if(A) + if(A.powered(EQUIP) && assembly.give_power(power_amount)) + A.use_power(power_amount, EQUIP) + // give_power() handles CELLRATE on its own. diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm new file mode 100644 index 0000000000..7dec779f83 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm @@ -0,0 +1,319 @@ +//These circuits do simple math. +/obj/item/integrated_circuit/arithmetic + complexity = 1 + inputs = list( + "A" = IC_PINTYPE_NUMBER, + "B" = IC_PINTYPE_NUMBER, + "C" = IC_PINTYPE_NUMBER, + "D" = IC_PINTYPE_NUMBER, + "E" = IC_PINTYPE_NUMBER, + "F" = IC_PINTYPE_NUMBER, + "G" = IC_PINTYPE_NUMBER, + "H" = IC_PINTYPE_NUMBER + ) + outputs = list("result" = IC_PINTYPE_NUMBER) + activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT) + category_text = "Arithmetic" + power_draw_per_use = 5 // Math is pretty cheap. + +// +Adding+ // + +/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;
\ + result = ((((A + B) + C) + D) ... ) and so on, until all pins have been added. \ + Null pins are ignored." + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/addition/do_work() + var/result = 0 + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.pull_data() + if(isnum(I.data)) + result = result + I.data + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// -Subtracting- // + +/obj/item/integrated_circuit/arithmetic/subtraction + name = "subtraction circuit" + desc = "This circuit can subtract numbers." + extended_desc = "The order that the calculation goes is;
\ + result = ((((A - B) - C) - D) ... ) and so on, until all pins have been subtracted. \ + Null pins are ignored. Pin A must be a number or the circuit will not function." + icon_state = "subtraction" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/subtraction/do_work() + var/datum/integrated_io/A = inputs[1] + if(!isnum(A.data)) + return + var/result = A.data + + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + if(I == A) + continue + I.pull_data() + if(isnum(I.data)) + result = result - I.data + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// *Multiply* // + +/obj/item/integrated_circuit/arithmetic/multiplication + name = "multiplication circuit" + desc = "This circuit can multiply numbers." + extended_desc = "The order that the calculation goes is;
\ + result = ((((A * B) * C) * D) ... ) and so on, until all pins have been multiplied. \ + Null pins are ignored. Pin A must be a number or the circuit will not function." + icon_state = "multiplication" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + + +/obj/item/integrated_circuit/arithmetic/multiplication/do_work() + var/datum/integrated_io/A = inputs[1] + if(!isnum(A.data)) + return + var/result = A.data + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + if(I == A) + continue + I.pull_data() + if(isnum(I.data)) + result = result * I.data + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// /Division/ // + +/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;
\ + result = ((((A / B) / C) / D) ... ) and so on, until all pins have been divided. \ + Null pins, and pins containing 0, are ignored. Pin A must be a number or the circuit will not function." + icon_state = "division" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/division/do_work() + var/datum/integrated_io/A = inputs[1] + if(!isnum(A.data)) + return + var/result = A.data + + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + if(I == A) + continue + I.pull_data() + if(isnum(I.data) && I.data != 0) //No runtimes here. + result = result / I.data + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +//^ Exponent ^// + +/obj/item/integrated_circuit/arithmetic/exponent + name = "exponent circuit" + desc = "Outputs A to the power of B." + icon_state = "exponent" + inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/exponent/do_work() + var/result = 0 + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + if(isnum(A.data) && isnum(B.data)) + result = A.data ** B.data + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// +-Sign-+ // + +/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." + icon_state = "sign" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/sign/do_work() + var/result = 0 + var/datum/integrated_io/A = inputs[1] + if(isnum(A.data)) + if(A.data > 0) + result = 1 + else if (A.data < 0) + result = -1 + else + result = 0 + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Round // + +/obj/item/integrated_circuit/arithmetic/round + name = "round circuit" + desc = "Rounds A to the nearest B multiple of A." + extended_desc = "If B is not given a number, it will output the floor of A instead." + icon_state = "round" + inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/round/do_work() + var/result = 0 + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + if(isnum(A.data)) + if(isnum(B.data) && B.data != 0) + result = round(A.data, B.data) + else + result = round(A.data) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Absolute // + +/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." + icon_state = "absolute" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/absolute/do_work() + var/result = 0 + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.pull_data() + if(isnum(I.data)) + result = abs(I.data) + + set_pin_data(IC_OUTPUT, 1, result) + 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." + icon_state = "average" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/average/do_work() + var/result = 0 + var/inputs_used = 0 + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.pull_data() + if(isnum(I.data)) + inputs_used++ + result = result + I.data + + if(inputs_used) + result = result / inputs_used + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// 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." + icon_state = "pi" + inputs = list() + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/pi/do_work() + set_pin_data(IC_OUTPUT, 1, 3.14159) + push_data() + activate_pin(2) + +// 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." + 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 strictly required." + icon_state = "random" + inputs = list("L" = IC_PINTYPE_NUMBER,"H" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/random/do_work() + var/result = 0 + var/L = get_pin_data(IC_INPUT, 1) + var/H = get_pin_data(IC_INPUT, 2) + + if(isnum(L) && isnum(H)) + result = rand(L, H) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Square Root // + +/obj/item/integrated_circuit/arithmetic/square_root + name = "square root circuit" + desc = "This outputs the square root of a number you put in." + icon_state = "square_root" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/square_root/do_work() + var/result = 0 + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] + I.pull_data() + if(isnum(I.data)) + result = sqrt(I.data) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// % Modulo % // + +/obj/item/integrated_circuit/arithmetic/modulo + name = "modulo circuit" + desc = "Gets the remainder of A / B." + icon_state = "modulo" + inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/arithmetic/modulo/do_work() + var/result = 0 + var/A = get_pin_data(IC_INPUT, 1) + var/B = get_pin_data(IC_INPUT, 2) + if(isnum(A) && isnum(B) && B != 0) + result = A % B + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm new file mode 100644 index 0000000000..373a059f45 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/converters.dm @@ -0,0 +1,320 @@ +//These circuits convert one variable to another. +/obj/item/integrated_circuit/converter + complexity = 2 + inputs = list("input") + outputs = list("output") + activators = list("convert" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) + category_text = "Converter" + power_draw_per_use = 10 + +/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." + icon_state = "num-string" + inputs = list("input" = IC_PINTYPE_NUMBER) + outputs = list("output" = IC_PINTYPE_STRING) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/num2text/do_work() + var/result = null + pull_data() + var/incoming = get_pin_data(IC_INPUT, 1) + if(!isnull(incoming)) + result = num2text(incoming) + else if(!incoming) + result = "0" + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/text2num + name = "string to number" + 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) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/text2num/do_work() + var/result = null + pull_data() + var/incoming = get_pin_data(IC_INPUT, 1) + if(!isnull(incoming)) + result = text2num(incoming) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/ref2text + name = "reference to string" + desc = "This circuit can convert a reference to something else to a string, specifically the name of that reference." + icon_state = "ref-string" + inputs = list("input" = IC_PINTYPE_REF) + outputs = list("output" = IC_PINTYPE_STRING) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/ref2text/do_work() + var/result = null + pull_data() + var/atom/A = get_pin_data(IC_INPUT, 1) + if(A && istype(A)) + result = A.name + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/refcode + name = "reference encoder" + desc = "This circuit can encode a reference into a string, which can then be read by an EPV2 circuit." + icon_state = "ref-string" + inputs = list("input" = IC_PINTYPE_REF) + outputs = list("output" = IC_PINTYPE_STRING) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/refcode/do_work() + var/result = null + pull_data() + var/atom/A = get_pin_data(IC_INPUT, 1) + if(A && istype(A)) + result = strtohex(XorEncrypt(REF(A),SScircuit.cipherkey)) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/refdecode + name = "reference decoder" + desc = "This circuit can convert a encoded reference to actual reference." + icon_state = "ref-string" + inputs = list("input" = IC_PINTYPE_STRING) + outputs = list("output" = IC_PINTYPE_REF) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/dec + +/obj/item/integrated_circuit/converter/refdecode/do_work() + pull_data() + dec=XorEncrypt(hextostr(get_pin_data(IC_INPUT, 1)),SScircuit.cipherkey) + set_pin_data(IC_OUTPUT, 1, WEAKREF(locate( dec ))) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/lowercase + name = "lowercase string converter" + desc = "this will cause a string to come out in all lowercase." + icon_state = "lowercase" + inputs = list("input" = IC_PINTYPE_STRING) + outputs = list("output" = IC_PINTYPE_STRING) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/lowercase/do_work() + var/result = null + pull_data() + var/incoming = get_pin_data(IC_INPUT, 1) + if(!isnull(incoming)) + result = lowertext(incoming) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/uppercase + name = "uppercase string converter" + desc = "THIS WILL CAUSE A STRING TO COME OUT IN ALL UPPERCASE." + icon_state = "uppercase" + inputs = list("input" = IC_PINTYPE_STRING) + outputs = list("output" = IC_PINTYPE_STRING) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/uppercase/do_work() + var/result = null + pull_data() + var/incoming = get_pin_data(IC_INPUT, 1) + if(!isnull(incoming)) + result = uppertext(incoming) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/concatenator + name = "concatenator" + desc = "This joins many strings together to get one big string." + complexity = 4 + inputs = list( + "A" = IC_PINTYPE_STRING, + "B" = IC_PINTYPE_STRING, + "C" = IC_PINTYPE_STRING, + "D" = IC_PINTYPE_STRING, + "E" = IC_PINTYPE_STRING, + "F" = IC_PINTYPE_STRING, + "G" = IC_PINTYPE_STRING, + "H" = IC_PINTYPE_STRING + ) + outputs = list("result" = IC_PINTYPE_STRING) + activators = list("concatenate" = IC_PINTYPE_PULSE_IN, "on concatenated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/concatenator/do_work() + var/result = null + for(var/datum/integrated_io/I in inputs) + I.pull_data() + if(!isnull(I.data)) + result = result + I.data + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +/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. \ + The index splits the string after the given index, including spaces. So 'a person' with an index of '3' \ + will split into 'a p' and 'erson'." + complexity = 4 + inputs = list( + "string to split" = IC_PINTYPE_STRING, + "index" = IC_PINTYPE_NUMBER, + ) + outputs = list( + "before split" = IC_PINTYPE_STRING, + "after split" = IC_PINTYPE_STRING + ) + activators = list("separate" = IC_PINTYPE_PULSE_IN, "on separated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/separator/do_work() + var/text = get_pin_data(IC_INPUT, 1) + var/index = get_pin_data(IC_INPUT, 2) + + var/split = min(index+1, length(text)) + + var/before_text = copytext(text, 1, split) + var/after_text = copytext(text, split, 0) + + set_pin_data(IC_OUTPUT, 1, before_text) + set_pin_data(IC_OUTPUT, 2, after_text) + push_data() + + activate_pin(2) + +/obj/item/integrated_circuit/converter/findstring + name = "find text" + desc = "This gives position of sample in the string. Or returns 0." + 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 + inputs = list( + "string" = IC_PINTYPE_STRING, + "sample" = IC_PINTYPE_STRING, + ) + outputs = list( + "position" = IC_PINTYPE_NUMBER + ) + activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + + + +/obj/item/integrated_circuit/converter/findstring/do_work() + + set_pin_data(IC_OUTPUT, 1, findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) ) + push_data() + + activate_pin(2) + +/obj/item/integrated_circuit/converter/exploders + name = "string exploder" + desc = "This splits a single string into a list of strings." + extended_desc = "This circuit splits a given string into a list of strings based on the string and given delimiter. \ + For example, 'eat this burger',' ' will be converted to list('eat','this','burger')." + complexity = 4 + inputs = list( + "string to split" = IC_PINTYPE_STRING, + "delimiter" = IC_PINTYPE_STRING, + ) + outputs = list( + "list" = IC_PINTYPE_LIST + ) + activators = list("separate" = IC_PINTYPE_PULSE_IN, "on separated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/exploders/do_work() + var/strin = get_pin_data(IC_INPUT, 1) + var/sample = get_pin_data(IC_INPUT, 2) + set_pin_data(IC_OUTPUT, 1, splittext( strin ,sample )) + push_data() + + 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 = ToDegrees(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 = ToRadians(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." + complexity = 4 + inputs = list( + "X1" = IC_PINTYPE_NUMBER, + "Y1" = IC_PINTYPE_NUMBER, + "X2" = IC_PINTYPE_NUMBER, + "Y2" = IC_PINTYPE_NUMBER + ) + outputs = list( + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER + ) + activators = list("compute rel coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work() + var/x1 = get_pin_data(IC_INPUT, 1) + var/y1 = get_pin_data(IC_INPUT, 2) + + var/x2 = get_pin_data(IC_INPUT, 3) + var/y2 = get_pin_data(IC_INPUT, 4) + + if(!isnull(x1) && !isnull(y1) && !isnull(x2) && !isnull(y2)) + set_pin_data(IC_OUTPUT, 1, x1 - x2) + set_pin_data(IC_OUTPUT, 2, y1 - y2) + + push_data() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm new file mode 100644 index 0000000000..28fc45cfea --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/data_transfer.dm @@ -0,0 +1,146 @@ +/obj/item/integrated_circuit/transfer + category_text = "Data Transfer" + power_draw_per_use = 2 + +/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." + 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 + icon_state = "mux2" + inputs = list("input selection" = IC_PINTYPE_NUMBER) + outputs = list("output" = IC_PINTYPE_ANY) + activators = list("select" = IC_PINTYPE_PULSE_IN, "on select" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + var/number_of_inputs = 2 + +/obj/item/integrated_circuit/transfer/multiplexer/New() + for(var/i = 1 to number_of_inputs) + inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. +// inputs += "input [i]" + complexity = number_of_inputs + ..() + desc += " It has [number_of_inputs] input pins." + extended_desc += " This multiplexer has a range from 1 to [inputs.len - 1]." + +/obj/item/integrated_circuit/transfer/multiplexer/do_work() + var/input_index = get_pin_data(IC_INPUT, 1) + + if(!isnull(input_index) && (input_index >= 1 && input_index < inputs.len)) + set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1)) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/transfer/multiplexer/medium + name = "four multiplexer" + number_of_inputs = 4 + icon_state = "mux4" + +/obj/item/integrated_circuit/transfer/multiplexer/large + name = "eight multiplexer" + number_of_inputs = 8 + w_class = WEIGHT_CLASS_SMALL + icon_state = "mux8" + +/obj/item/integrated_circuit/transfer/multiplexer/huge + name = "sixteen multiplexer" + icon_state = "mux16" + w_class = WEIGHT_CLASS_SMALL + number_of_inputs = 16 + +/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." + 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 + icon_state = "dmux2" + inputs = list("output selection" = IC_PINTYPE_NUMBER, "input" = IC_PINTYPE_ANY) + outputs = list() + activators = list("select" = IC_PINTYPE_PULSE_IN, "on select" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + var/number_of_outputs = 2 + +/obj/item/integrated_circuit/transfer/demultiplexer/New() + for(var/i = 1 to number_of_outputs) + // outputs += "output [i]" + outputs["output [i]"] = IC_PINTYPE_ANY + complexity = number_of_outputs + + ..() + desc += " It has [number_of_outputs] output pins." + extended_desc += " This demultiplexer has a range from 1 to [outputs.len]." + +/obj/item/integrated_circuit/transfer/demultiplexer/do_work() + var/output_index = get_pin_data(IC_INPUT, 1) + for(var/i = 1 to outputs.len) + set_pin_data(IC_OUTPUT, i, i == output_index ? get_pin_data(IC_INPUT, 2) : null) + + activate_pin(2) + +/obj/item/integrated_circuit/transfer/demultiplexer/medium + name = "four demultiplexer" + icon_state = "dmux4" + number_of_outputs = 4 + +/obj/item/integrated_circuit/transfer/demultiplexer/large + name = "eight demultiplexer" + icon_state = "dmux8" + w_class = WEIGHT_CLASS_SMALL + number_of_outputs = 8 + +/obj/item/integrated_circuit/transfer/demultiplexer/huge + name = "sixteen demultiplexer" + icon_state = "dmux16" + w_class = WEIGHT_CLASS_SMALL + number_of_outputs = 16 + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer + name = "two pulse demultiplexer" + desc = "Selector switch to choose the pin to be activated by number." + extended_desc = "The first input pin is used to select which of the pulse out pins will be activated after activation of the circuit. \ + If the output selection is outside the valid range then no output is given." + complexity = 2 + icon_state = "dmux2" + inputs = list("output selection" = IC_PINTYPE_NUMBER) + outputs = list() + activators = list("select" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + var/number_of_outputs = 2 + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer/New() + for(var/i = 1 to number_of_outputs) + // outputs += "output [i]" + activators["output [i]"] = IC_PINTYPE_PULSE_OUT + complexity = number_of_outputs + + ..() + desc += " It has [number_of_outputs] output pins." + extended_desc += " This pulse demultiplexer has a range from 1 to [activators.len - 1]." + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer/do_work() + var/output_index = get_pin_data(IC_INPUT, 1) + + if(output_index == Clamp(output_index, 1, number_of_outputs)) + activate_pin(round(output_index + 1 ,1)) + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer/medium + name = "four pulse demultiplexer" + icon_state = "dmux4" + number_of_outputs = 4 + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer/large + name = "eight pulse demultiplexer" + icon_state = "dmux8" + w_class = WEIGHT_CLASS_SMALL + number_of_outputs = 8 + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer/huge + name = "sixteen pulse demultiplexer" + icon_state = "dmux16" + w_class = WEIGHT_CLASS_SMALL + number_of_outputs = 16 \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm new file mode 100644 index 0000000000..eef002c4cf --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -0,0 +1,806 @@ +/obj/item/integrated_circuit/input + var/can_be_asked_input = 0 + category_text = "Input" + power_draw_per_use = 5 + +/obj/item/integrated_circuit/input/proc/ask_for_input(mob/user) + return + +/obj/item/integrated_circuit/input/button + name = "button" + desc = "This tiny button must do something, right?" + icon_state = "button" + complexity = 1 + can_be_asked_input = 1 + inputs = list() + outputs = list() + activators = list("on pressed" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/input/button/ask_for_input(mob/user) //Bit misleading name for this specific use. + to_chat(user, "You press the button labeled '[src]'.") + activate_pin(1) + +/obj/item/integrated_circuit/input/toggle_button + name = "toggle button" + desc = "It toggles on, off, on, off..." + icon_state = "toggle_button" + complexity = 1 + can_be_asked_input = 1 + inputs = list() + outputs = list("on" = IC_PINTYPE_BOOLEAN) + activators = list("on toggle" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/input/toggle_button/ask_for_input(mob/user) // Ditto. + set_pin_data(IC_OUTPUT, 1, !get_pin_data(IC_OUTPUT, 1)) + push_data() + activate_pin(1) + to_chat(user, "You toggle the button labeled '[src]' [get_pin_data(IC_OUTPUT, 1) ? "on" : "off"].") + +/obj/item/integrated_circuit/input/numberpad + name = "number pad" + desc = "This small number pad allows someone to input a number into the system." + icon_state = "numberpad" + complexity = 2 + can_be_asked_input = 1 + inputs = list() + outputs = list("number entered" = IC_PINTYPE_NUMBER) + activators = list("on entered" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + +/obj/item/integrated_circuit/input/numberpad/ask_for_input(mob/user) + var/new_input = input(user, "Enter a number, please.","Number pad") as null|num + if(isnum(new_input) && user.IsAdvancedToolUser()) + set_pin_data(IC_OUTPUT, 1, new_input) + push_data() + activate_pin(1) + +/obj/item/integrated_circuit/input/textpad + name = "text pad" + desc = "This small text pad allows someone to input a string into the system." + icon_state = "textpad" + complexity = 2 + can_be_asked_input = 1 + inputs = list() + outputs = list("string entered" = IC_PINTYPE_STRING) + activators = list("on entered" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + +/obj/item/integrated_circuit/input/textpad/ask_for_input(mob/user) + var/new_input = input(user, "Enter some words, please.","Number pad") as null|text + if(istext(new_input) && user.IsAdvancedToolUser()) + set_pin_data(IC_OUTPUT, 1, new_input) + push_data() + activate_pin(1) + +/obj/item/integrated_circuit/input/med_scanner + name = "integrated medical analyser" + desc = "A very small version of the common medical analyser. This allows the machine to know how healthy someone is." + icon_state = "medscan" + complexity = 4 + inputs = list("\ target") + outputs = list( + "total health %" = IC_PINTYPE_NUMBER, + "total missing health" = IC_PINTYPE_NUMBER + ) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + power_draw_per_use = 40 + +/obj/item/integrated_circuit/input/med_scanner/do_work() + var/mob/living/carbon/human/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living/carbon/human) + if(!istype(H)) //Invalid input + return + if(H.Adjacent(get_turf(src))) // Like normal analysers, it can't be used at range. + var/total_health = round(H.health/H.getMaxHealth(), 0.01)*100 + var/missing_health = H.getMaxHealth() - H.health + + set_pin_data(IC_OUTPUT, 1, total_health) + set_pin_data(IC_OUTPUT, 2, missing_health) + + push_data() + activate_pin(2) +/* +/obj/item/integrated_circuit/input/pressure_plate + name = "pressure plate" + desc = "Electronic plate with a scanner, that could retrieve references to things,that was put onto the machine" + icon_state = "pressure_plate" + complexity = 4 + inputs = list() + outputs = list("laid" = IC_PINTYPE_REF, "removed" = IC_PINTYPE_REF) + activators = list("laid" = IC_PINTYPE_PULSE_OUT, "removed" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + power_draw_per_use = 40 + var/list/cont + +/obj/item/integrated_circuit/input/pressure_plate/New() + ..() + processing_objects |= src + +/obj/item/integrated_circuit/input/pressure_plate/Destroy() + processing_objects -= src + +/obj/item/integrated_circuit/input/pressure_plate/process() + var/list/newcont + var/turf/T = get_turf(src) + newcont = T.contents + var/list/U = cont & newcont + for(var/laid in U) + if(!(laid in cont)) + var/datum/integrated_io/O = outputs[1] + O.data = WEAKREF(laid) + O.push_data() + activate_pin(1) + break + for(var/removed in U) + if(!(removed in newcont)) + var/datum/integrated_io/O = outputs[2] + O.data = WEAKREF(removed) + O.push_data() + activate_pin(2) + break + cont = newcont + +*/ + + +/obj/item/integrated_circuit/input/adv_med_scanner + name = "integrated advanced medical analyser" + desc = "A very small version of the medbot's medical analyser. This allows the machine to know how healthy someone is. \ + This type is much more precise, allowing the machine to know much more about the target than a normal analyzer." + icon_state = "medscan_adv" + complexity = 12 + inputs = list("\ target") + outputs = list( + "total health %" = IC_PINTYPE_NUMBER, + "total missing health" = IC_PINTYPE_NUMBER, + "brute damage" = IC_PINTYPE_NUMBER, + "burn damage" = IC_PINTYPE_NUMBER, + "tox damage" = IC_PINTYPE_NUMBER, + "oxy damage" = IC_PINTYPE_NUMBER, + "clone damage" = IC_PINTYPE_NUMBER + ) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 4) + power_draw_per_use = 80 + +/obj/item/integrated_circuit/input/adv_med_scanner/do_work() + var/mob/living/carbon/human/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living/carbon/human) + if(!istype(H)) //Invalid input + return + if(H in view(get_turf(H))) // Like medbot's analyzer it can be used in range.. + var/total_health = round(H.health/H.getMaxHealth(), 0.01)*100 + var/missing_health = H.getMaxHealth() - H.health + + set_pin_data(IC_OUTPUT, 1, total_health) + set_pin_data(IC_OUTPUT, 2, missing_health) + set_pin_data(IC_OUTPUT, 3, H.getBruteLoss()) + set_pin_data(IC_OUTPUT, 4, H.getFireLoss()) + set_pin_data(IC_OUTPUT, 5, H.getToxLoss()) + set_pin_data(IC_OUTPUT, 6, H.getOxyLoss()) + set_pin_data(IC_OUTPUT, 7, H.getCloneLoss()) + + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/plant_scanner + name = "integrated plant analyzer" + desc = "A very small version of the plant analyser. This allows the machine to know all valuable params of plants in trays. \ + it can't scan seeds and fruits.only plants." + icon_state = "medscan_adv" + complexity = 12 + inputs = list("\ target") + outputs = list( + "plant type" = IC_PINTYPE_STRING, + "age" = IC_PINTYPE_NUMBER, + "potency" = IC_PINTYPE_NUMBER, + "yield" = IC_PINTYPE_NUMBER, + "Maturation speed" = IC_PINTYPE_NUMBER, + "Production speed" = IC_PINTYPE_NUMBER, + "Endurance" = IC_PINTYPE_NUMBER, + "Lifespan" = IC_PINTYPE_NUMBER, + "Weed Growth Rate" = IC_PINTYPE_NUMBER, + "Weed Vulnerability" = IC_PINTYPE_NUMBER, + "Weed level" = IC_PINTYPE_NUMBER, + "Pest level" = IC_PINTYPE_NUMBER, + "Toxicity level" = IC_PINTYPE_NUMBER, + "Water level" = IC_PINTYPE_NUMBER, + "Nutrition level" = IC_PINTYPE_NUMBER, + "harvest" = IC_PINTYPE_NUMBER, + "dead" = IC_PINTYPE_NUMBER , + "plant health" = IC_PINTYPE_NUMBER + ) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 4) + power_draw_per_use = 10 + +/obj/item/integrated_circuit/input/plant_scanner/do_work() + var/obj/machinery/hydroponics/H = get_pin_data_as_type(IC_INPUT, 1, /obj/machinery/hydroponics) + if(!istype(H)) //Invalid input + return + for(var/i=1, i<=outputs.len, i++) + set_pin_data(IC_OUTPUT, i, null) + if(H in view(get_turf(H))) // Like medbot's analyzer it can be used in range.. + if(H.myseed) + set_pin_data(IC_OUTPUT, 1, H.myseed.plantname) + set_pin_data(IC_OUTPUT, 2, H.age) + set_pin_data(IC_OUTPUT, 3, H.myseed.potency) + set_pin_data(IC_OUTPUT, 4, H.myseed.yield) + set_pin_data(IC_OUTPUT, 5, H.myseed.maturation) + set_pin_data(IC_OUTPUT, 6, H.myseed.production) + set_pin_data(IC_OUTPUT, 7, H.myseed.endurance) + set_pin_data(IC_OUTPUT, 8, H.myseed.lifespan) + set_pin_data(IC_OUTPUT, 9, H.myseed.weed_rate) + set_pin_data(IC_OUTPUT, 10, H.myseed.weed_chance) + set_pin_data(IC_OUTPUT, 11, H.weedlevel) + set_pin_data(IC_OUTPUT, 12, H.pestlevel) + set_pin_data(IC_OUTPUT, 13, H.toxic) + set_pin_data(IC_OUTPUT, 14, H.waterlevel) + set_pin_data(IC_OUTPUT, 15, H.nutrilevel) + set_pin_data(IC_OUTPUT, 16, H.harvest) + set_pin_data(IC_OUTPUT, 17, H.dead) + set_pin_data(IC_OUTPUT, 18, H.plant_health) + + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/gene_scanner + name = "gene scanner" + desc = "This circuit will scan plant for traits and reagent genes." + extended_desc = "This allows the machine to scan plants in trays for reagent and trait genes. \ + it can't scan seeds and fruits.only plants." + inputs = list( + "\ target" = IC_PINTYPE_REF + ) + outputs = list( + "traits" = IC_PINTYPE_LIST, + "reagents" = IC_PINTYPE_LIST + ) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) + icon_state = "medscan_adv" + spawn_flags = IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/input/gene_scanner/do_work() + var/list/gtraits = list() + var/list/greagents = list() + var/obj/machinery/hydroponics/H = get_pin_data_as_type(IC_INPUT, 1, /obj/machinery/hydroponics) + if(!istype(H)) //Invalid input + return + for(var/i=1, i<=outputs.len, i++) + set_pin_data(IC_OUTPUT, i, null) + if(H in view(get_turf(H))) // Like medbot's analyzer it can be used in range.. + if(H.myseed) + for(var/datum/plant_gene/reagent/G in H.myseed.genes) + greagents.Add(G.get_name()) + + for(var/datum/plant_gene/trait/G in H.myseed.genes) + gtraits.Add(G.get_name()) + + set_pin_data(IC_OUTPUT, 1, gtraits) + set_pin_data(IC_OUTPUT, 2, greagents) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/input/examiner + name = "examiner" + desc = "It' s a little machine vision system. It can return the name, description, distance, \ + relative coordinates, total amount of reagents, and maximum amount of reagents of the referenced object." + icon_state = "video_camera" + complexity = 6 + inputs = list("\ target" = IC_PINTYPE_REF) + outputs = list( + "name" = IC_PINTYPE_STRING, + "description" = IC_PINTYPE_STRING, + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER, + "distance" = IC_PINTYPE_NUMBER, + "max reagents" = IC_PINTYPE_NUMBER, + "amount of reagents" = IC_PINTYPE_NUMBER, + ) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT, "not scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 4) + power_draw_per_use = 80 + +/obj/item/integrated_circuit/input/examiner/do_work() + var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/turf/T = get_turf(src) + if(!istype(H)) //Invalid input + return + + if(H in view(T)) // This is a camera. It can't examine thngs,that it can't see. + + + set_pin_data(IC_OUTPUT, 1, H.name) + set_pin_data(IC_OUTPUT, 2, H.desc) + set_pin_data(IC_OUTPUT, 3, H.x-T.x) + set_pin_data(IC_OUTPUT, 4, H.y-T.y) + set_pin_data(IC_OUTPUT, 5, sqrt((H.x-T.x)*(H.x-T.x)+ (H.y-T.y)*(H.y-T.y))) + var/mr = 0 + var/tr = 0 + if(H.reagents) + mr = H.reagents.maximum_volume + tr = H.reagents.total_volume + set_pin_data(IC_OUTPUT, 6, mr) + set_pin_data(IC_OUTPUT, 7, tr) + push_data() + activate_pin(2) + else + activate_pin(3) + +/obj/item/integrated_circuit/input/local_locator + name = "local locator" + desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \ + that is holding the machine containing it." + inputs = list() + outputs = list("located ref") + activators = list("locate" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/local_locator/do_work() + var/datum/integrated_io/O = outputs[1] + O.data = null + if(assembly) + if(istype(assembly.loc, /mob/living)) // Now check if someone's holding us. + O.data = WEAKREF(assembly.loc) + + O.push_data() + +/obj/item/integrated_circuit/input/adjacent_locator + name = "adjacent locator" + desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \ + that is standing a meter away from the machine." + extended_desc = "The first pin requires a ref to a kind of object that you want the locator to acquire. This means that it will \ + give refs to nearby objects that are similar. If more than one valid object is found nearby, it will choose one of them at \ + random." + inputs = list("desired type ref") + outputs = list("located ref") + activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT, + "not found" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 30 + +/obj/item/integrated_circuit/input/adjacent_locator/do_work() + var/datum/integrated_io/I = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = null + + if(!isweakref(I.data)) + return + var/atom/A = I.data.resolve() + if(!A) + return + var/desired_type = A.type + + var/list/nearby_things = range(1, get_turf(src)) + var/list/valid_things = list() + for(var/atom/thing in nearby_things) + if(thing.type != desired_type) + continue + valid_things.Add(thing) + if(valid_things.len) + O.data = WEAKREF(pick(valid_things)) + activate_pin(2) + else + activate_pin(3) + O.push_data() + +/obj/item/integrated_circuit/input/advanced_locator_list + complexity = 6 + name = "list advanced locator" + desc = "This is needed for certain devices that demand list of names for a targets to act upon. This type locates something \ + that is standing in given radius up to 8 meters" + extended_desc = "The first pin requires list a kinds of object that you want the locator to acquire. If This means that it will \ + give refs to nearby objects that are similar. It will locate objects by given names and description,given in list. It will give list of all found objects.\ + .The second pin is a radius" + inputs = list("desired type ref" = IC_PINTYPE_LIST, "radius" = IC_PINTYPE_NUMBER) + outputs = list("located ref" = IC_PINTYPE_LIST) + activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 30 + var/radius = 1 + +/obj/item/integrated_circuit/input/advanced_locator_list/on_data_written() + var/rad = get_pin_data(IC_INPUT, 2) + + if(isnum(rad)) + rad = Clamp(rad, 0, 8) + radius = rad + +/obj/item/integrated_circuit/input/advanced_locator_list/do_work() + var/datum/integrated_io/I = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = null + var/turf/T = get_turf(src) + var/list/nearby_things = view(radius,T) + var/list/valid_things = list() + var/list/GI = list() + GI = I.data + for(var/G in GI) + if(isweakref(G)) //It should search by refs. But don't want.will fix it later. + var/datum/integrated_io/G1 + G1.data = G + var/atom/A = G1.data.resolve() + var/desired_type = A.type + for(var/atom/thing in nearby_things) + if(thing.type != desired_type) + continue + valid_things.Add(WEAKREF(thing)) + else if(istext(G)) + for(var/atom/thing in nearby_things) + if(findtext(addtext(thing.name," ",thing.desc), G, 1, 0) ) + valid_things.Add(WEAKREF(thing)) + if(valid_things.len) + O.data = valid_things + O.push_data() + activate_pin(2) + else + O.push_data() + activate_pin(3) + +/obj/item/integrated_circuit/input/advanced_locator + complexity = 6 + name = "advanced locator" + desc = "This is needed for certain devices that demand a reference for a target to act upon. This type locates something \ + that is standing in given radius up to 8 meters" + extended_desc = "The first pin requires a ref to a kind of object that you want the locator to acquire. If This means that it will \ + give refs to nearby objects that are similar. If this pin is string, locator will search\ + item by matching desired text in name + description. If more than one valid object is found nearby, it will choose one of them at \ + random. The second pin is a radius." + inputs = list("desired type" = IC_PINTYPE_ANY, "radius" = IC_PINTYPE_NUMBER) + outputs = list("located ref") + activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 30 + var/radius = 1 + +/obj/item/integrated_circuit/input/advanced_locator/on_data_written() + var/rad = get_pin_data(IC_INPUT, 2) + if(isnum(rad)) + rad = Clamp(rad, 0, 8) + radius = rad + +/obj/item/integrated_circuit/input/advanced_locator/do_work() + var/datum/integrated_io/I = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = null + var/turf/T = get_turf(src) + var/list/nearby_things = view(radius,T) + var/list/valid_things = list() + if(isweakref(I.data)) + var/atom/A = I.data.resolve() + var/desired_type = A.type + if(desired_type) + for(var/atom/thing in nearby_things) + if(thing.type == desired_type) + valid_things.Add(thing) + else if(istext(I.data)) + var/DT = I.data + for(var/atom/thing in nearby_things) + if(findtext(addtext(thing.name," ",thing.desc), DT, 1, 0) ) + valid_things.Add(thing) + if(valid_things.len) + O.data = WEAKREF(pick(valid_things)) + O.push_data() + activate_pin(2) + else + O.push_data() + activate_pin(3) + + + + + +/obj/item/integrated_circuit/input/signaler + name = "integrated signaler" + desc = "Signals from a signaler can be received with this, allowing for remote control. Additionally, it can send signals as well." + extended_desc = "When a signal is received from another signaler, the 'on signal received' activator pin will be pulsed. \ + The two input pins are to configure the integrated signaler's settings. Note that the frequency should not have a decimal in it. \ + Meaning the default frequency is expressed as 1457, not 145.7. To send a signal, pulse the 'send signal' activator pin." + icon_state = "signal" + complexity = 4 + inputs = list("frequency" = IC_PINTYPE_NUMBER,"code" = IC_PINTYPE_NUMBER) + outputs = list() + activators = list( + "send signal" = IC_PINTYPE_PULSE_IN, + "on signal sent" = IC_PINTYPE_PULSE_OUT, + "on signal received" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_MAGNET = 2) + power_draw_idle = 5 + power_draw_per_use = 40 + + var/frequency = 1457 + var/code = 30 + var/datum/radio_frequency/radio_connection + +/obj/item/integrated_circuit/input/signaler/Initialize() + ..() + spawn(40) + set_frequency(frequency) + // Set the pins so when someone sees them, they won't show as null + set_pin_data(IC_INPUT, 1, frequency) + set_pin_data(IC_INPUT, 2, code) + +/obj/item/integrated_circuit/input/signaler/Destroy() + SSradio.remove_object(src,frequency) + + frequency = 0 + return ..() + +/obj/item/integrated_circuit/input/signaler/on_data_written() + var/new_freq = get_pin_data(IC_INPUT, 1) + var/new_code = get_pin_data(IC_INPUT, 2) + if(isnum(new_freq) && new_freq > 0) + set_frequency(new_freq) + if(isnum(new_code)) + code = new_code + + +/obj/item/integrated_circuit/input/signaler/do_work() // Sends a signal. + if(!radio_connection) + return + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = "ACTIVATE" + radio_connection.post_signal(src, signal) + + activate_pin(2) + +/obj/item/integrated_circuit/input/signaler/proc/set_frequency(new_frequency) + if(!frequency) + return + if(!SSradio) + sleep(20) + if(!SSradio) + return + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, GLOB.RADIO_CHAT) + +/obj/item/integrated_circuit/input/signaler/receive_signal(datum/signal/signal) + var/new_code = get_pin_data(IC_INPUT, 2) + var/code = 0 + + if(isnum(new_code)) + code = new_code + if(!signal) + return 0 + if(signal.encryption != code) + return 0 + if(signal.source == src) // Don't trigger ourselves. + return 0 + + activate_pin(3) + + for(var/mob/O in hearers(1, get_turf(src))) + audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 1) + +/obj/item/integrated_circuit/input/EPv2 + name = "EPv2 circuit" + desc = "Enables the sending and receiving of messages on the Exonet with the EPv2 protocol." + extended_desc = "An EPv2 address is a string with the format of XXXX:XXXX:XXXX:XXXX. Data can be send or received using the \ + second pin on each side, with additonal data reserved for the third pin. When a message is received, the second activation pin \ + will pulse whatever's connected to it. Pulsing the first activation pin will send a message." + icon_state = "signal" + complexity = 4 + inputs = list( + "target EPv2 address" = IC_PINTYPE_STRING, + "data to send" = IC_PINTYPE_STRING, + "secondary text" = IC_PINTYPE_STRING + ) + outputs = list( + "address received" = IC_PINTYPE_STRING, + "data received" = IC_PINTYPE_STRING, + "secondary text received" = IC_PINTYPE_STRING + ) + activators = list("send data" = IC_PINTYPE_PULSE_IN, "on data received" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_MAGNET = 2, TECH_BLUESPACE = 2) + power_draw_per_use = 50 + var/datum/exonet_protocol/exonet = null + +/obj/item/integrated_circuit/input/EPv2/New() + ..() + exonet = new(src) + exonet.make_address("EPv2_circuit-[REF(src)]") + desc += "
This circuit's EPv2 address is: [exonet.address]" + +/obj/item/integrated_circuit/input/EPv2/Destroy() + if(exonet) + exonet.remove_address() + qdel(exonet) + exonet = null + return ..() + +/obj/item/integrated_circuit/input/EPv2/do_work() + var/target_address = get_pin_data(IC_INPUT, 1) + var/message = get_pin_data(IC_INPUT, 2) + var/text = get_pin_data(IC_INPUT, 3) + + if(target_address && istext(target_address)) + exonet.send_message(target_address, message, text) + +/obj/item/integrated_circuit/input/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message, var/text) + set_pin_data(IC_OUTPUT, 1, origin_address) + set_pin_data(IC_OUTPUT, 2, message) + set_pin_data(IC_OUTPUT, 3, text) + + push_data() + activate_pin(2) + +//This circuit gives information on where the machine is. +/obj/item/integrated_circuit/input/gps + name = "global positioning system" + desc = "This allows you to easily know the position of a machine containing this device." + extended_desc = "The GPS's coordinates it gives is absolute, not relative." + icon_state = "gps" + complexity = 4 + inputs = list() + outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER) + activators = list("get coordinates" = IC_PINTYPE_PULSE_IN, "on get coordinates" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 30 + +/obj/item/integrated_circuit/input/gps/do_work() + var/turf/T = get_turf(src) + + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, null) + if(!T) + return + + set_pin_data(IC_OUTPUT, 1, T.x) + set_pin_data(IC_OUTPUT, 2, T.y) + set_pin_data(IC_OUTPUT, 3, T.z) + + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/microphone + name = "microphone" + desc = "Useful for spying on people or for voice activated machines." + extended_desc = "This will automatically translate most languages it hears to Galactic Common. \ + The first activation pin is always pulsed when the circuit hears someone talk, while the second one \ + is only triggered if it hears someone speaking a language other than Galactic Common." + icon_state = "recorder" + complexity = 8 + inputs = list() + flags_1 = CONDUCT_1 | HEAR_1 + outputs = list( + "speaker" = IC_PINTYPE_STRING, + "message" = IC_PINTYPE_STRING + ) + activators = list("on message received" = IC_PINTYPE_PULSE_OUT, "on translation" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 5 + +/obj/item/integrated_circuit/input/microphone/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode) + var/translated = FALSE + if(speaker && message) + if(raw_message) + if(message_langs != get_default_language()) + translated = TRUE + set_pin_data(IC_OUTPUT, 1, speaker.GetVoice()) + set_pin_data(IC_OUTPUT, 2, raw_message) + + push_data() + activate_pin(1) + if(translated) + activate_pin(2) + +/obj/item/integrated_circuit/input/sensor + name = "sensor" + desc = "Scans and obtains a reference for any objects or persons near you. All you need to do is shove the machine in their face." + extended_desc = "If 'ignore storage' pin is set to true, the sensor will disregard scanning various storage containers such as backpacks." + icon_state = "recorder" + complexity = 12 + inputs = list("ignore storage" = IC_PINTYPE_BOOLEAN) + outputs = list("scanned" = IC_PINTYPE_REF) + activators = list("on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 120 + +/obj/item/integrated_circuit/input/sensor/proc/scan(var/atom/A) + var/ignore_bags = get_pin_data(IC_INPUT, 1) + if(ignore_bags) + if(istype(A, /obj/item/storage)) + return FALSE + + set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) + push_data() + activate_pin(1) + return TRUE + +/obj/item/integrated_circuit/input/sensor/ranged + name = "Ranged sensor" + desc = "Scans and obtains a reference for any objects or persons in range. All you need to do is point the machine towards target." + extended_desc = "If 'ignore storage' pin is set to true, the sensor will disregard scanning various storage containers such as backpacks." + icon_state = "recorder" + complexity = 36 + inputs = list("ignore storage" = IC_PINTYPE_BOOLEAN) + outputs = list("scanned" = IC_PINTYPE_REF) + activators = list("on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 120 + +/obj/item/integrated_circuit/input/internalbm + name = "internal battery monitor" + desc = "This monitors the charge level of an internal battery." + icon_state = "internalbm" + extended_desc = "This circuit will give you values of charge, max charge and percentage of the internal battery on demand." + w_class = WEIGHT_CLASS_TINY + complexity = 1 + inputs = list() + outputs = list( + "cell charge" = IC_PINTYPE_NUMBER, + "max charge" = IC_PINTYPE_NUMBER, + "percentage" = IC_PINTYPE_NUMBER, + "refference to assembly" = IC_PINTYPE_REF + ) + activators = list("read" = IC_PINTYPE_PULSE_IN, "on read" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 4, TECH_MAGNET = 3) + power_draw_per_use = 1 + +/obj/item/integrated_circuit/input/internalbm/do_work() + 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, WEAKREF(assembly)) + if(assembly) + if(assembly.battery) + + set_pin_data(IC_OUTPUT, 1, assembly.battery.charge) + set_pin_data(IC_OUTPUT, 2, assembly.battery.maxcharge) + set_pin_data(IC_OUTPUT, 3, 100*assembly.battery.charge/assembly.battery.maxcharge) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/externalbm + name = "external battery monitor" + desc = "This can help to watch battery state of any device in view" + icon_state = "externalbm" + extended_desc = "This circuit will give you values of charge, max charge and percentage of any device or battery in view" + w_class = WEIGHT_CLASS_TINY + complexity = 2 + inputs = list("target" = IC_PINTYPE_REF) + outputs = list( + "cell charge" = IC_PINTYPE_NUMBER, + "max charge" = IC_PINTYPE_NUMBER, + "percentage" = IC_PINTYPE_NUMBER + ) + activators = list("read" = IC_PINTYPE_PULSE_IN, "on read" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 4, TECH_MAGNET = 3) + power_draw_per_use = 1 + +/obj/item/integrated_circuit/input/externalbm/do_work() + + var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, null) + if(AM) + var/obj/item/stock_parts/cell/cell = get_cell(AM) + if(cell) + var/turf/A = get_turf(src) + if(AM in view(A)) + push_data() + set_pin_data(IC_OUTPUT, 1, cell.charge) + set_pin_data(IC_OUTPUT, 2, cell.maxcharge) + set_pin_data(IC_OUTPUT, 3, cell.percent()) + push_data() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/lists.dm b/code/modules/integrated_electronics/subtypes/lists.dm new file mode 100644 index 0000000000..159898926b --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/lists.dm @@ -0,0 +1,200 @@ +//These circuits do things with lists, and use special list pins for stability. +/obj/item/integrated_circuit/lists + complexity = 1 + inputs = list( + "input" = IC_PINTYPE_LIST + ) + outputs = list("result" = IC_PINTYPE_STRING) + activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT) + category_text = "Lists" + power_draw_per_use = 20 + +/obj/item/integrated_circuit/lists/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." + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/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) + result = pick(input_list) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/lists/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." + inputs = list( + "list to append" = IC_PINTYPE_LIST, + "input" = IC_PINTYPE_ANY + ) + outputs = list( + "appended list" = IC_PINTYPE_LIST + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/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.Copy() + output_list.Add(new_entry) + + set_pin_data(IC_OUTPUT, 1, output_list) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/lists/search + name = "search circuit" + desc = "This circuit will give index of desired element in the list." + extended_desc = "Search will start at 1 position and will return first matching position." + inputs = list( + "list" = IC_PINTYPE_LIST, + "item" = IC_PINTYPE_ANY + ) + outputs = list( + "index" = IC_PINTYPE_NUMBER + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/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)) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/lists/at + name = "at circuit" + desc = "This circuit will pick an element from a list by index." + extended_desc = "If there is no element with such index, result will be null." + inputs = list( + "list" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_NUMBER + ) + outputs = list("item" = IC_PINTYPE_ANY) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/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] + set_pin_data(IC_OUTPUT, 1, item) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/lists/delete + name = "delete circuit" + desc = "This circuit will delete the element from a list by index." + extended_desc = "If there is no element with such index, result list will be unchanged." + inputs = list( + "list" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_NUMBER + ) + outputs = list( + "item" = IC_PINTYPE_LIST + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/delete/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/list/red_list = list() + var/index = get_pin_data(IC_INPUT, 2) + for(var/j in 1 to input_list.len) + var/I = input_list[j] + if(j != index) + red_list.Add(I) + set_pin_data(IC_OUTPUT, 1, red_list) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/lists/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." + inputs = list( + "list" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_NUMBER, + "item" = IC_PINTYPE_ANY + ) + outputs = list( + "redacted list" = IC_PINTYPE_LIST + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/write/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/index = get_pin_data(IC_INPUT, 2) + var/item = get_pin_data(IC_INPUT, 3) + if(!islist(item)) //crh proof + input_list[index] = item + set_pin_data(IC_OUTPUT, 1, input_list) + push_data() + activate_pin(2) + +obj/item/integrated_circuit/lists/len + name = "len circuit" + desc = "This circuit will give lenght of the list." + inputs = list( + "list" = IC_PINTYPE_LIST, + ) + outputs = list( + "item" = IC_PINTYPE_NUMBER + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/len/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + set_pin_data(IC_OUTPUT, 1, input_list.len) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/lists/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." + inputs = list( + "list to join" = IC_PINTYPE_LIST,// + "delimiter" = IC_PINTYPE_CHAR, + "start" = IC_PINTYPE_NUMBER, + "end" = IC_PINTYPE_NUMBER + ) + inputs_default = list( + "2" = ",", + "3" = 1, + "4" = 0 + ) + outputs = list( + "joined text" = IC_PINTYPE_STRING + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/jointext/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/delimiter = get_pin_data(IC_INPUT, 2) + var/start = get_pin_data(IC_INPUT, 3) + var/end = get_pin_data(IC_INPUT, 4) + + var/result = null + + if(input_list.len && delimiter && !isnull(start) && !isnull(end)) + result = jointext(input_list, delimiter, start, end) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm new file mode 100644 index 0000000000..0fa7fb2364 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/logic.dm @@ -0,0 +1,221 @@ +/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." + complexity = 3 + outputs = list("result") + activators = list("compare" = IC_PINTYPE_PULSE_IN) + category_text = "Logic" + power_draw_per_use = 1 + +/obj/item/integrated_circuit/logic/do_work() + push_data() + +/obj/item/integrated_circuit/logic/binary + inputs = list("A","B") + activators = list("compare" = IC_PINTYPE_PULSE_IN, "on true result" = IC_PINTYPE_PULSE_OUT, "on false result" = IC_PINTYPE_PULSE_OUT) + +/obj/item/integrated_circuit/logic/binary/do_work() + pull_data() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + var/datum/integrated_io/O = outputs[1] + O.data = do_compare(A, B) ? TRUE : FALSE + + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + ..() + +/obj/item/integrated_circuit/logic/binary/proc/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return FALSE + +/obj/item/integrated_circuit/logic/unary + inputs = list("A") + activators = list("compare" = IC_PINTYPE_PULSE_IN, "on compare" = IC_PINTYPE_PULSE_OUT) + +/obj/item/integrated_circuit/logic/unary/do_work() + pull_data() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = do_check(A) ? TRUE : FALSE + ..() + activate_pin(2) + +/obj/item/integrated_circuit/logic/unary/proc/do_check(var/datum/integrated_io/A) + return FALSE + +/obj/item/integrated_circuit/logic/binary/equals + name = "equal gate" + desc = "This gate compares two values, and outputs the number one if both are the same." + icon_state = "equal" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/equals/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data == B.data + +/obj/item/integrated_circuit/logic/binary/jklatch + name = "JK latch" + desc = "This gate is synchronysed JK latch." + icon_state = "jklatch" + inputs = list("J","K") + outputs = list("Q","!Q") + activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/lstate=FALSE + +/obj/item/integrated_circuit/logic/binary/jklatch/do_work() + pull_data() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + var/datum/integrated_io/O = outputs[1] + var/datum/integrated_io/Q = outputs[2] + if(A.data) + if(B.data) + lstate=!lstate + else + lstate = TRUE + else + if(B.data) + lstate=FALSE + O.data = lstate ? TRUE : FALSE + Q.data = !lstate ? TRUE : FALSE + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + push_data() + +/obj/item/integrated_circuit/logic/binary/rslatch + name = "RS latch" + desc = "This gate is synchronysed RS latch. If both R and S are true, state will not change." + icon_state = "sr_nor" + inputs = list("S","R") + outputs = list("Q","!Q") + activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/lstate=FALSE + +/obj/item/integrated_circuit/logic/binary/rslatch/do_work() + pull_data() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + var/datum/integrated_io/O = outputs[1] + var/datum/integrated_io/Q = outputs[2] + if(A.data) + if(!B.data) + lstate=TRUE + else + if(B.data) + lstate=FALSE + O.data = lstate ? TRUE : FALSE + Q.data = !lstate ? TRUE : FALSE + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + push_data() + +/obj/item/integrated_circuit/logic/binary/gdlatch + name = "gated D latch" + desc = "This gate is synchronysed gated D latch." + icon_state = "gated_d" + inputs = list("D","E") + outputs = list("Q","!Q") + activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/lstate=FALSE + +/obj/item/integrated_circuit/logic/binary/gdlatch/do_work() + pull_data() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + var/datum/integrated_io/O = outputs[1] + var/datum/integrated_io/Q = outputs[2] + if(B.data) + if(A.data) + lstate=TRUE + else + lstate=FALSE + + O.data = lstate ? TRUE : FALSE + Q.data = !lstate ? TRUE : FALSE + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + push_data() + +/obj/item/integrated_circuit/logic/binary/not_equals + name = "not equal gate" + desc = "This gate compares two values, and outputs the number one if both are different." + icon_state = "not_equal" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/not_equals/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data != B.data + +/obj/item/integrated_circuit/logic/binary/and + name = "and gate" + desc = "This gate will output 'one' if both inputs evaluate to true." + icon_state = "and" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/and/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data && B.data + +/obj/item/integrated_circuit/logic/binary/or + name = "or gate" + desc = "This gate will output 'one' if one of the inputs evaluate to true." + icon_state = "or" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/or/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data || B.data + +/obj/item/integrated_circuit/logic/binary/less_than + name = "less than gate" + desc = "This will output 'one' if the first input is less than the second input." + icon_state = "less_than" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/less_than/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data < B.data + +/obj/item/integrated_circuit/logic/binary/less_than_or_equal + 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" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/less_than_or_equal/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data <= B.data + +/obj/item/integrated_circuit/logic/binary/greater_than + name = "greater than gate" + desc = "This will output 'one' if the first input is greater than the second input." + icon_state = "greater_than" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/greater_than/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data > B.data + +/obj/item/integrated_circuit/logic/binary/greater_than_or_equal + 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" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/logic/binary/greater_than_or_equal/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data >= B.data + +/obj/item/integrated_circuit/logic/unary/not + name = "not gate" + desc = "This gate inverts what's fed into it." + icon_state = "not" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + activators = list("invert" = IC_PINTYPE_PULSE_IN, "on inverted" = IC_PINTYPE_PULSE_OUT) + +/obj/item/integrated_circuit/logic/unary/not/do_check(var/datum/integrated_io/A) + return !A.data diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm new file mode 100644 index 0000000000..f8eda5982c --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -0,0 +1,443 @@ +/obj/item/integrated_circuit/manipulation + category_text = "Manipulation" + +/obj/item/integrated_circuit/manipulation/weapon_firing + name = "weapon firing mechanism" + desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it." + extended_desc = "The firing mechanism can slot in any energy weapon. \ + The first and second inputs need to be numbers. They are coordinates for the gun to fire at, relative to the machine itself. \ + The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode is switch between\ + letal(TRUE) or stun(FALSE) modes.It uses internal battery of weapon." + complexity = 20 + w_class = WEIGHT_CLASS_SMALL + size = 3 + inputs = list( + "target X rel" = IC_PINTYPE_NUMBER, + "target Y rel" = IC_PINTYPE_NUMBER, + "mode" = IC_PINTYPE_BOOLEAN + ) + outputs = list("reference to gun" = IC_PINTYPE_REF) + activators = list( + "fire" = IC_PINTYPE_PULSE_IN + + ) + var/obj/item/gun/energy/installed_gun = null + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 4) + power_draw_per_use = 0 + var/mode = FALSE + + var/stun_projectile = null //stun mode projectile type + var/stun_projectile_sound + var/lethal_projectile = null //lethal mode projectile type + var/lethal_projectile_sound + + + +/obj/item/integrated_circuit/manipulation/weapon_firing/Destroy() + qdel(installed_gun) + ..() + +/obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user) + if(istype(O, /obj/item/gun/energy)) + var/obj/item/gun/gun = O + if(installed_gun) + to_chat(user, "There's already a weapon installed.") + return + + user.transferItemToLoc(gun,src) + installed_gun = gun + var/list/gun_properties = gun.get_turret_properties() + to_chat(user, "You slide \the [gun] into the firing mechanism.") + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + stun_projectile = gun_properties["stun_projectile"] + stun_projectile_sound = gun_properties["stun_projectile_sound"] + lethal_projectile = gun_properties["lethal_projectile"] + lethal_projectile_sound = gun_properties["lethal_projectile_sound"] + if(gun_properties["shot_delay"]) + cooldown_per_use = gun_properties["shot_delay"]*10 + if(cooldown_per_use<30) + cooldown_per_use = 40 + if(gun_properties["reqpower"]) + power_draw_per_use = gun_properties["reqpower"] + set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun)) + push_data() + else + ..() + +/obj/item/integrated_circuit/manipulation/weapon_firing/attack_self(var/mob/user) + if(installed_gun) + installed_gun.forceMove(drop_location()) + to_chat(user, "You slide \the [installed_gun] out of the firing mechanism.") + size = initial(size) + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + installed_gun = null + set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) + push_data() + else + to_chat(user, "There's no weapon to remove from the mechanism.") + +/obj/item/integrated_circuit/manipulation/weapon_firing/do_work() + if(!installed_gun) + return + set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun)) + push_data() + var/datum/integrated_io/xo = inputs[1] + var/datum/integrated_io/yo = inputs[2] + var/datum/integrated_io/mode1 = inputs[3] + + mode = mode1.data + if(assembly) + if(isnum(xo.data)) + xo.data = round(xo.data, 1) + if(isnum(yo.data)) + yo.data = round(yo.data, 1) + + var/turf/T = get_turf(assembly) + var/target_x = Clamp(T.x + xo.data, 0, world.maxx) + var/target_y = Clamp(T.y + yo.data, 0, world.maxy) + + shootAt(locate(target_x, target_y, T.z)) + +/obj/item/integrated_circuit/manipulation/weapon_firing/proc/shootAt(turf/target) + + var/turf/T = get_turf(src) + var/turf/U = target + if(!istype(T) || !istype(U)) + return + if(!installed_gun.cell) + return + if(!installed_gun.cell.charge) + return + var/obj/item/ammo_casing/energy/shot = installed_gun.ammo_type[mode?2:1] + if(installed_gun.cell.charge < shot.e_cost) + return + if(!shot) + return + update_icon() + var/obj/item/projectile/A + if(!mode) + A = new stun_projectile(T) + playsound(loc, stun_projectile_sound, 75, 1) + else + A = new lethal_projectile(T) + playsound(loc, lethal_projectile_sound, 75, 1) + + + installed_gun.cell.use(shot.e_cost) + //Shooting Code: + A.preparePixelProjectile(target, src) + A.fire() + return A + +/obj/item/integrated_circuit/manipulation/locomotion + name = "locomotion circuit" + desc = "This allows a machine to move in a given direction." + icon_state = "locomotion" + extended_desc = "The circuit accepts a 'dir' number as a direction to move towards.
\ + Pulsing the 'step towards dir' activator pin will cause the machine to move a meter in that direction, assuming it is not \ + being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits." + w_class = WEIGHT_CLASS_SMALL + complexity = 20 +// size = 5 + inputs = list("direction" = IC_PINTYPE_DIR) + outputs = list() + activators = list("step towards dir" = IC_PINTYPE_PULSE_IN,"on step"=IC_PINTYPE_PULSE_OUT,"blocked"=IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 100 + +/obj/item/integrated_circuit/manipulation/locomotion/do_work() + ..() + var/turf/T = get_turf(src) + if(T && assembly) + if(assembly.anchored || !assembly.can_move()) + return + if(assembly.loc == T) // Check if we're held by someone. If the loc is the floor, we're not held. + var/datum/integrated_io/wanted_dir = inputs[1] + if(isnum(wanted_dir.data)) + if(step(assembly, wanted_dir.data)) + activate_pin(2) + else + activate_pin(3) + return FALSE + +/obj/item/integrated_circuit/manipulation/grenade + name = "grenade primer" + desc = "This circuit comes with the ability to attach most types of grenades at prime them at will." + extended_desc = "Time between priming and detonation is limited to between 1 to 12 seconds but is optional. \ + If unset, not a number, or a number less than 1 then the grenade's built-in timing will be used. \ + Beware: Once primed there is no aborting the process!" + icon_state = "grenade" + complexity = 30 + inputs = list("detonation time" = IC_PINTYPE_NUMBER) + outputs = list() + activators = list("prime grenade" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 4) + var/obj/item/grenade/attached_grenade + var/pre_attached_grenade_type + +/obj/item/integrated_circuit/manipulation/grenade/Initialize() + . = ..() + if(pre_attached_grenade_type) + var/grenade = new pre_attached_grenade_type(src) + attach_grenade(grenade) + +/obj/item/integrated_circuit/manipulation/grenade/Destroy() + if(attached_grenade && !attached_grenade.active) + attached_grenade.forceMove(loc) + detach_grenade() + return ..() + +/obj/item/integrated_circuit/manipulation/grenade/attackby(var/obj/item/grenade/G, var/mob/user) + if(istype(G)) + if(attached_grenade) + to_chat(user, "There is already a grenade attached!") + else if(user.transferItemToLoc(G,src)) + user.visible_message("\The [user] attaches \a [G] to \the [src]!", "You attach \the [G] to \the [src].") + attach_grenade(G) + G.forceMove(src) + else + return ..() + +/obj/item/integrated_circuit/manipulation/grenade/attack_self(var/mob/user) + if(attached_grenade) + user.visible_message("\The [user] removes \an [attached_grenade] from \the [src]!", "You remove \the [attached_grenade] from \the [src].") + user.put_in_hands(attached_grenade) + detach_grenade() + else + return ..() + +/obj/item/integrated_circuit/manipulation/grenade/do_work() + if(attached_grenade && !attached_grenade.active) + var/datum/integrated_io/detonation_time = inputs[1] + var/dt + if(isnum(detonation_time.data) && detonation_time.data > 0) + dt = Clamp(detonation_time.data, 1, 12)*10 + else + dt = 15 + addtimer(CALLBACK(attached_grenade, /obj/item/grenade.proc/prime), dt) + var/atom/holder = loc + message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]") + +// These procs do not relocate the grenade, that's the callers responsibility +/obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/grenade/G) + attached_grenade = G + G.forceMove(src) + desc += " \An [attached_grenade] is attached to it!" + +/obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade() + if(!attached_grenade) + return + attached_grenade.forceMove(drop_location()) + attached_grenade = null + desc = initial(desc) +/* +/obj/item/integrated_circuit/manipulation/grenade/frag + pre_attached_grenade_type = /obj/item/weapon/grenade/explosive + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 10) + spawn_flags = null // Used for world initializing, see the #defines above. +*/ +/obj/item/integrated_circuit/manipulation/shocker + name = "shocker circuit" + desc = "Used to shock adjacent creatures with electricity." + icon_state = "shocker" + extended_desc = "The circuit accepts a reference to creature,who needs to be shocked. It can shock target on adjacent tiles. \ + Severity determines hardness of shock and it's power consumption. It's given between 0 and 60." + w_class = WEIGHT_CLASS_TINY + complexity = 10 + inputs = list("target" = IC_PINTYPE_REF,"severity" = IC_PINTYPE_NUMBER) + outputs = list() + activators = list("shock" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 0 + +/obj/item/integrated_circuit/manipulation/shocker/on_data_written() + var/s = get_pin_data(IC_INPUT, 2) + power_draw_per_use = Clamp(s,0,60)*1200/60 + +/obj/item/integrated_circuit/manipulation/shocker/do_work() + ..() + var/turf/T = get_turf(src) + var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /mob/living) + if(!istype(AM,/mob/living)) //Invalid input + return + var/mob/living/M = AM + if(!M.Adjacent(T)) + return //Can't reach + to_chat(M, "You feel a sharp shock!") + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(12, 1, src) + s.start() + var/stf=Clamp(get_pin_data(IC_INPUT, 2),0,60) + M.Knockdown(stf) + M.apply_effect(STUTTER, stf) + +/obj/item/integrated_circuit/manipulation/plant_module + name = "plant manipulation module" + desc = "Used to uproot weeds or harvest plants in trays." + icon_state = "plant_m" + extended_desc = "The circuit accepts a reference to hydroponic tray. It work from adjacent tiles. \ + Mode(0- harvest, 1-uproot weeds, 2-uproot plant) determinies action." + w_class = WEIGHT_CLASS_TINY + complexity = 10 + inputs = list("target" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER) + outputs = list() + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out"=IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 50 + +/obj/item/integrated_circuit/manipulation/plant_module/do_work() + ..() + var/turf/T = get_turf(src) + var/obj/machinery/hydroponics/AM = get_pin_data_as_type(IC_INPUT, 1, /obj/machinery/hydroponics) + if(!istype(AM)) //Invalid input + return + var/mob/living/M = get_turf(AM) + if(!M.Adjacent(T)) + return //Can't reach + switch(get_pin_data(IC_INPUT, 2)) + if(0) + if(AM.myseed) + if(AM.harvest) + AM.myseed.harvest() + AM.harvest = 0 + AM.lastproduce = AM.age + if(!AM.myseed.get_gene(/datum/plant_gene/trait/repeated_harvest)) + qdel(AM.myseed) + AM.myseed = null + AM.dead = 0 + AM.update_icon() + if(1) + AM.weedlevel = 0 + if(2) + if(AM.myseed) //Could be that they're just using it as a de-weeder + AM.age = 0 + AM.plant_health = 0 + if(AM.harvest) + AM.harvest = FALSE //To make sure they can't just put in another seed and insta-harvest it + qdel(AM.myseed) + AM.myseed = null + AM.weedlevel = 0 //Has a side effect of cleaning up those nasty weeds + AM.update_icon() + else + activate_pin(2) + return FALSE + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/grabber + name = "grabber" + desc = "A circuit with it's own inventory for small/medium items, used to grab and store things." + icon_state = "grabber" + extended_desc = "The circuit accepts a reference to thing to be grabbed. It can store up to 10 things. Modes: 1 for grab. 0 for eject the first thing. -1 for eject all." + w_class = WEIGHT_CLASS_SMALL + size = 3 + + complexity = 10 + inputs = list("target" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER) + outputs = list("first" = IC_PINTYPE_REF, "last" = IC_PINTYPE_REF, "amount" = IC_PINTYPE_NUMBER,"contents" = IC_PINTYPE_LIST) + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 50 + var/max_w_class = WEIGHT_CLASS_NORMAL + var/max_items = 10 +/* +/obj/item/integrated_circuit/manipulation/thrower/New() + processing_objects |= src + +/obj/item/integrated_circuit/manipulation/thrower/Destroy() + processing_objects -= src + +/obj/item/integrated_circuit/manipulation/thrower/process() + set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1])) + set_pin_data(IC_OUTPUT, 2, WEAKREF(contents[contents.len])) + set_pin_data(IC_OUTPUT, 3, contents.len) + push_data() +*/ +/obj/item/integrated_circuit/manipulation/grabber/do_work() + var/turf/T = get_turf(src) + var/obj/item/AM = get_pin_data_as_type(IC_INPUT, 1, /obj/item) + if(AM) + var/turf/P = get_turf(AM) + var/mode = get_pin_data(IC_INPUT, 2) + + if(mode == 1) + if(P.Adjacent(T)) + if((contents.len < max_items) && AM && (AM.w_class <= max_w_class)) + AM.forceMove(src) + if(mode == 0) + if(contents.len) + var/obj/item/U = contents[1] + U.forceMove(T) + if(mode == -1) + if(contents.len) + var/obj/item/U + for(U in contents) + U.forceMove(T) + if(contents.len) + set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1])) + set_pin_data(IC_OUTPUT, 2, WEAKREF(contents[contents.len])) + else + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, contents.len) + set_pin_data(IC_OUTPUT, 4, contents) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user) + if(contents.len) + var/turf/T = get_turf(src) + var/obj/item/U + for(U in contents) + U.forceMove(T) + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, contents.len) + push_data() + + +/obj/item/integrated_circuit/manipulation/thrower + name = "thrower" + desc = "A compact launcher to throw things from inside or nearby tiles" + extended_desc = "The first and second inputs need to be numbers. They are coordinates to throw thing at, relative to the machine itself. \ + The 'fire' activator will cause the mechanism to attempt to throw thing at the coordinates, if possible. Note that the \ + projectile need to be inside the machine, or to be on an adjacent tile, and to be up to medium size." + complexity = 15 + w_class = WEIGHT_CLASS_SMALL + size = 2 + inputs = list( + "target X rel" = IC_PINTYPE_NUMBER, + "target Y rel" = IC_PINTYPE_NUMBER, + "projectile" = IC_PINTYPE_REF + ) + outputs = list() + activators = list( + "fire" = IC_PINTYPE_PULSE_IN + ) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 4) + power_draw_per_use = 50 + +/obj/item/integrated_circuit/manipulation/thrower/do_work() + var/datum/integrated_io/target_x = inputs[1] + var/datum/integrated_io/target_y = inputs[2] + var/datum/integrated_io/projectile = inputs[3] + if(!isweakref(projectile.data)) + return + var/obj/item/A = projectile.data.resolve() + if(A.anchored || (A.w_class > WEIGHT_CLASS_NORMAL)) + return + var/turf/T = get_turf(assembly) + if(!(A.Adjacent(T) || (A in assembly.GetAllContents()))) + return + if(assembly) + if(isnum(target_x.data)) + target_x.data = round(target_x.data, 1) + if(isnum(target_y.data)) + target_y.data = round(target_y.data, 1) + var/_x = Clamp(T.x + target_x.data, 0, world.maxx) + var/_y = Clamp(T.y + target_y.data, 0, world.maxy) + + A.forceMove(drop_location()) + A.throw_at(locate(_x, _y, T.z), round(Clamp(sqrt(target_x.data*target_x.data+target_y.data*target_y.data),0,8),1), 3) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm new file mode 100644 index 0000000000..548ea3fa1d --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/memory.dm @@ -0,0 +1,124 @@ +/obj/item/integrated_circuit/memory + name = "memory chip" + desc = "This tiny chip can store one piece of data." + icon_state = "memory" + complexity = 1 + inputs = list() + outputs = list() + activators = list("set" = IC_PINTYPE_PULSE_IN, "on set" = IC_PINTYPE_PULSE_OUT) + category_text = "Memory" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 1 + var/number_of_pins = 1 + +/obj/item/integrated_circuit/memory/New() + for(var/i = 1 to number_of_pins) + inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. + outputs["output [i]"] = IC_PINTYPE_ANY + complexity = number_of_pins + ..() + +/obj/item/integrated_circuit/memory/examine(mob/user) + ..() + var/i + for(i = 1, i <= outputs.len, i++) + var/datum/integrated_io/O = outputs[i] + var/data = "nothing" + if(isweakref(O.data)) + var/datum/d = O.data_as_type(/datum) + if(d) + data = "[d]" + else if(!isnull(O.data)) + data = O.data + to_chat(user, "\The [src] has [data] saved to address [i].") + +/obj/item/integrated_circuit/memory/do_work() + for(var/i = 1 to inputs.len) + var/datum/integrated_io/I = inputs[i] + var/datum/integrated_io/O = outputs[i] + O.data = I.data + O.push_data() + activate_pin(2) + +/obj/item/integrated_circuit/memory/tiny + name = "small memory circuit" + desc = "This circuit can store two pieces of data." + icon_state = "memory4" + power_draw_per_use = 2 + number_of_pins = 2 + +/obj/item/integrated_circuit/memory/medium + name = "medium memory circuit" + desc = "This circuit can store four pieces of data." + icon_state = "memory4" + power_draw_per_use = 2 + number_of_pins = 4 + +/obj/item/integrated_circuit/memory/large + name = "large memory circuit" + desc = "This big circuit can hold eight pieces of data." + icon_state = "memory8" + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3) + power_draw_per_use = 4 + number_of_pins = 8 + +/obj/item/integrated_circuit/memory/huge + name = "large memory stick" + desc = "This stick of memory can hold up up to sixteen pieces of data." + icon_state = "memory16" + w_class = WEIGHT_CLASS_SMALL + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4) + power_draw_per_use = 8 + number_of_pins = 16 + +/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." + icon_state = "memory" + inputs = list() + outputs = list("output pin" = IC_PINTYPE_ANY) + activators = list("push data" = IC_PINTYPE_PULSE_IN) + var/accepting_refs = FALSE + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/memory/constant/do_work() + var/datum/integrated_io/O = outputs[1] + O.push_data() + +/obj/item/integrated_circuit/memory/constant/attack_self(mob/user) + var/datum/integrated_io/O = outputs[1] + if(!user.IsAdvancedToolUser()) + return + var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") + + var/new_data = null + switch(type_to_use) + if("string") + accepting_refs = FALSE + new_data = input("Now type in a string.","[src] string writing") as null|text + if(istext(new_data) && user.IsAdvancedToolUser()) + O.data = new_data + to_chat(user, "You set \the [src]'s memory to [O.display_data(O.data)].") + if("number") + accepting_refs = FALSE + new_data = input("Now type in a number.","[src] number writing") as null|num + if(isnum(new_data) && user.IsAdvancedToolUser()) + O.data = new_data + to_chat(user, "You set \the [src]'s memory to [O.display_data(O.data)].") + if("ref") + accepting_refs = TRUE + to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ + an object for a ref of that object to save it in memory.") + if("null") + O.data = null + to_chat(user, "You set \the [src]'s memory to absolutely nothing.") + +/obj/item/integrated_circuit/memory/constant/afterattack(atom/target, mob/living/user, proximity) + if(accepting_refs && proximity) + var/datum/integrated_io/O = outputs[1] + O.data = WEAKREF(target) + visible_message("[user] slides \a [src]'s over \the [target].") + to_chat(user, "You set \the [src]'s memory to a reference to [O.display_data(O.data)]. The ref scanner is \ + now off.") + accepting_refs = FALSE \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm new file mode 100644 index 0000000000..75ed9d5216 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -0,0 +1,359 @@ +/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." + icon_state = "screen" + inputs = list("displayed data" = IC_PINTYPE_ANY) + outputs = list() + activators = list("load data" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 10 + var/stuff_to_display = null + +/obj/item/integrated_circuit/output/screen/disconnect_all() + ..() + stuff_to_display = null + +/obj/item/integrated_circuit/output/screen/any_examine(mob/user) + to_chat(user, "There is a little screen labeled '[name]', which displays [!isnull(stuff_to_display) ? "'[stuff_to_display]'" : "nothing"].") + +/obj/item/integrated_circuit/output/screen/do_work() + var/datum/integrated_io/I = inputs[1] + if(isweakref(I.data)) + var/datum/d = I.data_as_type(/datum) + if(d) + stuff_to_display = "[d]" + else + stuff_to_display = I.data + +/obj/item/integrated_circuit/output/screen/medium + name = "screen" + desc = "This screen allows for people holding the device to see a piece of data." + icon_state = "screen_medium" + power_draw_per_use = 20 + +/obj/item/integrated_circuit/output/screen/medium/do_work() + ..() + var/list/nearby_things = range(0, get_turf(src)) + for(var/mob/M in nearby_things) + var/obj/O = assembly ? assembly : src + to_chat(M, "[icon2html(O.icon, world, O.icon_state)] [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." + icon_state = "screen_large" + power_draw_per_use = 40 + +/obj/item/integrated_circuit/output/screen/large/do_work() + ..() + var/obj/O = assembly ? loc : assembly + O.visible_message("[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]") + +/obj/item/integrated_circuit/output/light + name = "light" + desc = "This light can turn on and off on command." + icon_state = "light" + complexity = 4 + inputs = list() + outputs = list() + activators = list("toggle light" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/light_toggled = 0 + var/light_brightness = 3 + var/light_rgb = "#FFFFFF" + power_draw_idle = 0 // Adjusted based on brightness. + +/obj/item/integrated_circuit/output/light/do_work() + light_toggled = !light_toggled + update_lighting() + +/obj/item/integrated_circuit/output/light/proc/update_lighting() + if(light_toggled) + if(assembly) + assembly.set_light(l_range = light_brightness, l_power = light_brightness, l_color = light_rgb) + else + if(assembly) + assembly.set_light(0) + power_draw_idle = light_toggled ? light_brightness * 2 : 0 + +/obj/item/integrated_circuit/output/light/advanced/update_lighting() + var/new_color = get_pin_data(IC_INPUT, 1) + var/brightness = get_pin_data(IC_INPUT, 2) + + if(new_color && isnum(brightness)) + brightness = Clamp(brightness, 0, 6) + light_rgb = new_color + light_brightness = brightness + + ..() + +/obj/item/integrated_circuit/output/light/power_fail() // Turns off the flashlight if there's no power left. + light_toggled = FALSE + update_lighting() + +/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." + icon_state = "light_adv" + complexity = 8 + inputs = list( + "color" = IC_PINTYPE_COLOR, + "brightness" = IC_PINTYPE_NUMBER + ) + outputs = list() + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3) + +/obj/item/integrated_circuit/output/light/advanced/on_data_written() + update_lighting() + +/obj/item/integrated_circuit/output/sound + name = "speaker circuit" + desc = "A miniature speaker is attached to this component." + icon_state = "speaker" + complexity = 8 + cooldown_per_use = 4 SECONDS + inputs = list( + "sound ID" = IC_PINTYPE_STRING, + "volume" = IC_PINTYPE_NUMBER, + "frequency" = IC_PINTYPE_BOOLEAN + ) + outputs = list() + activators = list("play sound" = IC_PINTYPE_PULSE_IN) + power_draw_per_use = 10 + var/list/sounds = list() + +/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." + icon_state = "speaker" + complexity = 12 + inputs = list("text" = IC_PINTYPE_STRING) + outputs = list() + activators = list("to speech" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 60 + +/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 + O.say(strip_html_simple(text)) + +/obj/item/integrated_circuit/output/sound/Initialize() + .= ..() + extended_desc = list() + extended_desc += "The first input pin determines which sound is used. The choices are; " + extended_desc += jointext(sounds, ", ") + extended_desc += ". The second pin determines the volume of sound that is played" + extended_desc += ", and the third determines if the frequency of the sound will vary with each activation." + extended_desc = jointext(extended_desc, null) + +/obj/item/integrated_circuit/output/sound/do_work() + var/ID = get_pin_data(IC_INPUT, 1) + var/vol = get_pin_data(IC_INPUT, 2) + var/freq = get_pin_data(IC_INPUT, 3) + if(!isnull(ID) && !isnull(vol)) + var/selected_sound = sounds[ID] + if(!selected_sound) + return + vol = Clamp(vol ,0 , 100) + playsound(get_turf(src), selected_sound, vol, freq, -1) + +/obj/item/integrated_circuit/output/sound/on_data_written() + power_draw_per_use = get_pin_data(IC_INPUT, 2) * 15 + +/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 \ + 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( + "beep" = 'sound/machines/twobeep.ogg', + "chime" = 'sound/machines/chime.ogg', + "buzz sigh" = 'sound/machines/buzz-sigh.ogg', + "buzz twice" = 'sound/machines/buzz-two.ogg', + "ping" = 'sound/machines/ping.ogg', + "synth yes" = 'sound/machines/synth_yes.ogg', + "synth no" = 'sound/machines/synth_no.ogg', + "warning buzz" = 'sound/machines/warning-buzzer.ogg' + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/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." + sounds = list( + "creep" = 'sound/voice/bcreep.ogg', + "criminal" = 'sound/voice/bcriminal.ogg', + "freeze" = 'sound/voice/bfreeze.ogg', + "god" = 'sound/voice/bgod.ogg', + "i am the law" = 'sound/voice/biamthelaw.ogg', + "insult" = 'sound/voice/binsult.ogg', + "radio" = 'sound/voice/bradio.ogg', + "secure day" = 'sound/voice/bsecureday.ogg', + ) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_ILLEGAL = 1) + +/obj/item/integrated_circuit/output/sound/medbot + name = "medbot sound circuit" + desc = "A miniature speaker is attached to this component, used to annoy patients while they get pricked by a medbot." + sounds = list( + "surgeon" = 'sound/voice/msurgeon.ogg', + "radar" = 'sound/voice/mradar.ogg', + "feel better" = 'sound/voice/mfeelbetter.ogg', + "patched up" = 'sound/voice/mpatchedup.ogg', + "injured" = 'sound/voice/minjured.ogg', + "insult" = 'sound/voice/minsult.ogg', + "coming" = 'sound/voice/mcoming.ogg', + "help" = 'sound/voice/mhelp.ogg', + "live" = 'sound/voice/mlive.ogg', + "lost" = 'sound/voice/mlost.ogg', + "flies" = 'sound/voice/mflies.ogg', + "catch" = 'sound/voice/mcatch.ogg', + "delicious" = 'sound/voice/mdelicious.ogg', + "apple" = 'sound/voice/mapple.ogg', + "no" = 'sound/voice/mno.ogg', + ) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 1) + +/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." + icon_state = "video_camera" + w_class = WEIGHT_CLASS_SMALL + complexity = 10 + inputs = list( + "camera name" = IC_PINTYPE_STRING, + "camera active" = IC_PINTYPE_BOOLEAN + ) + inputs_default = list("1" = "video camera circuit") + outputs = list() + activators = list() + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_idle = 0 // Raises to 20 when on. + var/obj/machinery/camera/camera + var/updating = FALSE + +/obj/item/integrated_circuit/output/video_camera/New() + ..() + camera = new(src) + camera.network = list("RD") + on_data_written() + +/obj/item/integrated_circuit/output/video_camera/Destroy() + QDEL_NULL(camera) + return ..() + +/obj/item/integrated_circuit/output/video_camera/proc/set_camera_status(var/status) + if(camera) + camera.status = status + GLOB.cameranet.updatePortableCamera(camera) + power_draw_idle = camera.status ? 20 : 0 + if(camera.status) // Ensure that there's actually power. + if(!draw_idle_power()) + power_fail() + +/obj/item/integrated_circuit/output/video_camera/on_data_written() + if(camera) + var/cam_name = get_pin_data(IC_INPUT, 1) + var/cam_active = get_pin_data(IC_INPUT, 2) + if(!isnull(cam_name)) + camera.c_tag = cam_name + set_camera_status(cam_active) + +/obj/item/integrated_circuit/output/video_camera/power_fail() + if(camera) + set_camera_status(0) + set_pin_data(IC_INPUT, 2, FALSE) + +/obj/item/integrated_circuit/output/video_camera/ext_moved(oldLoc, dir) + . = ..() + update_camera_location(oldLoc) + +#define VIDEO_CAMERA_BUFFER 10 +/obj/item/integrated_circuit/output/video_camera/proc/update_camera_location(oldLoc) + oldLoc = get_turf(oldLoc) + if(!QDELETED(camera) && !updating && oldLoc != get_turf(src)) + updating = TRUE + addtimer(CALLBACK(src, .proc/do_camera_update, oldLoc), VIDEO_CAMERA_BUFFER) +#undef VIDEO_CAMERA_BUFFER + +/obj/item/integrated_circuit/output/video_camera/proc/do_camera_update(oldLoc) + if(!QDELETED(camera) && oldLoc != get_turf(src)) + GLOB.cameranet.updatePortableCamera(camera) + updating = FALSE + +/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." + complexity = 0.1 + icon_state = "led" + inputs = list("lit" = IC_PINTYPE_BOOLEAN) + outputs = list() + activators = list() + power_draw_idle = 0 // Raises to 1 when lit. + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/led_color + +/obj/item/integrated_circuit/output/led/on_data_written() + power_draw_idle = get_pin_data(IC_INPUT, 1) ? 1 : 0 + +/obj/item/integrated_circuit/output/led/power_fail() + set_pin_data(IC_INPUT, 1, FALSE) + +/obj/item/integrated_circuit/output/led/external_examine(mob/user) + var/text_output = list() + + // Doing all this work just to have a color-blind friendly output. + text_output += "There is " + if(name == displayed_name ) + text_output += "\an [name]" + else + text_output += "\an ["\improper[name]"] labeled '[displayed_name ]'" + text_output += " which is currently [(get_pin_data(IC_INPUT, 1)==1) ? "lit *" : "unlit."]" + to_chat(user,jointext(text_output,null)) + +/obj/item/integrated_circuit/output/led/red + name = "red LED" + led_color = "#FF0000" + +/obj/item/integrated_circuit/output/led/orange + name = "orange LED" + led_color = "#FF9900" + +/obj/item/integrated_circuit/output/led/yellow + name = "yellow LED" + led_color = "#FFFF00" + +/obj/item/integrated_circuit/output/led/green + name = "green LED" + led_color = "#008000" + +/obj/item/integrated_circuit/output/led/blue + name = "blue LED" + led_color = "#0000FF" + +/obj/item/integrated_circuit/output/led/purple + name = "purple LED" + led_color = "#800080" + +/obj/item/integrated_circuit/output/led/cyan + name = "cyan LED" + led_color = "#00FFFF" + +/obj/item/integrated_circuit/output/led/white + name = "white LED" + led_color = "#FFFFFF" + +/obj/item/integrated_circuit/output/led/pink + name = "pink LED" + led_color = "#FF00FF" diff --git a/code/modules/integrated_electronics/subtypes/power.dm b/code/modules/integrated_electronics/subtypes/power.dm new file mode 100644 index 0000000000..db39e067ad --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/power.dm @@ -0,0 +1,81 @@ +/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." + 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 \ + 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, \ + some power is lost due to ineffiency." + w_class = WEIGHT_CLASS_SMALL + complexity = 16 + inputs = list("target" = IC_PINTYPE_REF) + outputs = list( + "target cell charge" = IC_PINTYPE_NUMBER, + "target cell max charge" = IC_PINTYPE_NUMBER, + "target cell percentage" = IC_PINTYPE_NUMBER + ) + activators = list("transmit" = IC_PINTYPE_PULSE_IN, "on transmit" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 4, TECH_MAGNET = 3) + power_draw_per_use = 500 // Inefficency has to come from somewhere. + var/amount_to_move = 5000 + +/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." + 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, \ + some power is lost due to ineffiency." + w_class = WEIGHT_CLASS_BULKY + complexity = 32 + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 6, TECH_MAGNET = 5) + power_draw_per_use = 2000 + amount_to_move = 20000 + +/obj/item/integrated_circuit/power/transmitter/do_work() + + var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + if(AM) + if(!assembly) + return FALSE // Pointless to do everything else if there's no battery to draw from. + + var/obj/item/stock_parts/cell/cell = AM.get_cell() + if(cell) + var/transfer_amount = amount_to_move + var/turf/A = get_turf(src) + var/turf/B = get_turf(AM) + if(A.Adjacent(B)) + if(AM.loc != assembly) + transfer_amount *= 0.8 // Losses due to distance. + + if(cell.charge == cell.maxcharge) + return FALSE + + if(transfer_amount && assembly.draw_power(amount_to_move)) // CELLRATE is already handled in draw_power() + cell.give(transfer_amount * GLOB.CELLRATE) + set_pin_data(IC_OUTPUT, 1, cell.charge) + set_pin_data(IC_OUTPUT, 2, cell.maxcharge) + set_pin_data(IC_OUTPUT, 3, cell.percent()) + activate_pin(2) + push_data() + return TRUE + else + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, null) + activate_pin(2) + push_data() + return FALSE + return FALSE + +/obj/item/integrated_circuit/power/transmitter/large/do_work() + if(..()) // If the above code succeeds, do this below. + if(prob(20)) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(12, 1, src) + s.start() + visible_message("\The [assembly] makes some sparks!") diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm new file mode 100644 index 0000000000..e6eeb3521c --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -0,0 +1,365 @@ +/obj/item/integrated_circuit/reagent + category_text = "Reagent" + var/volume = 0 + resistance_flags = UNACIDABLE | FIRE_PROOF + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/integrated_circuit/reagent/New() + ..() + if(volume) + create_reagents(volume) + +/obj/item/integrated_circuit/reagent/smoke + name = "smoke generator" + desc = "Unlike most electronics, creating smoke is completely intentional." + icon_state = "smoke" + extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \ + into the smoke clouds when activated. The reagents are consumed when smoke is made." + container_type = OPENCONTAINER_1 + complexity = 20 + cooldown_per_use = 1 SECONDS + inputs = list() + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF) + activators = list("create smoke" = IC_PINTYPE_PULSE_IN,"on smoked" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 3) + volume = 100 + power_draw_per_use = 20 + var/smoke_radius = 5 + +/obj/item/integrated_circuit/reagent/smoke/on_reagent_change() + set_pin_data(IC_OUTPUT, 1, reagents.total_volume) + push_data() + + +/obj/item/integrated_circuit/reagent/smoke/interact(mob/user) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + ..() + +/obj/item/integrated_circuit/reagent/smoke/do_work() + var/location = get_turf(src) + var/datum/effect_system/smoke_spread/chem/S = new + S.attach(location) + playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) + if(S) + S.set_up(reagents, smoke_radius, location, 0) + S.start() + if(reagents) + reagents.clear_reagents() + activate_pin(2) + +/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." + 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. Negative given amount makes injector suck out reagents." + container_type = OPENCONTAINER_1 + complexity = 20 + 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) + 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 + power_draw_per_use = 15 + var/direction_mode = SYRINGE_INJECT + var/transfer_amount = 10 + var/busy = FALSE + +/obj/item/integrated_circuit/reagent/injector/interact(mob/user) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + ..() + + +/obj/item/integrated_circuit/reagent/injector/on_reagent_change() + set_pin_data(IC_OUTPUT, 1, reagents.total_volume) + push_data() + +/obj/item/integrated_circuit/reagent/injector/on_data_written() + var/new_amount = get_pin_data(IC_INPUT, 2) + if(new_amount < 0) + new_amount = -new_amount + direction_mode = SYRINGE_DRAW + else + direction_mode = SYRINGE_INJECT + if(isnum(new_amount)) + new_amount = Clamp(new_amount, 0, volume) + transfer_amount = new_amount + +/obj/item/integrated_circuit/reagent/proc/inject_tray(var/obj/machinery/hydroponics/H,var/atom/movable/SO,var/A) + var/datum/reagents/S = new /datum/reagents() //This is a strange way, but I don't know of a better one so I can't fix it at the moment... + S.my_atom = H + SO.reagents.trans_to(S,A) + H.applyChemicals(S) + S.clear_reagents() + qdel(S) + +/obj/item/integrated_circuit/reagent/injector/do_work() + set waitfor = FALSE // 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)||!Adjacent(AM)||busy) + activate_pin(3) + return + if(istype(AM,/obj/machinery/hydroponics)&&(direction_mode==SYRINGE_INJECT)&&(reagents.total_volume))//injection into tray. + inject_tray(AM,src,transfer_amount) + activate_pin(2) + return + if(!AM.reagents) + activate_pin(3) + return + if(direction_mode == SYRINGE_INJECT) + if(!reagents.total_volume) // Empty + activate_pin(3) + return + if(AM.is_injectable()) + if(AM.reagents.total_volume>=AM.reagents.maximum_volume) + activate_pin(3) + return + if(isliving(AM)) + var/mob/living/L = AM + if(!L.can_inject(null, 0)) + activate_pin(3) + return + //Always log attemped injections for admins + var/list/rinject = list() + for(var/datum/reagent/R in reagents.reagent_list) + rinject += R.name + var/contained = english_list(rinject) + add_logs(src, L, "attemped to inject", addition="which had [contained]") //TODO: proper logging (maybe last touched and assembled) + L.visible_message("[src] is trying to inject [L]!", \ + "[src] is trying to inject you!") + busy = TRUE + if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0))) + var/fraction = min(transfer_amount/reagents.total_volume, 1) + reagents.reaction(L, INJECT, fraction) + reagents.trans_to(L, transfer_amount) + L.visible_message("[src] injects [L] with it's needle!", \ + "[src] injects you with it's needle!") + else + busy = FALSE + activate_pin(3) + return + busy = FALSE + else + reagents.trans_to(AM, transfer_amount) + else + activate_pin(3) + return + else + if(reagents.total_volume >= reagents.maximum_volume) // Full + activate_pin(3) + return + var/tramount = Clamp(min(transfer_amount, reagents.maximum_volume - reagents.total_volume), 0, reagents.maximum_volume) + if(isliving(AM)) + var/mob/living/L = AM + L.visible_message("[src] is trying to take a blood sample from [L]!", \ + "[src] is trying to take a blood sample from you!") + busy = TRUE + if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0))) + if(L.transfer_blood_to(src, tramount)) + L.visible_message("[src] takes a blood sample from [L].") + else + busy = FALSE + activate_pin(3) + return + busy = FALSE + else + if(!AM.reagents.total_volume || !AM.is_drawable()) + activate_pin(3) + return + AM.reagents.trans_to(src, tramount) + activate_pin(2) + + + + +/obj/item/integrated_circuit/reagent/pump + name = "reagent pump" + desc = "Moves liquids safely inside a machine, or even nearby it." + 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 \ + outside the machine if it is next to the machine. Note that this cannot be used on entities." + container_type = OPENCONTAINER_1 + complexity = 8 + inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER) + inputs_default = list("3" = 5) + outputs = list() + activators = list("transfer reagents" = IC_PINTYPE_PULSE_IN, "on transfer" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + var/transfer_amount = 10 + var/direction_mode = SYRINGE_INJECT + power_draw_per_use = 10 + +/obj/item/integrated_circuit/reagent/pump/on_data_written() + var/new_amount = get_pin_data(IC_INPUT, 3) + if(new_amount < 0) + new_amount = -new_amount + direction_mode = SYRINGE_DRAW + else + direction_mode = SYRINGE_INJECT + if(isnum(new_amount)) + new_amount = Clamp(new_amount, 0, 50) + transfer_amount = new_amount + +/obj/item/integrated_circuit/reagent/pump/do_work() + var/atom/movable/source = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 2, /atom/movable) + + if(!istype(source) || !istype(target) || !source.reagents) //Invalid input + return + if(Adjacent(source) && Adjacent(target)) + if(istype(target,/obj/machinery/hydroponics)&&(source.reagents.total_volume))//injection into tray. + inject_tray(target,source,transfer_amount) + activate_pin(2) + return + + if(!target.reagents) + return + if(ismob(source) || ismob(target)) + return + if(!source.is_open_container() || !target.is_open_container()) + return + if(direction_mode) + if(target.reagents.maximum_volume - target.reagents.total_volume <= 0) //full + return + source.reagents.trans_to(target, transfer_amount) + else + if(source.reagents.maximum_volume - source.reagents.total_volume <= 0) + return + target.reagents.trans_to(source, transfer_amount) + activate_pin(2) + +/obj/item/integrated_circuit/reagent/storage + name = "reagent storage" + desc = "Stores liquid inside, and away from electrical components. Can store up to 60u." + icon_state = "reagent_storage" + extended_desc = "This is effectively an internal beaker." + container_type = OPENCONTAINER_1 + complexity = 4 + inputs = list() + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF) + activators = list() + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + volume = 60 + + +/obj/item/integrated_circuit/reagent/storage/interact(mob/user) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + ..() + +/obj/item/integrated_circuit/reagent/storage/on_reagent_change() + set_pin_data(IC_OUTPUT, 1, reagents.total_volume) + push_data() + +/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." + icon_state = "reagent_storage_cryo" + extended_desc = "This is effectively an internal cryo beaker." + container_type = OPENCONTAINER_1 + complexity = 8 + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_MATERIALS = 4, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/integrated_circuit/reagent/storage/cryo/New() + . = ..() + reagents.set_reacting(FALSE) + +/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." + icon_state = "reagent_storage_big" + extended_desc = "This is effectively an internal beaker." + container_type = OPENCONTAINER_1 + complexity = 16 + volume = 180 + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_MATERIALS = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + +/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." + icon_state = "reagent_scan" + extended_desc = "Mostly useful for reagent filter." + container_type = OPENCONTAINER_1 + complexity = 8 + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF,"list of reagents" = IC_PINTYPE_LIST) + activators = list("scan" = IC_PINTYPE_PULSE_IN) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_MATERIALS = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + +/obj/item/integrated_circuit/reagent/storage/scan/do_work() + var/cont[0] + for(var/datum/reagent/RE in reagents.reagent_list) + cont += RE.id + set_pin_data(IC_OUTPUT, 3, cont) + push_data() + + +/obj/item/integrated_circuit/reagent/filter + name = "reagent filter" + desc = "Filtering liquids by list of desired or unwanted reagents." + 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.\ + Or it will move only desired reagents if amount is negative, The third pin determines \ + how much reagent is moved per pulse, between 0 and 50. Amount is given for each separate reagent." + container_type = OPENCONTAINER_1 + complexity = 8 + inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER, "list of reagents" = IC_PINTYPE_LIST) + inputs_default = list("3" = 5) + outputs = list() + activators = list("transfer reagents" = IC_PINTYPE_PULSE_IN, "on transfer" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + var/transfer_amount = 10 + var/direction_mode = SYRINGE_INJECT + power_draw_per_use = 10 + +/obj/item/integrated_circuit/reagent/filter/on_data_written() + var/new_amount = get_pin_data(IC_INPUT, 3) + if(new_amount < 0) + new_amount = -new_amount + direction_mode = SYRINGE_DRAW + else + direction_mode = SYRINGE_INJECT + if(isnum(new_amount)) + new_amount = Clamp(new_amount, 0, 50) + transfer_amount = new_amount + +/obj/item/integrated_circuit/reagent/filter/do_work() + var/atom/movable/source = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 2, /atom/movable) + var/list/demand = get_pin_data(IC_INPUT, 4) + if(!istype(source) || !istype(target)) //Invalid input + return + var/turf/T = get_turf(src) + if(source.Adjacent(T) && target.Adjacent(T)) + if(!source.reagents || !target.reagents) + return + if(ismob(source) || ismob(target)) + return + if(!source.is_open_container() || !target.is_open_container()) + return + if(target.reagents.maximum_volume - target.reagents.total_volume <= 0) + return + for(var/datum/reagent/G in source.reagents.reagent_list) + if (!direction_mode) + if(G.id in demand) + source.reagents.trans_id_to(target, G.id, transfer_amount) + else + if(!(G.id in demand)) + source.reagents.trans_id_to(target, G.id, transfer_amount) + activate_pin(2) + push_data() + + + diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm new file mode 100644 index 0000000000..1c0dc470b4 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -0,0 +1,33 @@ +/obj/item/integrated_circuit/smart + category_text = "Smart" + +/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 \ + cannot see the target, it will not be able to calculate the correct direction." + icon_state = "numberpad" + complexity = 25 + inputs = list("target" = IC_PINTYPE_REF) + outputs = list("dir" = IC_PINTYPE_DIR) + activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 5) + power_draw_per_use = 40 + +/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)) + return + var/atom/A = I.data.resolve() + if(!A) + return + if(!(A in view(get_turf(src)))) + push_data() + return // Can't see the target. + + set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A))) + push_data() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm new file mode 100644 index 0000000000..57fc16ccf9 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -0,0 +1,145 @@ +/obj/item/integrated_circuit/time + name = "time circuit" + desc = "Now you can build your own clock!" + complexity = 2 + inputs = list() + outputs = list() + category_text = "Time" + +/obj/item/integrated_circuit/time/delay + name = "two-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 two seconds." + icon_state = "delay-20" + var/delay = 20 + activators = list("incoming"= IC_PINTYPE_PULSE_IN,"outgoing" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 2 + +/obj/item/integrated_circuit/time/delay/do_work() + addtimer(CALLBACK(src, .proc/activate_pin, 2), delay) + +/obj/item/integrated_circuit/time/delay/five_sec + name = "five-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 five seconds." + icon_state = "delay-50" + delay = 50 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/time/delay/one_sec + name = "one-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 one second." + icon_state = "delay-10" + delay = 10 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/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" + delay = 5 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/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" + delay = 1 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/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" + inputs = list("delay time" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/time/delay/custom/do_work() + var/delay_input = get_pin_data(IC_INPUT, 1) + if(delay_input && isnum(delay_input) ) + var/new_delay = Clamp(delay_input ,1 ,36000) //An hour. + delay = new_delay + + ..() + +/obj/item/integrated_circuit/time/ticker + name = "ticker circuit" + desc = "This circuit sends an automatic pulse every four seconds." + icon_state = "tick-m" + complexity = 8 + var/delay = 4 SECONDS + var/next_fire = 0 + var/is_running = FALSE + inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN) + activators = list("outgoing pulse" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 4 + +/obj/item/integrated_circuit/time/ticker/Destroy() + if(is_running) + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/item/integrated_circuit/time/ticker/on_data_written() + var/do_tick = get_pin_data(IC_INPUT, 1) + if(do_tick && !is_running) + is_running = TRUE + tick() + else if(is_running) + is_running = FALSE + + +/obj/item/integrated_circuit/time/ticker/proc/tick() + if(is_running) + addtimer(CALLBACK(src, .proc/tick), delay) + if(world.time > next_fire) + next_fire = world.time + delay + activate_pin(1) + + +/obj/item/integrated_circuit/time/ticker/fast + name = "fast ticker" + desc = "This advanced circuit sends an automatic pulse every two seconds." + icon_state = "tick-f" + complexity = 12 + delay = 2 SECONDS + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 8 + +/obj/item/integrated_circuit/time/ticker/slow + name = "slow ticker" + desc = "This simple circuit sends an automatic pulse every six seconds." + icon_state = "tick-s" + complexity = 4 + delay = 6 SECONDS + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 2 + +/obj/item/integrated_circuit/time/clock + name = "integrated clock" + desc = "Tells you what the local time is, specific to your station or planet." + icon_state = "clock" + inputs = list() + outputs = list( + "time" = IC_PINTYPE_STRING, + "hours" = IC_PINTYPE_NUMBER, + "minutes" = IC_PINTYPE_NUMBER, + "seconds" = IC_PINTYPE_NUMBER + ) + activators = list("get time" = IC_PINTYPE_PULSE_IN, "on time got" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + +/obj/item/integrated_circuit/time/clock/do_work() + var/wtime = world.time + set_pin_data(IC_OUTPUT, 1, time2text(wtime, "hh:mm:ss") ) + set_pin_data(IC_OUTPUT, 2, text2num(time2text(wtime, "hh") ) ) + set_pin_data(IC_OUTPUT, 3, text2num(time2text(wtime, "mm") ) ) + set_pin_data(IC_OUTPUT, 4, text2num(time2text(wtime, "ss") ) ) + push_data() + activate_pin(2) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/trig.dm b/code/modules/integrated_electronics/subtypes/trig.dm new file mode 100644 index 0000000000..8b789ed480 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/trig.dm @@ -0,0 +1,138 @@ +//These circuits do not-so-simple math. +/obj/item/integrated_circuit/trig + complexity = 1 + inputs = list( + "A" = IC_PINTYPE_NUMBER, + "B" = IC_PINTYPE_NUMBER, + "C" = IC_PINTYPE_NUMBER, + "D" = IC_PINTYPE_NUMBER, + "E" = IC_PINTYPE_NUMBER, + "F" = IC_PINTYPE_NUMBER, + "G" = IC_PINTYPE_NUMBER, + "H" = IC_PINTYPE_NUMBER + ) + outputs = list("result" = IC_PINTYPE_NUMBER) + activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT) + category_text = "Trig" + extended_desc = "Input and output are in degrees." + power_draw_per_use = 1 // Still cheap math. + +// Sine // + +/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." + icon_state = "sine" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/trig/sine/do_work() + pull_data() + var/result = null + var/A = get_pin_data(IC_INPUT, 1) + if(!isnull(A)) + result = sin(A) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Cosine // + +/obj/item/integrated_circuit/trig/cosine + name = "cos circuit" + desc = "Outputs the cosine of A." + icon_state = "cosine" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/trig/cosine/do_work() + pull_data() + var/result = null + var/A = get_pin_data(IC_INPUT, 1) + if(!isnull(A)) + result = cos(A) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Tangent // + +/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." + icon_state = "tangent" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/trig/tangent/do_work() + pull_data() + var/result = null + var/A = get_pin_data(IC_INPUT, 1) + if(!isnull(A)) + result = Tan(A) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Cosecant // + +/obj/item/integrated_circuit/trig/cosecant + name = "csc circuit" + desc = "Outputs the cosecant of A." + icon_state = "cosecant" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/trig/cosecant/do_work() + pull_data() + var/result = null + var/A = get_pin_data(IC_INPUT, 1) + if(!isnull(A)) + result = Csc(A) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Secant // + +/obj/item/integrated_circuit/trig/secant + name = "sec circuit" + desc = "Outputs the secant of A. Has nothing to do with the security department." + icon_state = "secant" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/trig/secant/do_work() + pull_data() + var/result = null + var/A = get_pin_data(IC_INPUT, 1) + if(!isnull(A)) + result = Sec(A) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// Cotangent // + +/obj/item/integrated_circuit/trig/cotangent + name = "cot circuit" + desc = "Outputs the cotangent of A." + icon_state = "cotangent" + inputs = list("A" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/trig/cotangent/do_work() + pull_data() + var/result = null + var/A = get_pin_data(IC_INPUT, 1) + if(!isnull(A)) + result = Cot(A) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index b6c4180d76..1bd1eba3c4 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -1,6 +1,3 @@ -#define SYRINGE_DRAW 0 -#define SYRINGE_INJECT 1 - /obj/item/reagent_containers/syringe name = "syringe" desc = "A syringe that can hold up to 15 units." diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 051d7422a0..d2b587cc68 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -91,6 +91,35 @@ other types of metals and chemistry for reagents). build_path = /obj/item/device/paicard category = list("Electronics") +/datum/design/integrated_printer + name = "Integrated circuits printer" + desc = "This machine provides all neccesary things for circuitry." + id = "icprinter" + req_tech = list("programming" = 2,"materials" = 2, "engineering" = 3) + build_type = PROTOLATHE + materials = list(MAT_GLASS = 5000, MAT_METAL = 5000) + build_path = /obj/item/device/integrated_circuit_printer + category = list("Electronics") + +/datum/design/advupdisk + name = "Upgrade disk-advanced circuits" + desc = "Upgrade disk for integrated circuits printer.Allows advanced designs." + id = "udiskadv" + req_tech = list("programming" = 3, "engineering" = 3) + build_type = PROTOLATHE + materials = list(MAT_GLASS = 500, MAT_METAL = 500) + build_path = /obj/item/disk/integrated_circuit/upgrade/advanced + category = list("Electronics") + +/datum/design/cloneupisk + name = "Upgrade disk-assembly cloning" + desc = "Upgrade disk for integrated circuits printer.Allows assembly cloning." + id = "udiskclone" + req_tech = list("programming" = 4, "engineering" = 4) + build_type = PROTOLATHE + materials = list(MAT_GLASS = 500, MAT_METAL = 500) + build_path = /obj/item/disk/integrated_circuit/upgrade/clone + category = list("Electronics") //////////////////////////////////////// //////////Disk Construction Disks/////// diff --git a/code/modules/research/designs/telecomms_designs.dm b/code/modules/research/designs/telecomms_designs.dm index 5822c5b808..a9e89ac9d0 100644 --- a/code/modules/research/designs/telecomms_designs.dm +++ b/code/modules/research/designs/telecomms_designs.dm @@ -10,6 +10,14 @@ build_path = /obj/item/circuitboard/machine/telecomms/receiver category = list("Subspace Telecomms") +/datum/design/board/exonet_node + name = "Machine Design (Exonet Node)" + desc = "Allows for the construction of Exonet Node." + id = "e-node" + req_tech = list("programming" = 2, "engineering" = 3, "bluespace" = 1) + build_path = /obj/item/circuitboard/machine/exonet_node + category = list("Subspace Telecomms") + /datum/design/board/telecomms_bus name = "Machine Design (Bus Mainframe)" desc = "Allows for the construction of Telecommunications Bus Mainframes." diff --git a/config/admins.txt b/config/admins.txt index eda0cd6cfd..b7d8cf0e6b 100644 --- a/config/admins.txt +++ b/config/admins.txt @@ -7,5 +7,133 @@ # NOTE: if the rank-name cannot be found in admin_ranks.txt, they will not be adminned! ~Carn # # NOTE: syntax was changed to allow hyphenation of ranknames, since spaces are stripped. # ############################################################################################### +<<<<<<< HEAD -Jayehh = Host \ No newline at end of file +Jayehh = Host +======= +Optimumtact = Host +NewSta = Game Master +Expletives = Game Master +kingofkosmos = Game Master +MrStonedOne = Game Master +microscopics = Game Master +Gun Hog = Game Master +KorPhaeron = Game Master +razharas = Game Master +Lordpidey = Game Master +Niknakflak = Game Master +rolan7 = Game Master +quarxink = Game Master +adrix89 = Game Master +tle = Game Master +xsi = Game Master +scaredofshadows = Game Master +neofite = Game Master +trubblebass = Game Master +mport2004 = Game Master +deuryn = Game Master +agouri = Game Master +errorage = Game Master +superxpdude = Game Master +petethegoat = Game Master +korphaeron = Game Master +nodrak = Game Master +carnwennan = Game Master +ikarrus = Game Master +cheridan = Game Master +giacomand = Game Master +rockdtben = Game Master +sieve = Game Master +aranclanos = Game Master +intigracy = Game Master +dumpdavidson = Game Master +kazeespada = Game Master +malkevin = Game Master +incoming = Game Master +demas = Game Master +fleure = Game Master +ricotez = Game Master +misterperson = Game Master +crimsonvision = Game Master +iamgoofball = Game Master +zelacks = Game Master +androidsfv = Game Master +miggles = Game Master +jordie0608 = Game Master +s0ldi3rkr4s0 = Game Master +ergovisavi = Game Master +vistapowa = Game Master +miauw62 = Game Master +kazeespada = Game Master +rumia29 = Game Master +bobylein = Game Master +sirbayer = Game Master +hornygranny = Game Master +yota = Game Master +firecage = Game Master +donkieyo = Game Master +argoneus = Game Master +paprka = Game Master +cookingboy3 = Game Master +limeliz = Game Master +steelpoint = Game Master +phil235 = Game Master +CorruptComputer = Game Master +xxnoob = Game Master +tkdrg = Game Master +Cuboos = Game Master +thunder12345 = Game Master +wjohnston = Game Master +mandurrh = Game Master +thurgatar = Game Master +xerux = Game Master +dannno = Game Master +lo6a4evskiy = Game Master +vekter = Game Master +Ahammer18 = Game Master +ACCount12 = Game Master +fayrik = Game Master +shadowlight213 = Game Master +drovidicorv = Game Master +Dunc = Game Master +MMMiracles = Game Master +bear1ake = Game Master +CoreOverload = Game Master +Jalleo = Game Master +ChangelingRain = Game Master +FoxPMcCloud = Game Master +Xhuis = Game Master +Astralenigma = Game Master +Tokiko1 = Game Master +SuperSayu = Game Master +Lzimann = Game Master +As334 = Game Master +neersighted = Game Master +Swankcookie = Game Master +Ressler = Game Master +Folix = Game Master +Bawhoppennn = Game Master +Anturke = Host +Lumipharon = Game Master +bgobandit = Game Master +coiax = Game Master +RandomMarine = Game Master +PKPenguin321 = Game Master +TechnoAlchemist = Game Master +Aloraydrel = Game Master +Quiltyquilty = Game Master +SnipeDragon = Game Master +Fjeld = Game Master +kevinz000 = Game Master +Tacolizard = Game Master +TrustyGun = Game Master +Cyberboss = Game Master +PJB3005 = Game Master +Sweaterkittens = Game Master +Feemjmeem = Game Master +JStheguy = Game Master +excessiveuseofcobby = Game Master +Plizzard = Game Master +octareenroon91 = Game Master +Serpentarium = Game Master +>>>>>>> bdfbafd... [READY]integrated circuitry port+upgrade. (#32481) diff --git a/config/game_options.txt b/config/game_options.txt index d3fa274ed8..7794fb9376 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -504,6 +504,7 @@ MICE_ROUNDSTART 10 ## If the percentage of players alive (doesn't count conversions) drops below this threshold the emergency shuttle will be forcefully called (provided it can be) #EMERGENCY_SHUTTLE_AUTOCALL_THRESHOLD 0.2 +<<<<<<< HEAD ## CREW OBJECTIVES ## ## Comment to disable objectives for innocent crew members. ALLOW_CREW_OBJECTIVES @@ -514,3 +515,7 @@ ALLOW_MISCREANTS ## Uncomment to let miscreants spawn during Extended. I hold no responsibility for fun that may occur while this is enabled. #ALLOW_EXTENDED_MISCREANTS +======= +## Determines if players are allowed to print integrated circuits, uncomment to allow. +#IC_PRINTING +>>>>>>> bdfbafd... [READY]integrated circuitry port+upgrade. (#32481) diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi index 64a877c2a1257e30f83470f89495a34abbb82290..38edd8dc7dafeeaa39eafe2bc344e75db021c8b4 100644 GIT binary patch delta 7471 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 D+E(hR diff --git a/icons/obj/electronic_assemblies.dmi b/icons/obj/electronic_assemblies.dmi new file mode 100644 index 0000000000000000000000000000000000000000..8faa77ad02105b5787ce3b64b0e7143f9429b4c0 GIT binary patch literal 41014 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%0D)AM4B-Tj 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 literal 0 HcmV?d00001 diff --git a/sound/machines/synth_yes.ogg b/sound/machines/synth_yes.ogg new file mode 100644 index 0000000000000000000000000000000000000000..300cad132ede46bd5cf1523c157aa72184680b74 GIT binary patch literal 6867 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 d$Ick-2_*fwKqC!$(wicaiWhr?HuDJx{11i!1 + + + Turn {{data.toggle ? "off" : "on"}} + + + + {{#each data.logs}} + {{.}} + {{/each}} + + \ No newline at end of file