diff --git a/code/__DEFINES/integrated_electronics.dm b/code/__DEFINES/integrated_electronics.dm new file mode 100644 index 000000000000..e2467af26879 --- /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 787aee540828..f558511954b2 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 d7f7b33a2e68..143761d3f42e 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -469,3 +469,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 b8579e7dbe39..7eaf7e089d47 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 d5d311ab6446..56be39a0a811 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -577,3 +577,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 f7a5c095c8d2..86dcc1a3f1ad 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -508,6 +508,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() @@ -1427,6 +1428,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 607ab80d9930..b7cf723c2c56 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -258,3 +258,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 000000000000..cb10503a8011 --- /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 000000000000..85088ff903a2 --- /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 8af2491451b9..d8adba652c8f 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) @@ -17,3 +16,4 @@ /datum/weakref/proc/resolve() var/datum/D = locate(reference) return (!QDELETED(D) && D.weak_reference == src) ? D : null + diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 75d2ffaea238..e3104c71e67b 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 000000000000..b521f404ce13 --- /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 dc5a57838198..ee0f28cab99e 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 6f4a59a2ab87..8a52c4ccaba6 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -8,6 +8,9 @@ * */ + + + /obj/item/device/multitool name = "multitool" desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." @@ -24,6 +27,69 @@ 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. diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 994109675d9a..e169f656c4cd 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 000000000000..fb48bb5ae60c --- /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 000000000000..d992718bc745 --- /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 000000000000..77deaa94dbba --- /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 000000000000..e2fed297e6d4 --- /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 000000000000..b8fe3e3cbb10 --- /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 000000000000..ba0dbdcf3161 --- /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 000000000000..3dc3f2d6efaf --- /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 000000000000..d2c9d1d3ae0b --- /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 000000000000..46b1f4c759f9 --- /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 000000000000..5c6a42590b40 --- /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 000000000000..703afe5825ff --- /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 000000000000..4ce5597af8f6 --- /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 000000000000..f4ad34f4d7ee --- /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 000000000000..319ac2de06bd --- /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 000000000000..461965f254bc --- /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 000000000000..ee0398413e2b --- /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 000000000000..c1caba00cd3d --- /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 000000000000..02f03d48d7c4 --- /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 000000000000..f99912a149fb --- /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 000000000000..7dec779f838f --- /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 000000000000..373a059f451c --- /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 000000000000..28fc45cfeabc --- /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 000000000000..eef002c4cf97 --- /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 000000000000..159898926b35 --- /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 000000000000..0fa7fb236438 --- /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 000000000000..f8eda5982c2f --- /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 000000000000..548ea3fa1d77 --- /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 000000000000..75ed9d52162a --- /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 000000000000..db39e067ade9 --- /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 000000000000..e6eeb3521c98 --- /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 000000000000..1c0dc470b41e --- /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 000000000000..57fc16ccf944 --- /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 000000000000..8b789ed4802d --- /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 b6c4180d7699..1bd1eba3c402 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 051d7422a013..d2b587cc68c9 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 5822c5b8083e..a9e89ac9d0f3 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 1e5de5505079..d59aad6b1c24 100644 --- a/config/admins.txt +++ b/config/admins.txt @@ -130,4 +130,5 @@ Feemjmeem = Game Master JStheguy = Game Master excessiveuseofcobby = Game Master Plizzard = Game Master -octareenroon91 = Game Master \ No newline at end of file +octareenroon91 = Game Master +Serpentarium = Game Master \ No newline at end of file diff --git a/config/game_options.txt b/config/game_options.txt index f610495632ed..85a9bc6d5b4c 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -490,3 +490,6 @@ 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 + +## Determines if players are allowed to print integrated circuits, uncomment to allow. +#IC_PRINTING \ No newline at end of file diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi index 64a877c2a125..38edd8dc7daf 100644 Binary files a/icons/obj/abductor.dmi and b/icons/obj/abductor.dmi differ diff --git a/icons/obj/electronic_assemblies.dmi b/icons/obj/electronic_assemblies.dmi new file mode 100644 index 000000000000..8faa77ad0210 Binary files /dev/null and b/icons/obj/electronic_assemblies.dmi differ diff --git a/icons/obj/electronic_assemblies2.dmi b/icons/obj/electronic_assemblies2.dmi new file mode 100644 index 000000000000..6cfb51affcc5 Binary files /dev/null and b/icons/obj/electronic_assemblies2.dmi differ diff --git a/icons/obj/stationobjs.dmi b/icons/obj/stationobjs.dmi index 0f4cda5d8bf1..e4c1de028241 100644 Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.dmi differ diff --git a/sound/machines/synth_no.ogg b/sound/machines/synth_no.ogg new file mode 100644 index 000000000000..f0d2c3bfb0c4 Binary files /dev/null and b/sound/machines/synth_no.ogg differ diff --git a/sound/machines/synth_yes.ogg b/sound/machines/synth_yes.ogg new file mode 100644 index 000000000000..300cad132ede Binary files /dev/null and b/sound/machines/synth_yes.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 56f747cac49c..d5c87affdd8b 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -43,6 +43,7 @@ #include "code\__DEFINES\flags.dm" #include "code\__DEFINES\food.dm" #include "code\__DEFINES\hud.dm" +#include "code\__DEFINES\integrated_electronics.dm" #include "code\__DEFINES\inventory.dm" #include "code\__DEFINES\is_helpers.dm" #include "code\__DEFINES\jobs.dm" @@ -225,6 +226,7 @@ #include "code\controllers\subsystem\title.dm" #include "code\controllers\subsystem\vote.dm" #include "code\controllers\subsystem\weather.dm" +#include "code\controllers\subsystem\processing\circuit.dm" #include "code\controllers\subsystem\processing\fastprocess.dm" #include "code\controllers\subsystem\processing\fields.dm" #include "code\controllers\subsystem\processing\flightpacks.dm" @@ -244,6 +246,7 @@ #include "code\datums\dna.dm" #include "code\datums\dog_fashion.dm" #include "code\datums\emotes.dm" +#include "code\datums\EPv2.dm" #include "code\datums\explosion.dm" #include "code\datums\forced_movement.dm" #include "code\datums\holocall.dm" @@ -577,6 +580,7 @@ #include "code\game\machinery\dna_scanner.dm" #include "code\game\machinery\doppler_array.dm" #include "code\game\machinery\droneDispenser.dm" +#include "code\game\machinery\exonet_node.dm" #include "code\game\machinery\firealarm.dm" #include "code\game\machinery\flasher.dm" #include "code\game\machinery\gulag_item_reclaimer.dm" @@ -1468,6 +1472,39 @@ #include "code\modules\hydroponics\grown\tobacco.dm" #include "code\modules\hydroponics\grown\tomato.dm" #include "code\modules\hydroponics\grown\towercap.dm" +#include "code\modules\integrated_electronics\core\analyzer.dm" +#include "code\modules\integrated_electronics\core\assemblies.dm" +#include "code\modules\integrated_electronics\core\debugger.dm" +#include "code\modules\integrated_electronics\core\helpers.dm" +#include "code\modules\integrated_electronics\core\integrated_circuit.dm" +#include "code\modules\integrated_electronics\core\pins.dm" +#include "code\modules\integrated_electronics\core\prefab.dm" +#include "code\modules\integrated_electronics\core\printer.dm" +#include "code\modules\integrated_electronics\core\wirer.dm" +#include "code\modules\integrated_electronics\core\special_pins\boolean_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\char_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\color_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\dir_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\list_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\number_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\ref_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\string_pin.dm" +#include "code\modules\integrated_electronics\passive\passive.dm" +#include "code\modules\integrated_electronics\passive\power.dm" +#include "code\modules\integrated_electronics\subtypes\arithmetic.dm" +#include "code\modules\integrated_electronics\subtypes\converters.dm" +#include "code\modules\integrated_electronics\subtypes\data_transfer.dm" +#include "code\modules\integrated_electronics\subtypes\input.dm" +#include "code\modules\integrated_electronics\subtypes\lists.dm" +#include "code\modules\integrated_electronics\subtypes\logic.dm" +#include "code\modules\integrated_electronics\subtypes\manipulation.dm" +#include "code\modules\integrated_electronics\subtypes\memory.dm" +#include "code\modules\integrated_electronics\subtypes\output.dm" +#include "code\modules\integrated_electronics\subtypes\power.dm" +#include "code\modules\integrated_electronics\subtypes\reagents.dm" +#include "code\modules\integrated_electronics\subtypes\smart.dm" +#include "code\modules\integrated_electronics\subtypes\time.dm" +#include "code\modules\integrated_electronics\subtypes\trig.dm" #include "code\modules\jobs\access.dm" #include "code\modules\jobs\job_exp.dm" #include "code\modules\jobs\jobs.dm" diff --git a/tgui/assets/tgui.js b/tgui/assets/tgui.js index a4a6f5fc363b..e054b21845e6 100644 --- a/tgui/assets/tgui.js +++ b/tgui/assets/tgui.js @@ -1,17 +1,18 @@ -require=function t(e,n,a){function r(o,s){if(!n[o]){if(!e[o]){var p="function"==typeof require&&require;if(!s&&p)return p(o,!0);if(i)return i(o,!0);var u=Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return r(n?n:t)},c,c.exports,t,e,n,a)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o2?u[2]:void 0,l=Math.min((void 0===c?o:r(c,o))-p,o-s),d=1;for(s>p&&p+l>s&&(d=-1,p+=l-1,s+=l-1);l-- >0;)p in n?n[s]=n[p]:delete n[s],s+=d,p+=d;return n}},{76:76,79:79,80:80}],6:[function(t,e,n){"use strict";var a=t(80),r=t(76),i=t(79);e.exports=[].fill||function(t){for(var e=a(this),n=i(e.length),o=arguments,s=o.length,p=r(s>1?o[1]:void 0,n),u=s>2?o[2]:void 0,c=void 0===u?n:r(u,n);c>p;)e[p++]=t;return e}},{76:76,79:79,80:80}],7:[function(t,e,n){var a=t(78),r=t(79),i=t(76);e.exports=function(t){return function(e,n,o){var s,p=a(e),u=r(p.length),c=i(o,u);if(t&&n!=n){for(;u>c;)if(s=p[c++],s!=s)return!0}else for(;u>c;c++)if((t||c in p)&&p[c]===n)return t||c;return!t&&-1}}},{76:76,78:78,79:79}],8:[function(t,e,n){var a=t(17),r=t(34),i=t(80),o=t(79),s=t(9);e.exports=function(t){var e=1==t,n=2==t,p=3==t,u=4==t,c=6==t,l=5==t||c;return function(d,f,h){for(var m,v,g=i(d),b=r(g),y=a(f,h,3),x=o(b.length),_=0,w=e?s(d,x):n?s(d,0):void 0;x>_;_++)if((l||_ in b)&&(m=b[_],v=y(m,_,g),t))if(e)w[_]=v;else if(v)switch(t){case 3:return!0;case 5:return m;case 6:return _;case 2:w.push(m)}else if(u)return!1;return c?-1:p||u?u:w}}},{17:17,34:34,79:79,80:80,9:9}],9:[function(t,e,n){var a=t(38),r=t(36),i=t(83)("species");e.exports=function(t,e){var n;return r(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!r(n.prototype)||(n=void 0),a(n)&&(n=n[i],null===n&&(n=void 0))),new(void 0===n?Array:n)(e)}},{36:36,38:38,83:83}],10:[function(t,e,n){var a=t(11),r=t(83)("toStringTag"),i="Arguments"==a(function(){return arguments}());e.exports=function(t){var e,n,o;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=(e=Object(t))[r])?n:i?a(e):"Object"==(o=a(e))&&"function"==typeof e.callee?"Arguments":o}},{11:11,83:83}],11:[function(t,e,n){var a={}.toString;e.exports=function(t){return a.call(t).slice(8,-1)}},{}],12:[function(t,e,n){"use strict";var a=t(46),r=t(31),i=t(60),o=t(17),s=t(69),p=t(18),u=t(27),c=t(42),l=t(44),d=t(82)("id"),f=t(30),h=t(38),m=t(65),v=t(19),g=Object.isExtensible||h,b=v?"_s":"size",y=0,x=function(t,e){if(!h(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!f(t,d)){if(!g(t))return"F";if(!e)return"E";r(t,d,++y)}return"O"+t[d]},_=function(t,e){var n,a=x(e);if("F"!==a)return t._i[a];for(n=t._f;n;n=n.n)if(n.k==e)return n};e.exports={getConstructor:function(t,e,n,r){var c=t(function(t,i){s(t,c,e),t._i=a.create(null),t._f=void 0,t._l=void 0,t[b]=0,void 0!=i&&u(i,n,t[r],t)});return i(c.prototype,{clear:function(){for(var t=this,e=t._i,n=t._f;n;n=n.n)n.r=!0,n.p&&(n.p=n.p.n=void 0),delete e[n.i];t._f=t._l=void 0,t[b]=0},"delete":function(t){var e=this,n=_(e,t);if(n){var a=n.n,r=n.p;delete e._i[n.i],n.r=!0,r&&(r.n=a),a&&(a.p=r),e._f==n&&(e._f=a),e._l==n&&(e._l=r),e[b]--}return!!n},forEach:function(t){for(var e,n=o(t,arguments.length>1?arguments[1]:void 0,3);e=e?e.n:this._f;)for(n(e.v,e.k,this);e&&e.r;)e=e.p},has:function(t){return!!_(this,t)}}),v&&a.setDesc(c.prototype,"size",{get:function(){return p(this[b])}}),c},def:function(t,e,n){var a,r,i=_(t,e);return i?i.v=n:(t._l=i={i:r=x(e,!0),k:e,v:n,p:a=t._l,n:void 0,r:!1},t._f||(t._f=i),a&&(a.n=i),t[b]++,"F"!==r&&(t._i[r]=i)),t},getEntry:_,setStrong:function(t,e,n){c(t,e,function(t,e){this._t=t,this._k=e,this._l=void 0},function(){for(var t=this,e=t._k,n=t._l;n&&n.r;)n=n.p;return t._t&&(t._l=n=n?n.n:t._t._f)?"keys"==e?l(0,n.k):"values"==e?l(0,n.v):l(0,[n.k,n.v]):(t._t=void 0,l(1))},n?"entries":"values",!n,!0),m(e)}}},{17:17,18:18,19:19,27:27,30:30,31:31,38:38,42:42,44:44,46:46,60:60,65:65,69:69,82:82}],13:[function(t,e,n){var a=t(27),r=t(10);e.exports=function(t){return function(){if(r(this)!=t)throw TypeError(t+"#toJSON isn't generic");var e=[];return a(this,!1,e.push,e),e}}},{10:10,27:27}],14:[function(t,e,n){"use strict";var a=t(31),r=t(60),i=t(4),o=t(38),s=t(69),p=t(27),u=t(8),c=t(30),l=t(82)("weak"),d=Object.isExtensible||o,f=u(5),h=u(6),m=0,v=function(t){return t._l||(t._l=new g)},g=function(){this.a=[]},b=function(t,e){return f(t.a,function(t){return t[0]===e})};g.prototype={get:function(t){var e=b(this,t);return e?e[1]:void 0},has:function(t){return!!b(this,t)},set:function(t,e){var n=b(this,t);n?n[1]=e:this.a.push([t,e])},"delete":function(t){var e=h(this.a,function(e){return e[0]===t});return~e&&this.a.splice(e,1),!!~e}},e.exports={getConstructor:function(t,e,n,a){var i=t(function(t,r){s(t,i,e),t._i=m++,t._l=void 0,void 0!=r&&p(r,n,t[a],t)});return r(i.prototype,{"delete":function(t){return o(t)?d(t)?c(t,l)&&c(t[l],this._i)&&delete t[l][this._i]:v(this)["delete"](t):!1},has:function(t){return o(t)?d(t)?c(t,l)&&c(t[l],this._i):v(this).has(t):!1}}),i},def:function(t,e,n){return d(i(e))?(c(e,l)||a(e,l,{}),e[l][t._i]=n):v(t).set(e,n),t},frozenStore:v,WEAK:l}},{27:27,30:30,31:31,38:38,4:4,60:60,69:69,8:8,82:82}],15:[function(t,e,n){"use strict";var a=t(29),r=t(22),i=t(61),o=t(60),s=t(27),p=t(69),u=t(38),c=t(24),l=t(43),d=t(66);e.exports=function(t,e,n,f,h,m){var v=a[t],g=v,b=h?"set":"add",y=g&&g.prototype,x={},_=function(t){var e=y[t];i(y,t,"delete"==t?function(t){return m&&!u(t)?!1:e.call(this,0===t?0:t)}:"has"==t?function(t){return m&&!u(t)?!1:e.call(this,0===t?0:t)}:"get"==t?function(t){return m&&!u(t)?void 0:e.call(this,0===t?0:t)}:"add"==t?function(t){return e.call(this,0===t?0:t),this}:function(t,n){return e.call(this,0===t?0:t,n),this})};if("function"==typeof g&&(m||y.forEach&&!c(function(){(new g).entries().next()}))){var w,k=new g,E=k[b](m?{}:-0,1)!=k,S=c(function(){k.has(1)}),C=l(function(t){new g(t)});C||(g=e(function(e,n){p(e,g,t);var a=new v;return void 0!=n&&s(n,h,a[b],a),a}),g.prototype=y,y.constructor=g),m||k.forEach(function(t,e){w=1/e===-(1/0)}),(S||w)&&(_("delete"),_("has"),h&&_("get")),(w||E)&&_(b),m&&y.clear&&delete y.clear}else g=f.getConstructor(e,t,h,b),o(g.prototype,n);return d(g,t),x[t]=g,r(r.G+r.W+r.F*(g!=v),x),m||f.setStrong(g,t,h),g}},{22:22,24:24,27:27,29:29,38:38,43:43,60:60,61:61,66:66,69:69}],16:[function(t,e,n){var a=e.exports={version:"1.2.6"};"number"==typeof __e&&(__e=a)},{}],17:[function(t,e,n){var a=t(2);e.exports=function(t,e,n){if(a(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,a){return t.call(e,n,a)};case 3:return function(n,a,r){return t.call(e,n,a,r)}}return function(){return t.apply(e,arguments)}}},{2:2}],18:[function(t,e,n){e.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},{}],19:[function(t,e,n){e.exports=!t(24)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},{24:24}],20:[function(t,e,n){var a=t(38),r=t(29).document,i=a(r)&&a(r.createElement);e.exports=function(t){return i?r.createElement(t):{}}},{29:29,38:38}],21:[function(t,e,n){var a=t(46);e.exports=function(t){var e=a.getKeys(t),n=a.getSymbols;if(n)for(var r,i=n(t),o=a.isEnum,s=0;i.length>s;)o.call(t,r=i[s++])&&e.push(r);return e}},{46:46}],22:[function(t,e,n){var a=t(29),r=t(16),i=t(31),o=t(61),s=t(17),p="prototype",u=function(t,e,n){var c,l,d,f,h=t&u.F,m=t&u.G,v=t&u.S,g=t&u.P,b=t&u.B,y=m?a:v?a[e]||(a[e]={}):(a[e]||{})[p],x=m?r:r[e]||(r[e]={}),_=x[p]||(x[p]={});m&&(n=e);for(c in n)l=!h&&y&&c in y,d=(l?y:n)[c],f=b&&l?s(d,a):g&&"function"==typeof d?s(Function.call,d):d,y&&!l&&o(y,c,d),x[c]!=d&&i(x,c,f),g&&_[c]!=d&&(_[c]=d)};a.core=r,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,e.exports=u},{16:16,17:17,29:29,31:31,61:61}],23:[function(t,e,n){var a=t(83)("match");e.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[a]=!1,!"/./"[t](e)}catch(r){}}return!0}},{83:83}],24:[function(t,e,n){e.exports=function(t){try{return!!t()}catch(e){return!0}}},{}],25:[function(t,e,n){"use strict";var a=t(31),r=t(61),i=t(24),o=t(18),s=t(83);e.exports=function(t,e,n){var p=s(t),u=""[t];i(function(){var e={};return e[p]=function(){return 7},7!=""[t](e)})&&(r(String.prototype,t,n(o,p,u)),a(RegExp.prototype,p,2==e?function(t,e){return u.call(t,this,e)}:function(t){return u.call(t,this)}))}},{18:18,24:24,31:31,61:61,83:83}],26:[function(t,e,n){"use strict";var a=t(4);e.exports=function(){var t=a(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},{4:4}],27:[function(t,e,n){var a=t(17),r=t(40),i=t(35),o=t(4),s=t(79),p=t(84);e.exports=function(t,e,n,u){var c,l,d,f=p(t),h=a(n,u,e?2:1),m=0;if("function"!=typeof f)throw TypeError(t+" is not iterable!");if(i(f))for(c=s(t.length);c>m;m++)e?h(o(l=t[m])[0],l[1]):h(t[m]);else for(d=f.call(t);!(l=d.next()).done;)r(d,h,l.value,e)}},{17:17,35:35,4:4,40:40,79:79,84:84}],28:[function(t,e,n){var a=t(78),r=t(46).getNames,i={}.toString,o="object"==typeof window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(t){try{return r(t)}catch(e){return o.slice()}};e.exports.get=function(t){return o&&"[object Window]"==i.call(t)?s(t):r(a(t))}},{46:46,78:78}],29:[function(t,e,n){var a=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=a)},{}],30:[function(t,e,n){var a={}.hasOwnProperty;e.exports=function(t,e){return a.call(t,e)}},{}],31:[function(t,e,n){var a=t(46),r=t(59);e.exports=t(19)?function(t,e,n){return a.setDesc(t,e,r(1,n))}:function(t,e,n){return t[e]=n,t}},{19:19,46:46,59:59}],32:[function(t,e,n){e.exports=t(29).document&&document.documentElement},{29:29}],33:[function(t,e,n){e.exports=function(t,e,n){var a=void 0===n;switch(e.length){case 0:return a?t():t.call(n);case 1:return a?t(e[0]):t.call(n,e[0]);case 2:return a?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return a?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return a?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3])}return t.apply(n,e)}},{}],34:[function(t,e,n){var a=t(11);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==a(t)?t.split(""):Object(t)}},{11:11}],35:[function(t,e,n){var a=t(45),r=t(83)("iterator"),i=Array.prototype;e.exports=function(t){return void 0!==t&&(a.Array===t||i[r]===t)}},{45:45,83:83}],36:[function(t,e,n){var a=t(11);e.exports=Array.isArray||function(t){return"Array"==a(t)}},{11:11}],37:[function(t,e,n){var a=t(38),r=Math.floor;e.exports=function(t){return!a(t)&&isFinite(t)&&r(t)===t}},{38:38}],38:[function(t,e,n){e.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},{}],39:[function(t,e,n){var a=t(38),r=t(11),i=t(83)("match");e.exports=function(t){var e;return a(t)&&(void 0!==(e=t[i])?!!e:"RegExp"==r(t))}},{11:11,38:38,83:83}],40:[function(t,e,n){var a=t(4);e.exports=function(t,e,n,r){try{return r?e(a(n)[0],n[1]):e(n)}catch(i){var o=t["return"];throw void 0!==o&&a(o.call(t)),i}}},{4:4}],41:[function(t,e,n){"use strict";var a=t(46),r=t(59),i=t(66),o={};t(31)(o,t(83)("iterator"),function(){return this}),e.exports=function(t,e,n){t.prototype=a.create(o,{next:r(1,n)}),i(t,e+" Iterator")}},{31:31,46:46,59:59,66:66,83:83}],42:[function(t,e,n){"use strict";var a=t(48),r=t(22),i=t(61),o=t(31),s=t(30),p=t(45),u=t(41),c=t(66),l=t(46).getProto,d=t(83)("iterator"),f=!([].keys&&"next"in[].keys()),h="@@iterator",m="keys",v="values",g=function(){return this};e.exports=function(t,e,n,b,y,x,_){u(n,e,b);var w,k,E=function(t){if(!f&&t in A)return A[t];switch(t){case m:return function(){return new n(this,t)};case v:return function(){return new n(this,t)}}return function(){return new n(this,t)}},S=e+" Iterator",C=y==v,P=!1,A=t.prototype,O=A[d]||A[h]||y&&A[y],T=O||E(y);if(O){var R=l(T.call(new t));c(R,S,!0),!a&&s(A,h)&&o(R,d,g),C&&O.name!==v&&(P=!0,T=function(){return O.call(this)})}if(a&&!_||!f&&!P&&A[d]||o(A,d,T),p[e]=T,p[S]=g,y)if(w={values:C?T:E(v),keys:x?T:E(m),entries:C?E("entries"):T},_)for(k in w)k in A||i(A,k,w[k]);else r(r.P+r.F*(f||P),e,w);return w}},{22:22,30:30,31:31,41:41,45:45,46:46,48:48,61:61,66:66,83:83}],43:[function(t,e,n){var a=t(83)("iterator"),r=!1;try{var i=[7][a]();i["return"]=function(){r=!0},Array.from(i,function(){throw 2})}catch(o){}e.exports=function(t,e){if(!e&&!r)return!1;var n=!1;try{var i=[7],o=i[a]();o.next=function(){return{done:n=!0}},i[a]=function(){return o},t(i)}catch(s){}return n}},{83:83}],44:[function(t,e,n){e.exports=function(t,e){return{value:e,done:!!t}}},{}],45:[function(t,e,n){e.exports={}},{}],46:[function(t,e,n){var a=Object;e.exports={create:a.create,getProto:a.getPrototypeOf,isEnum:{}.propertyIsEnumerable,getDesc:a.getOwnPropertyDescriptor,setDesc:a.defineProperty,setDescs:a.defineProperties,getKeys:a.keys,getNames:a.getOwnPropertyNames,getSymbols:a.getOwnPropertySymbols,each:[].forEach}},{}],47:[function(t,e,n){var a=t(46),r=t(78);e.exports=function(t,e){for(var n,i=r(t),o=a.getKeys(i),s=o.length,p=0;s>p;)if(i[n=o[p++]]===e)return n}},{46:46,78:78}],48:[function(t,e,n){e.exports=!1},{}],49:[function(t,e,n){e.exports=Math.expm1||function(t){return 0==(t=+t)?t:t>-1e-6&&1e-6>t?t+t*t/2:Math.exp(t)-1}},{}],50:[function(t,e,n){e.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&1e-8>t?t-t*t/2:Math.log(1+t)}},{}],51:[function(t,e,n){e.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:0>t?-1:1}},{}],52:[function(t,e,n){var a,r,i,o=t(29),s=t(75).set,p=o.MutationObserver||o.WebKitMutationObserver,u=o.process,c=o.Promise,l="process"==t(11)(u),d=function(){var t,e,n;for(l&&(t=u.domain)&&(u.domain=null,t.exit());a;)e=a.domain,n=a.fn,e&&e.enter(),n(),e&&e.exit(),a=a.next;r=void 0,t&&t.enter()};if(l)i=function(){u.nextTick(d)};else if(p){var f=1,h=document.createTextNode("");new p(d).observe(h,{characterData:!0}),i=function(){h.data=f=-f}}else i=c&&c.resolve?function(){c.resolve().then(d)}:function(){s.call(o,d)};e.exports=function(t){var e={fn:t,next:void 0,domain:l&&u.domain};r&&(r.next=e),a||(a=e,i()),r=e}},{11:11,29:29,75:75}],53:[function(t,e,n){var a=t(46),r=t(80),i=t(34);e.exports=t(24)(function(){var t=Object.assign,e={},n={},a=Symbol(),r="abcdefghijklmnopqrst";return e[a]=7,r.split("").forEach(function(t){n[t]=t}),7!=t({},e)[a]||Object.keys(t({},n)).join("")!=r})?function(t,e){for(var n=r(t),o=arguments,s=o.length,p=1,u=a.getKeys,c=a.getSymbols,l=a.isEnum;s>p;)for(var d,f=i(o[p++]),h=c?u(f).concat(c(f)):u(f),m=h.length,v=0;m>v;)l.call(f,d=h[v++])&&(n[d]=f[d]);return n}:Object.assign},{24:24,34:34,46:46,80:80}],54:[function(t,e,n){var a=t(22),r=t(16),i=t(24);e.exports=function(t,e){var n=(r.Object||{})[t]||Object[t],o={};o[t]=e(n),a(a.S+a.F*i(function(){n(1)}),"Object",o)}},{16:16,22:22,24:24}],55:[function(t,e,n){var a=t(46),r=t(78),i=a.isEnum;e.exports=function(t){return function(e){for(var n,o=r(e),s=a.getKeys(o),p=s.length,u=0,c=[];p>u;)i.call(o,n=s[u++])&&c.push(t?[n,o[n]]:o[n]);return c}}},{46:46,78:78}],56:[function(t,e,n){var a=t(46),r=t(4),i=t(29).Reflect;e.exports=i&&i.ownKeys||function(t){var e=a.getNames(r(t)),n=a.getSymbols;return n?e.concat(n(t)):e}},{29:29,4:4,46:46}],57:[function(t,e,n){"use strict";var a=t(58),r=t(33),i=t(2);e.exports=function(){for(var t=i(this),e=arguments.length,n=Array(e),o=0,s=a._,p=!1;e>o;)(n[o]=arguments[o++])===s&&(p=!0);return function(){var a,i=this,o=arguments,u=o.length,c=0,l=0;if(!p&&!u)return r(t,n,i);if(a=n.slice(),p)for(;e>c;c++)a[c]===s&&(a[c]=o[l++]);for(;u>l;)a.push(o[l++]);return r(t,a,i)}}},{2:2,33:33,58:58}],58:[function(t,e,n){e.exports=t(29)},{29:29}],59:[function(t,e,n){e.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},{}],60:[function(t,e,n){var a=t(61);e.exports=function(t,e){for(var n in e)a(t,n,e[n]);return t}},{61:61}],61:[function(t,e,n){var a=t(29),r=t(31),i=t(82)("src"),o="toString",s=Function[o],p=(""+s).split(o);t(16).inspectSource=function(t){return s.call(t)},(e.exports=function(t,e,n,o){"function"==typeof n&&(n.hasOwnProperty(i)||r(n,i,t[e]?""+t[e]:p.join(e+"")),n.hasOwnProperty("name")||r(n,"name",e)),t===a?t[e]=n:(o||delete t[e],r(t,e,n))})(Function.prototype,o,function(){return"function"==typeof this&&this[i]||s.call(this)})},{16:16,29:29,31:31,82:82}],62:[function(t,e,n){e.exports=function(t,e){var n=e===Object(e)?function(t){return e[t]}:e;return function(e){return(e+"").replace(t,n)}}},{}],63:[function(t,e,n){e.exports=Object.is||function(t,e){return t===e?0!==t||1/t===1/e:t!=t&&e!=e}},{}],64:[function(t,e,n){var a=t(46).getDesc,r=t(38),i=t(4),o=function(t,e){if(i(t),!r(e)&&null!==e)throw TypeError(e+": can't set as prototype!")};e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,n,r){try{r=t(17)(Function.call,a(Object.prototype,"__proto__").set,2),r(e,[]),n=!(e instanceof Array)}catch(i){n=!0}return function(t,e){return o(t,e),n?t.__proto__=e:r(t,e),t}}({},!1):void 0),check:o}},{17:17,38:38,4:4,46:46}],65:[function(t,e,n){"use strict";var a=t(29),r=t(46),i=t(19),o=t(83)("species");e.exports=function(t){var e=a[t];i&&e&&!e[o]&&r.setDesc(e,o,{configurable:!0,get:function(){return this}})}},{19:19,29:29,46:46,83:83}],66:[function(t,e,n){var a=t(46).setDesc,r=t(30),i=t(83)("toStringTag");e.exports=function(t,e,n){t&&!r(t=n?t:t.prototype,i)&&a(t,i,{configurable:!0,value:e})}},{30:30,46:46,83:83}],67:[function(t,e,n){var a=t(29),r="__core-js_shared__",i=a[r]||(a[r]={});e.exports=function(t){return i[t]||(i[t]={})}},{29:29}],68:[function(t,e,n){var a=t(4),r=t(2),i=t(83)("species");e.exports=function(t,e){var n,o=a(t).constructor;return void 0===o||void 0==(n=a(o)[i])?e:r(n)}},{2:2,4:4,83:83}],69:[function(t,e,n){e.exports=function(t,e,n){if(!(t instanceof e))throw TypeError(n+": use the 'new' operator!");return t}},{}],70:[function(t,e,n){var a=t(77),r=t(18);e.exports=function(t){return function(e,n){var i,o,s=r(e)+"",p=a(n),u=s.length;return 0>p||p>=u?t?"":void 0:(i=s.charCodeAt(p),55296>i||i>56319||p+1===u||(o=s.charCodeAt(p+1))<56320||o>57343?t?s.charAt(p):i:t?s.slice(p,p+2):(i-55296<<10)+(o-56320)+65536)}}},{18:18,77:77}],71:[function(t,e,n){var a=t(39),r=t(18);e.exports=function(t,e,n){if(a(e))throw TypeError("String#"+n+" doesn't accept regex!");return r(t)+""}},{18:18,39:39}],72:[function(t,e,n){var a=t(79),r=t(73),i=t(18);e.exports=function(t,e,n,o){var s=i(t)+"",p=s.length,u=void 0===n?" ":n+"",c=a(e);if(p>=c)return s;""==u&&(u=" ");var l=c-p,d=r.call(u,Math.ceil(l/u.length));return d.length>l&&(d=d.slice(0,l)),o?d+s:s+d}},{18:18,73:73,79:79}],73:[function(t,e,n){"use strict";var a=t(77),r=t(18);e.exports=function(t){var e=r(this)+"",n="",i=a(t);if(0>i||i==1/0)throw RangeError("Count can't be negative");for(;i>0;(i>>>=1)&&(e+=e))1&i&&(n+=e);return n}},{18:18,77:77}],74:[function(t,e,n){var a=t(22),r=t(18),i=t(24),o=" \n\x0B\f\r   ᠎              \u2028\u2029\ufeff",s="["+o+"]",p="​…",u=RegExp("^"+s+s+"*"),c=RegExp(s+s+"*$"),l=function(t,e){var n={};n[t]=e(d),a(a.P+a.F*i(function(){return!!o[t]()||p[t]()!=p}),"String",n)},d=l.trim=function(t,e){return t=r(t)+"",1&e&&(t=t.replace(u,"")),2&e&&(t=t.replace(c,"")),t};e.exports=l},{18:18,22:22,24:24}],75:[function(t,e,n){var a,r,i,o=t(17),s=t(33),p=t(32),u=t(20),c=t(29),l=c.process,d=c.setImmediate,f=c.clearImmediate,h=c.MessageChannel,m=0,v={},g="onreadystatechange",b=function(){var t=+this;if(v.hasOwnProperty(t)){var e=v[t];delete v[t],e()}},y=function(t){b.call(t.data)};d&&f||(d=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return v[++m]=function(){s("function"==typeof t?t:Function(t),e)},a(m),m},f=function(t){delete v[t]},"process"==t(11)(l)?a=function(t){l.nextTick(o(b,t,1))}:h?(r=new h,i=r.port2,r.port1.onmessage=y,a=o(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(a=function(t){c.postMessage(t+"","*")},c.addEventListener("message",y,!1)):a=g in u("script")?function(t){p.appendChild(u("script"))[g]=function(){p.removeChild(this),b.call(t)}}:function(t){setTimeout(o(b,t,1),0)}),e.exports={set:d,clear:f}},{11:11,17:17,20:20,29:29,32:32,33:33}],76:[function(t,e,n){var a=t(77),r=Math.max,i=Math.min;e.exports=function(t,e){return t=a(t),0>t?r(t+e,0):i(t,e)}},{77:77}],77:[function(t,e,n){var a=Math.ceil,r=Math.floor;e.exports=function(t){return isNaN(t=+t)?0:(t>0?r:a)(t)}},{}],78:[function(t,e,n){var a=t(34),r=t(18);e.exports=function(t){return a(r(t))}},{18:18,34:34}],79:[function(t,e,n){var a=t(77),r=Math.min;e.exports=function(t){return t>0?r(a(t),9007199254740991):0}},{77:77}],80:[function(t,e,n){var a=t(18);e.exports=function(t){return Object(a(t))}},{18:18}],81:[function(t,e,n){var a=t(38);e.exports=function(t,e){if(!a(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!a(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")}},{38:38}],82:[function(t,e,n){var a=0,r=Math.random();e.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++a+r).toString(36))}},{}],83:[function(t,e,n){var a=t(67)("wks"),r=t(82),i=t(29).Symbol;e.exports=function(t){return a[t]||(a[t]=i&&i[t]||(i||r)("Symbol."+t))}},{29:29,67:67,82:82}],84:[function(t,e,n){var a=t(10),r=t(83)("iterator"),i=t(45);e.exports=t(16).getIteratorMethod=function(t){return void 0!=t?t[r]||t["@@iterator"]||i[a(t)]:void 0}},{10:10,16:16,45:45,83:83}],85:[function(t,e,n){"use strict";var a,r=t(46),i=t(22),o=t(19),s=t(59),p=t(32),u=t(20),c=t(30),l=t(11),d=t(33),f=t(24),h=t(4),m=t(2),v=t(38),g=t(80),b=t(78),y=t(77),x=t(76),_=t(79),w=t(34),k=t(82)("__proto__"),E=t(8),S=t(7)(!1),C=Object.prototype,P=Array.prototype,A=P.slice,O=P.join,T=r.setDesc,R=r.getDesc,j=r.setDescs,M={};o||(a=!f(function(){return 7!=T(u("div"),"a",{get:function(){return 7}}).a}),r.setDesc=function(t,e,n){if(a)try{return T(t,e,n)}catch(r){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(h(t)[e]=n.value),t},r.getDesc=function(t,e){if(a)try{return R(t,e)}catch(n){}return c(t,e)?s(!C.propertyIsEnumerable.call(t,e),t[e]):void 0},r.setDescs=j=function(t,e){h(t);for(var n,a=r.getKeys(e),i=a.length,o=0;i>o;)r.setDesc(t,n=a[o++],e[n]);return t}),i(i.S+i.F*!o,"Object",{getOwnPropertyDescriptor:r.getDesc,defineProperty:r.setDesc,defineProperties:j});var L="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(","),D=L.concat("length","prototype"),N=L.length,F=function(){var t,e=u("iframe"),n=N,a=">";for(e.style.display="none",p.appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write("