diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 82128de15ffd9..7f6d114ebab70 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -20,6 +20,10 @@ var/list/sqrtTable = list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, /proc/Ceiling(x) return -round(-x) +/proc/Floor(x) + return -round(-x)-1 + + ///proc/Clamp(val, min, max) // return max(min, min(val, max)) diff --git a/code/_macros.dm b/code/_macros.dm new file mode 100644 index 0000000000000..b3359678236e7 --- /dev/null +++ b/code/_macros.dm @@ -0,0 +1,53 @@ +#define Clamp(x, y, z) (x <= y ? y : (x >= z ? z : x)) + +#define CLAMP01(x) (Clamp(x, 0, 1)) + +//Already defined + +//#define get_turf(A) get_step(A,0) + +//#define isAI(A) istype(A, /mob/living/silicon/ai) + +//#define isalien(A) istype(A, /mob/living/carbon/alien) + +//#define isanimal(A) istype(A, /mob/living/simple_animal) + +//#define isairlock(A) istype(A, /obj/machinery/door/airlock) + +//#define isbrain(A) istype(A, /mob/living/carbon/brain) + +//#define iscarbon(A) istype(A, /mob/living/carbon) + +//#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi) + +//#define isEye(A) istype(A, /mob/observer/eye) + +//#define ishuman(A) istype(A, /mob/living/carbon/human) + +//#define isliving(A) istype(A, /mob/living) + +//#define ismouse(A) istype(A, /mob/living/simple_animal/mouse) + +//#define isnewplayer(A) istype(A, /mob/new_player) + +//#define isobserver(A) istype(A, /mob/observer/dead) + +//#define isorgan(A) istype(A, /obj/item/organ/external) + +//#define ispAI(A) istype(A, /mob/living/silicon/pai) + +//#define isrobot(A) istype(A, /mob/living/silicon/robot) + +//#define issilicon(A) istype(A, /mob/living/silicon) + +//#define isslime(A) istype(A, /mob/living/carbon/slime) + +//#define isxeno(A) istype(A, /mob/living/simple_animal/xeno) + +#define isweakref(A) istype(A, /weakref) + +#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") + +#define to_chat(target, message) target << message + +#define CanInteract(user,src) user.Adjacent(src) //changed so you need to be next to it is all \ No newline at end of file diff --git a/code/datums/EPv2.dm b/code/datums/EPv2.dm new file mode 100644 index 0000000000000..f0e912fa55a95 --- /dev/null +++ b/code/datums/EPv2.dm @@ -0,0 +1,127 @@ +/* +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() + +*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. +*/ + +var/global/list/all_exonet_connections = list() + +/datum/exonet_protocol + var/address = "" //Resembles IPv6, but with only five 'groups', e.g. XXXX:XXXX:XXXX:XXXX:XXXX + var/atom/movable/holder = null + +/datum/exonet_protocol/New(var/atom/holder) + src.holder = holder + ..() + + +// 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) + var/new_address = null + while(new_address == find_address(new_address)) //Collision test. + var/hash = md5(string) + var/raw_address = copytext(hash,1,25) + var/addr_0 = "fc00" //Used for unique local address in real-life IPv6. + var/addr_1 = hexadecimal_to_EPv2(raw_address) + + new_address = "[addr_0]:[addr_1]" + string = "[string]0" //If we did get a collision, this should make the next attempt not have one. + sleep(1) + address = new_address + all_exonet_connections |= 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 == find_address(new_address) ) //Collision test. + return 0 + address = new_address + all_exonet_connections |= src + return 1 + +// Proc: hexadecimal_to_EPv2() +// Parameters: 1 (hex - a string of hexadecimals to convert) +// Description: Helper proc to add colons to a string in the right places. +/proc/hexadecimal_to_EPv2(var/hex) + if(!hex) + return null + 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) + var/new_address = "[addr_1]:[addr_2]:[addr_3]:[addr_4]" + return new_address + + +// Proc: remove_address() +// Parameters: None +// Description: Deallocates the address, freeing it for use. +/datum/exonet_protocol/proc/remove_address() + address = "" + all_exonet_connections.Remove(src) + + +// Proc: find_address() +// Parameters: 1 (target_address - the desired address to find) +// Description: Searches the global list all_exonet_connections for a specific address, and returns it if found, otherwise returns null. +/datum/exonet_protocol/proc/find_address(var/target_address) + for(var/datum/exonet_protocol/exonet in all_exonet_connections) + if(exonet.address == target_address) + return exonet.address + return null + +// Proc: send_message() +// Parameters: 2 (target_address - the desired address to send the message to, message - the message to send) +// Description: Sends the message to target_address, by calling receive_message() on the desired datum. +/datum/exonet_protocol/proc/send_message(var/target_address, var/message) + if(!address) + return 0 + for(var/datum/exonet_protocol/exonet in all_exonet_connections) + if(exonet.address == target_address) + exonet.receive_message(holder, address, message) + break + +// Proc: receive_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: 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/message) + holder.receive_exonet_message(origin_atom, origin_address, message) + return + +// 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) + return diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm new file mode 100644 index 0000000000000..d225de991fb68 --- /dev/null +++ b/code/datums/weakref.dm @@ -0,0 +1,33 @@ +#define QDELETED(X) (!X || X.gc_destroyed) + +/datum + var/weakref/weakref + +/datum/Destroy() + weakref = null // Clear this reference to ensure it's kept for as brief duration as possible. + . = ..() + +//obtain a weak reference to a datum +/proc/weakref(datum/D) + if(D.gc_destroyed) + return + if(!D.weakref) + D.weakref = new/weakref(D) + return D.weakref + +/weakref + var/ref + +/weakref/New(datum/D) + ref = "\ref[D]" + +/weakref/Destroy() + // A weakref datum should not be manually destroyed as it is a shared resource, + // rather it should be automatically collected by the BYOND GC when all references are gone. + return 0 + +/weakref/proc/resolve() + var/datum/D = locate(ref) + if(D && D.weakref == src) + return D + return null \ 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 0000000000000..682df50e60050 --- /dev/null +++ b/code/game/machinery/exonet_node.dm @@ -0,0 +1,173 @@ +/obj/machinery/exonet_node + name = "exonet node" + desc = "This machine is one of many, many nodes inside Vir's section of the Exonet, connecting the Northern Star to the rest of the system, at least \ + electronically." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "exonet_node" + idle_power_usage = 2500 + density = 1 + anchored = 1 + var/on = 1 + var/toggle = 1 + + var/allow_external_PDAs = 1 + var/allow_external_communicators = 1 + var/allow_external_newscasters = 1 + + var/opened = 0 + +// Proc: New() +// Parameters: None +// Description: Adds components to the machine for deconstruction. +/obj/machinery/exonet_node/New() + ..() + + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/telecomms/exonet_node(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/ansible(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/filter(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/crystal(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src) + component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src) + component_parts += new /obj/item/stack/cable_coil(src, 2) + RefreshParts() + +// Proc: update_icon() +// Parameters: None +// Description: Self explanatory. +/obj/machinery/exonet_node/update_icon() + if(on) + if(!allow_external_PDAs && !allow_external_communicators && !allow_external_newscasters) + icon_state = "[initial(icon_state)]_idle" + else + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_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() + if(toggle) + if(stat & (BROKEN|NOPOWER|EMPED)) + on = 0 + idle_power_usage = 0 + else + on = 1 + idle_power_usage = 2500 + else + on = 0 + idle_power_usage = 0 + +// 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 + spawn(rand(duration - 20, duration + 20)) + stat &= ~EMPED + update_icon() + ..() + +// Proc: process() +// Parameters: None +// Description: Calls the procs below every tick. +/obj/machinery/exonet_node/process() + update_power() + +// 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/weapon/screwdriver)) + default_deconstruction_screwdriver(user, I) + else if(istype(I, /obj/item/weapon/crowbar)) + default_deconstruction_crowbar(user, I) + else + ..() + +// 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) + attack_hand(user) + +// Proc: attack_hand() +// Parameters: 1 (user - the person clicking on the machine) +// Description: Opens the NanoUI interface with ui_interact() +/obj/machinery/exonet_node/attack_hand(mob/user) + ui_interact(user) + +// Proc: ui_interact() +// Parameters: 4 (standard NanoUI arguments) +// Description: Allows the user to turn the machine on or off, or open or close certain 'ports' for things like external PDA messages, newscasters, etc. +/obj/machinery/exonet_node/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // this is the data which will be sent to the ui + var/data = list() + + + data["on"] = on ? 1 : 0 + data["allowPDAs"] = allow_external_PDAs + data["allowCommunicators"] = allow_external_communicators + data["allowNewscasters"] = allow_external_newscasters + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "exonet_node.tmpl", "Exonet Node #157", 400, 400) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +// Proc: Topic() +// Parameters: 2 (standard Topic arguments) +// Description: Responds to button presses on the NanoUI interface. +/obj/machinery/exonet_node/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggle_power"]) + toggle = !toggle + update_power() + if(!toggle) + var/msg = "[usr.client.key] ([usr]) has turned [src] off, at [x],[y],[z]." + message_admins(msg) + log_game(msg) + + if(href_list["toggle_PDA_port"]) + allow_external_PDAs = !allow_external_PDAs + + if(href_list["toggle_communicator_port"]) + allow_external_communicators = !allow_external_communicators + if(!allow_external_communicators) + var/msg = "[usr.client.key] ([usr]) has turned [src]'s communicator port off, at [x],[y],[z]." + message_admins(msg) + log_game(msg) + + if(href_list["toggle_newscaster_port"]) + allow_external_newscasters = !allow_external_newscasters + if(!allow_external_newscasters) + var/msg = "[usr.client.key] ([usr]) has turned [src]'s newscaster port off, at [x],[y],[z]." + message_admins(msg) + log_game(msg) + + update_icon() + SSnano.update_uis(src) + add_fingerprint(usr) + +// Proc: get_exonet_node() +// Parameters: None +// Description: Helper proc to get a reference to an Exonet node. +/proc/get_exonet_node() + for(var/obj/machinery/exonet_node/E in machines) + if(E.on) + return E diff --git a/code/game/machinery/telecomms/machines/relay.dm b/code/game/machinery/telecomms/machines/relay.dm index 51d76d47255a4..1326e569d5f3e 100644 --- a/code/game/machinery/telecomms/machines/relay.dm +++ b/code/game/machinery/telecomms/machines/relay.dm @@ -84,3 +84,17 @@ hide = 1 toggled = 0 autolinkers = list("r_relay") + +//This isn't a real telecomms board but I don't want to make a whole file to hold only one circuitboard. +/obj/item/weapon/circuitboard/telecomms/exonet_node + name = "exonet node" + build_path = "/obj/machinery/exonet_node" + origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4) + req_components = list( + "/obj/item/weapon/stock_parts/subspace/ansible" = 1, + "/obj/item/weapon/stock_parts/subspace/filter" = 1, + "/obj/item/weapon/stock_parts/manipulator" = 2, + "/obj/item/weapon/stock_parts/micro_laser" = 1, + "/obj/item/weapon/stock_parts/subspace/crystal" = 1, + "/obj/item/weapon/stock_parts/subspace/treatment" = 2, + "/obj/item/stack/cable_coil" = 2) diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index e1c44eac0cd66..0bfa2a47bdee2 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -70,6 +70,17 @@ removed(imp_in) ..() +/obj/item/weapon/implant/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/implanter)) + var/obj/item/weapon/implanter/implanter = I + if(implanter.imp) + return // It's full. + user.remove_from_mob(src) + forceMove(implanter) + implanter.imp = src + implanter.update_icon() + else + ..() /obj/item/weapon/implant/proc/get_data() return "No information available" diff --git a/code/game/objects/items/weapons/implants/implantcircuits.dm b/code/game/objects/items/weapons/implants/implantcircuits.dm new file mode 100644 index 0000000000000..0dc18fff7eac2 --- /dev/null +++ b/code/game/objects/items/weapons/implants/implantcircuits.dm @@ -0,0 +1,48 @@ +/obj/item/weapon/implant/integrated_circuit + name = "electronic implant" + desc = "It's a case, for building very tiny electronics with." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "setup_implant" + var/obj/item/device/electronic_assembly/implant/IC = null + +/obj/item/weapon/implant/integrated_circuit/proc/islegal() + return TRUE + +/obj/item/weapon/implant/integrated_circuit/New() + ..() + IC = new(src) + IC.implant = src + +/obj/item/weapon/implant/integrated_circuit/Destroy() + IC.implant = null + qdel(IC) + ..() + +/obj/item/weapon/implant/integrated_circuit/get_data() + var/dat = {" + Implant Specifications:
+ Name: Modular Implant
+ Life: 3 years.
+ Important Notes: EMP can cause malfunctions in the internal electronics of this implant.
+
+ Implant Details:
+ Function: Contains no innate functions until other components are added.
+ Special Features: + Modular Circuitry- Can be loaded with specific modular circuitry in order to fulfill a wide possibility of functions.
+ Integrity: Implant is not shielded from electromagnetic interferance, otherwise it is independant of subject's status."} + return dat + +/obj/item/weapon/implant/integrated_circuit/emp_act(severity) + IC.emp_act(severity) + +/obj/item/weapon/implant/integrated_circuit/examine(mob/user) + IC.examine(user) + +/obj/item/weapon/implant/integrated_circuit/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/weapon/crowbar) || istype(O, /obj/item/device/integrated_electronics) || istype(O, /obj/item/integrated_circuit) || istype(O, /obj/item/weapon/screwdriver) ) + IC.attackby(O, user) + else + ..() + +/obj/item/weapon/implant/integrated_circuit/attack_self(mob/user) + IC.attack_self(user) \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index c3d4af1d51679..fa39d528fdb0d 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -29,7 +29,8 @@ /obj/item/stack/cable_coil, /obj/item/device/t_scanner, /obj/item/device/analyzer, - /obj/item/weapon/extinguisher/mini + /obj/item/weapon/extinguisher/mini, + /obj/item/device/integrated_electronics/wirer ) /obj/item/weapon/storage/belt/utility/full/New() diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 1a4e1f3224f52..42ddd910ca6a7 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -502,3 +502,13 @@ if(verbs.Find(/obj/item/weapon/storage/verb/quick_empty)) quick_empty() +/obj/item/weapon/storage/proc/make_exact_fit() + storage_slots = contents.len + + can_hold.Cut() + max_w_class = 0 + max_combined_w_class = 0 + for(var/obj/item/I in src) + can_hold[I.type]++ + max_w_class = max(I.w_class, max_w_class) + max_combined_w_class += I.w_class diff --git a/code/modules/error_handler/~defines.dm b/code/modules/error_handler/~defines.dm new file mode 100644 index 0000000000000..98712eaa091bd --- /dev/null +++ b/code/modules/error_handler/~defines.dm @@ -0,0 +1,6 @@ +// Settings for the error handler and error viewer +#undef ERROR_COOLDOWN +#undef ERROR_LIMIT +#undef ERROR_MAX_COOLDOWN +#undef ERROR_SILENCE_TIME +#undef ERROR_MSG_DELAY diff --git a/code/modules/integrated_electronics/_defines.dm b/code/modules/integrated_electronics/_defines.dm new file mode 100644 index 0000000000000..219cac711e467 --- /dev/null +++ b/code/modules/integrated_electronics/_defines.dm @@ -0,0 +1,130 @@ +#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_ANY "\" +#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/list + +#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 200 + +//Some colors + +#define COLOR_WHITE "#ffffff" +#define COLOR_SILVER "#c0c0c0" +#define COLOR_GRAY "#808080" +#define COLOR_FLOORTILE_GRAY "#8D8B8B" +#define COLOR_BLACK "#000000" +#define COLOR_RED "#ff0000" +#define COLOR_RED_LIGHT "#b00000" +#define COLOR_MAROON "#800000" +#define COLOR_YELLOW "#ffff00" +#define COLOR_AMBER "#ffbf00" +#define COLOR_OLIVE "#808000" +#define COLOR_LIME "#00ff00" +#define COLOR_GREEN "#008000" +#define COLOR_CYAN "#00ffff" +#define COLOR_TEAL "#008080" +#define COLOR_BLUE "#0000ff" +#define COLOR_BLUE_LIGHT "#33ccff" +#define COLOR_NAVY "#000080" +#define COLOR_PINK "#ff00ff" +#define COLOR_PURPLE "#800080" +#define COLOR_ORANGE "#ff9900" +#define COLOR_LUMINOL "#66ffff" +#define COLOR_BEIGE "#ceb689" +#define COLOR_BLUE_GRAY "#6a97b0" +#define COLOR_BROWN "#b19664" +#define COLOR_DARK_BROWN "#917448" +#define COLOR_DARK_ORANGE "#b95a00" +#define COLOR_GREEN_GRAY "#8daf6a" +#define COLOR_RED_GRAY "#aa5f61" +#define COLOR_PALE_BLUE_GRAY "#8bbbd5" +#define COLOR_PALE_GREEN_GRAY "#aed18b" +#define COLOR_PALE_RED_GRAY "#cc9090" +#define COLOR_PALE_PURPLE_GRAY "#bda2ba" +#define COLOR_PURPLE_GRAY "#a2819e" +#define COLOR_SUN "#ec8b2f" + +//Color defines used by the assembly detailer. +#define COLOR_ASSEMBLY_BLACK "#545454" +#define COLOR_ASSEMBLY_BGRAY "#9497AB" +#define COLOR_ASSEMBLY_WHITE "#E2E2E2" +#define COLOR_ASSEMBLY_RED "#CC4242" +#define COLOR_ASSEMBLY_ORANGE "#E39751" +#define COLOR_ASSEMBLY_BEIGE "#AF9366" +#define COLOR_ASSEMBLY_BROWN "#97670E" +#define COLOR_ASSEMBLY_GOLD "#AA9100" +#define COLOR_ASSEMBLY_YELLOW "#CECA2B" +#define COLOR_ASSEMBLY_GURKHA "#999875" +#define COLOR_ASSEMBLY_LGREEN "#789876" +#define COLOR_ASSEMBLY_GREEN "#44843C" +#define COLOR_ASSEMBLY_LBLUE "#5D99BE" +#define COLOR_ASSEMBLY_BLUE "#38559E" +#define COLOR_ASSEMBLY_PURPLE "#6F6192" + +var/global/list/alphabet_uppercase = 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/list/all_integrated_circuits = list() + +/proc/initialize_integrated_circuits_list() + for(var/thing in typesof(/obj/item/integrated_circuit)) + all_integrated_circuits += new thing() + +/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 = 1 + var/obj/item/device/electronic_assembly/assembly = null // Reference to the assembly holding this circuit, if any. + var/extended_desc = null + 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 = null //This acts as a limitation on building machines, bigger components cost more 'space'. -1 for size 0 + var/cooldown_per_use = 1 SECONDS // 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 = null // 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 = 1 // Allows additional multitool functionality + // Used as a global var, (Do not set manually in children). diff --git a/code/modules/integrated_electronics/coordinate.dm b/code/modules/integrated_electronics/coordinate.dm new file mode 100644 index 0000000000000..732a97dd3b14c --- /dev/null +++ b/code/modules/integrated_electronics/coordinate.dm @@ -0,0 +1,50 @@ +//This circuit gives information on where the machine is. +/obj/item/integrated_circuit/gps + name = "global positioning system" + desc = "This allows you to easily know the position of a machine containing this device." + icon_state = "gps" + complexity = 4 + inputs = list() + outputs = list("X (abs)", "Y (abs)") + activators = list("get coordinates") + +/obj/item/integrated_circuit/gps/do_work() + var/turf/T = get_turf(src) + var/datum/integrated_io/result_x = outputs[1] + var/datum/integrated_io/result_y = outputs[2] + + result_x.data = null + result_y.data = null + if(!T) + return + + result_x.data = T.x + result_y.data = T.y + + for(var/datum/integrated_io/O in outputs) + O.push_data() + +/obj/item/integrated_circuit/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 (abs)", "Y1 (abs)", "X2 (abs)", "Y2 (abs)") + outputs = list("X (rel)", "Y (rel)") + activators = list("compute rel coordinates") + +/obj/item/integrated_circuit/abs_to_rel_coords/do_work() + var/datum/integrated_io/x1 = inputs[1] + var/datum/integrated_io/y1 = inputs[2] + + var/datum/integrated_io/x2 = inputs[3] + var/datum/integrated_io/y2 = inputs[4] + + var/datum/integrated_io/result_x = outputs[1] + var/datum/integrated_io/result_y = outputs[2] + + if(x1.data && y1.data && x2.data && y2.data) + result_x.data = x1.data - x2.data + result_y.data = y1.data - y2.data + + for(var/datum/integrated_io/O in outputs) + O.push_data() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm new file mode 100644 index 0000000000000..2e6bfa53f5314 --- /dev/null +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -0,0 +1,506 @@ +#define IC_COMPONENTS_BASE 20 +#define IC_COMPLEXITY_BASE 80 + +/obj/item/device/electronic_assembly + name = "electronic assembly" + desc = "It's a case, for building electronics with." + w_class = 2 + icon = 'icons/obj/assemblies/electronic_setups.dmi' + icon_state = "setup_small" + var/show_messages = TRUE + var/max_components = IC_COMPONENTS_BASE + var/max_complexity = IC_COMPLEXITY_BASE + var/opened = 0 + var/detail_color = COLOR_ASSEMBLY_BLACK + var/list/color_whitelist = list( //This is just for checking that hacked colors aren't in the save data. + COLOR_ASSEMBLY_BLACK, + COLOR_FLOORTILE_GRAY, + COLOR_ASSEMBLY_BGRAY, + COLOR_ASSEMBLY_WHITE, + COLOR_ASSEMBLY_RED, + COLOR_ASSEMBLY_ORANGE, + COLOR_ASSEMBLY_BEIGE, + COLOR_ASSEMBLY_BROWN, + COLOR_ASSEMBLY_GOLD, + COLOR_ASSEMBLY_YELLOW, + COLOR_ASSEMBLY_GURKHA, + COLOR_ASSEMBLY_LGREEN, + COLOR_ASSEMBLY_GREEN, + COLOR_ASSEMBLY_LBLUE, + COLOR_ASSEMBLY_BLUE, + COLOR_ASSEMBLY_PURPLE + ) + var/obj/item/weapon/stock_parts/cell/battery = null // Internal cell which most circuits need to work. + +/obj/item/device/electronic_assembly/default //The /default electronic_assemblys are to allow the introduction of the new naming scheme without breaking old saves. + name = "type-a electronic assembly" + +/obj/item/device/electronic_assembly/calc + name = "type-b electronic assembly" + icon_state = "setup_small_calc" + desc = "It's a case, for building small electronics with. This one resembles a pocket calculator." + +/obj/item/device/electronic_assembly/clam + name = "type-c electronic assembly" + icon_state = "setup_small_clam" + desc = "It's a case, for building small electronics with. This one has a clamshell design." + +/obj/item/device/electronic_assembly/simple + name = "type-d electronic assembly" + icon_state = "setup_small_simple" + desc = "It's a case, for building small electronics with. This one has a simple design." + +/obj/item/device/electronic_assembly/hook + name = "type-e electronic assembly" + icon_state = "setup_small_hook" + desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative." + +/obj/item/device/electronic_assembly/pda + name = "type-f electronic assembly" + icon_state = "setup_small_pda" + desc = "It's a case, for building small electronics with. This one resembles a PDA." + +/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 = 3 + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + +/obj/item/device/electronic_assembly/medium/default + name = "type-a electronic mechanism" + +/obj/item/device/electronic_assembly/medium/box + name = "type-b electronic mechanism" + icon_state = "setup_medium_box" + desc = "It's a case, for building medium-sized electronics with. This one has a boxy design." + +/obj/item/device/electronic_assembly/medium/clam + name = "type-c electronic mechanism" + icon_state = "setup_medium_clam" + desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design." + +/obj/item/device/electronic_assembly/medium/medical + name = "type-d electronic mechanism" + icon_state = "setup_medium_med" + desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus." + +/obj/item/device/electronic_assembly/medium/gun + name = "type-e electronic mechanism" + icon_state = "setup_medium_gun" + desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, if you're feeling optimistic." + +/obj/item/device/electronic_assembly/medium/radio + name = "type-f electronic mechanism" + icon_state = "setup_medium_radio" + desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio." + +/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 = 5 + max_components = IC_COMPONENTS_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + +/obj/item/device/electronic_assembly/large/default + name = "type-a electronic machine" + +/obj/item/device/electronic_assembly/large/scope + name = "type-b electronic machine" + icon_state = "setup_large_scope" + desc = "It's a case, for building large electronics with. This one resembles an oscilloscope." + +/obj/item/device/electronic_assembly/large/terminal + name = "type-c electronic machine" + icon_state = "setup_large_terminal" + desc = "It's a case, for building large electronics with. This one resembles a computer terminal." + +/obj/item/device/electronic_assembly/large/arm + name = "type-d electronic machine" + icon_state = "setup_large_arm" + desc = "It's a case, for building large electronics with. This one resembles a robotic arm." + +/obj/item/device/electronic_assembly/large/tall + name = "type-e electronic machine" + icon_state = "setup_large_tall" + desc = "It's a case, for building large electronics with. This one has a tall design." + +/obj/item/device/electronic_assembly/large/industrial + name = "type-f electronic machine" + icon_state = "setup_large_industrial" + desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery." + +/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 = 4 + max_components = IC_COMPONENTS_BASE * 3 + max_complexity = IC_COMPLEXITY_BASE * 3 + +/obj/item/device/electronic_assembly/drone/can_move() + return TRUE + +/obj/item/device/electronic_assembly/drone/default + name = "type-a electronic drone" + +/obj/item/device/electronic_assembly/drone/arms + name = "type-b electronic drone" + icon_state = "setup_drone_arms" + desc = "It's a case, for building mobile electronics with. This one is armed and dangerous." + +/obj/item/device/electronic_assembly/drone/secbot + name = "type-c electronic drone" + icon_state = "setup_drone_secbot" + desc = "It's a case, for building mobile electronics with. This one resembles a Securitron." + +/obj/item/device/electronic_assembly/drone/medbot + name = "type-d electronic drone" + icon_state = "setup_drone_medbot" + desc = "It's a case, for building mobile electronics with. This one resembles a Medibot." + +/obj/item/device/electronic_assembly/drone/genbot + name = "type-e electronic drone" + icon_state = "setup_drone_genbot" + desc = "It's a case, for building mobile electronics with. This one has a generic bot design." + +/obj/item/device/electronic_assembly/drone/android + name = "type-f electronic drone" + icon_state = "setup_drone_android" + desc = "It's a case, for building mobile electronics with. This one has a hominoid design." + +/obj/item/device/electronic_assembly/wallmount + name = "wall-mounted electronic assembly" + icon_state = "setup_wallmount_medium" + desc = "It's a case, for building medium-sized electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on." + w_class = 3 + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + +/obj/item/device/electronic_assembly/wallmount/heavy + name = "heavy wall-mounted electronic assembly" + icon_state = "setup_wallmount_large" + desc = "It's a case, for building large electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on." + w_class = 5 + max_components = IC_COMPONENTS_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + +/obj/item/device/electronic_assembly/wallmount/light + name = "light wall-mounted electronic assembly" + icon_state = "setup_wallmount_small" + desc = "It's a case, for building small electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on." + w_class = 2 + max_components = IC_COMPONENTS_BASE + max_complexity = IC_COMPLEXITY_BASE + +/obj/item/device/electronic_assembly/implant + name = "electronic implant" + icon_state = "setup_implant" + desc = "It's a case, for building very tiny electronics with." + w_class = 1 + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + var/obj/item/weapon/implant/integrated_circuit/implant = null + +/obj/item/device/electronic_assembly/New() + ..() + battery = new(src) + SSobj.processing |= src + +/obj/item/device/electronic_assembly/Destroy() + battery = null + SSobj.processing -= src + for(var/atom/movable/AM in contents) + qdel(AM) + ..() + +/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/implant/update_icon() + ..() + implant.icon_state = icon_state + overlays.Cut() + if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off. + return + var/image/detail_overlay = image('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color") + detail_overlay.color = detail_color + overlays.Add(detail_overlay) + + +/obj/item/device/electronic_assembly/implant/proc/nano_host() + return implant + +/obj/item/device/electronic_assembly/proc/resolve_nano_host() + return src + +/obj/item/device/electronic_assembly/implant/resolve_nano_host() + return implant + +/obj/item/device/electronic_assembly/proc/check_interactivity(mob/user) + if(!CanInteract(user, src)) + return 0 + return 1 + +/obj/item/device/electronic_assembly/interact(mob/user) + if(!check_interactivity(user)) + return + + var/total_parts = 0 + var/total_complexity = 0 + for(var/obj/item/integrated_circuit/part in contents) + total_parts += part.size + total_complexity = total_complexity + part.complexity + var/HTML = list() + + HTML += "[src.name]" + HTML += "
\[Refresh\] | " + HTML += "\[Rename\]
" + HTML += "[total_parts]/[max_components] ([round((total_parts / 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/verb/rename() + 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 = sanitize_text(input("What do you want to name this?", "Rename", src.name) as null|text, MAX_NAME_LEN) + if(src && input) + 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) + overlays.Cut() + if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off. + return + var/image/detail_overlay = image('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color") + detail_overlay.color = detail_color + overlays.Add(detail_overlay) + +/obj/item/device/electronic_assembly/examine(mob/user) + . = ..(user, 1) + if(.) + for(var/obj/item/integrated_circuit/IC in contents) + IC.external_examine(user) + // for(var/obj/item/integrated_circuit/output/screen/S in contents) + // if(S.stuff_to_display) + // to_chat(user, "There's a little screen labeled '[S.name]', which displays '[S.stuff_to_display]'.") + if(opened) + interact(user) + +/obj/item/device/electronic_assembly/proc/get_part_complexity() + . = 0 + for(var/obj/item/integrated_circuit/part in contents) + . += part.complexity + +/obj/item/device/electronic_assembly/proc/get_part_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 > src.w_class) + to_chat(user, "\The [IC] is way too big to fit into \the [src].") + return FALSE + + var/total_part_size = get_part_size() + var/total_complexity = get_part_complexity() + + if((total_part_size + IC.size) > max_components) + to_chat(user, "You can't seem to add the '[IC.name]', 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.name]', since this setup's too complicated for the case.") + return FALSE + + if(!IC.forceMove(src)) + return FALSE + + IC.assembly = src + + return TRUE + +/obj/item/device/electronic_assembly/afterattack(atom/target, mob/user, proximity) + if(proximity) + var/scanned = FALSE + for(var/obj/item/integrated_circuit/input/sensor/S in contents) +// S.set_pin_data(IC_OUTPUT, 1, weakref(target)) +// S.check_then_do_work() + if(S.scan(target)) + scanned = TRUE + if(scanned) + visible_message("\The [user] waves \the [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.unEquip(I)) + return FALSE + if(add_circuit(I, user)) + to_chat(user, "You slide \the [I] inside \the [src].") + playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) + interact(user) + return TRUE + else if(istype(I, /obj/item/weapon/crowbar)) + playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) + opened = !opened + user << "You [opened ? "opened" : "closed"] \the [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/weapon/screwdriver)) + if(opened) + interact(user) + else + to_chat(user, "\The [src] isn't opened, so you can't fiddle with the internal components. \ + Try using a crowbar.") + else if(istype(I, /obj/item/weapon/stock_parts/cell)) + if(!opened) + to_chat(user, "\The [src] isn't opened, so you can't put anything inside. Try using a crowbar.") + return FALSE + if(battery) + to_chat(user, "\The [src] already has \a [battery] inside. Remove it first if you want to replace it.") + return FALSE + var/obj/item/weapon/stock_parts/cell = I + user.drop_item(cell) + 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 if(istype(I, /obj/item/device/integrated_electronics/detailer)) + var/obj/item/device/integrated_electronics/detailer/D = I + detail_color = D.detail_color + update_icon() + 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.name]\]" + if(i) + disp_name += " ([i+1])" + input_selection.Add(disp_name) + + var/obj/item/integrated_circuit/input/choice + if(available_inputs) + var/selection = input(user, "What do you want to interact with?", "Interaction") as null|anything in input_selection + 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/atom/movable/AM in contents) + 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 * CELLRATE)) + return TRUE + return FALSE + +// Ditto for giving. +/obj/item/device/electronic_assembly/proc/give_power(amount) + if(battery && battery.give(amount * CELLRATE)) + return TRUE + return FALSE + diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm new file mode 100644 index 0000000000000..a63c174c2846d --- /dev/null +++ b/code/modules/integrated_electronics/core/detailer.dm @@ -0,0 +1,46 @@ +/obj/item/device/integrated_electronics/detailer + name = "assembly detailer" + desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish." + icon = 'icons/obj/assemblies/electronic_tools.dmi' + icon_state = "detailer" + w_class = 2 + var/data_to_write = null + var/accepting_refs = FALSE + var/detail_color = COLOR_ASSEMBLY_WHITE + var/list/color_list = list( + "black" = COLOR_ASSEMBLY_BLACK, + "gray" = COLOR_FLOORTILE_GRAY, + "machine gray" = COLOR_ASSEMBLY_BGRAY, + "white" = COLOR_ASSEMBLY_WHITE, + "red" = COLOR_ASSEMBLY_RED, + "orange" = COLOR_ASSEMBLY_ORANGE, + "beige" = COLOR_ASSEMBLY_BEIGE, + "brown" = COLOR_ASSEMBLY_BROWN, + "gold" = COLOR_ASSEMBLY_GOLD, + "yellow" = COLOR_ASSEMBLY_YELLOW, + "gurkha" = COLOR_ASSEMBLY_GURKHA, + "light green" = COLOR_ASSEMBLY_LGREEN, + "green" = COLOR_ASSEMBLY_GREEN, + "light blue" = COLOR_ASSEMBLY_LBLUE, + "blue" = COLOR_ASSEMBLY_BLUE, + "purple" = COLOR_ASSEMBLY_PURPLE + ) + +/obj/item/device/integrated_electronics/detailer/New() + .=..() + update_icon() + +/obj/item/device/integrated_electronics/detailer/update_icon() + overlays.Cut() + var/image/detail_overlay = image('icons/obj/assemblies/electronic_tools.dmi', "detailer-color") + detail_overlay.color = detail_color + overlays.Add(detail_overlay) + +/obj/item/device/integrated_electronics/detailer/attack_self(mob/user) + var/color_choice = input(user, "Select color.", "Assembly Detailer") as null|anything in color_list + if(!color_list[color_choice]) + return + if(!in_range(src, user)) + return + detail_color = color_list[color_choice] + update_icon() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/device.dm b/code/modules/integrated_electronics/core/device.dm new file mode 100644 index 0000000000000..38044595b7b35 --- /dev/null +++ b/code/modules/integrated_electronics/core/device.dm @@ -0,0 +1,84 @@ +/obj/item/device/assembly/electronic_assembly + name = "electronic device" + desc = "It's a case for building electronics with. It can be attached to other small devices." + icon_state = "setup_device" + var/opened = 0 + + var/obj/item/device/electronic_assembly/device/EA + +/obj/item/device/assembly/electronic_assembly/New() + EA = new(src) + EA.holder = src + ..() + +/obj/item/device/assembly/electronic_assembly/attackby(obj/item/I as obj, mob/user as mob) + if (istype(I, /obj/item/weapon/crowbar)) + toggle_open(user) + else if (opened) + EA.attackby(I, user) + else + ..() + +/obj/item/device/assembly/electronic_assembly/proc/toggle_open(mob/user) + playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) + opened = !opened + EA.opened = opened + to_chat(user, "You [opened ? "opened" : "closed"] \the [src].") + secured = 1 + update_icon() + +/obj/item/device/assembly/electronic_assembly/update_icon() + if(EA) + icon_state = initial(icon_state) + else + icon_state = initial(icon_state)+"0" + if(opened) + icon_state = icon_state + "-open" + +/obj/item/device/assembly/electronic_assembly/attack_self(mob/user as mob) + if(EA) + EA.attack_self(user) + +/obj/item/device/assembly/electronic_assembly/pulsed(var/radio = 0) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs + if(EA) + for(var/obj/item/integrated_circuit/built_in/device_input/I in EA.contents) + I.do_work() + return + +/obj/item/device/assembly/electronic_assembly/examine(mob/user) + .=..(user, 1) + if(EA) + for(var/obj/item/integrated_circuit/IC in EA.contents) + IC.external_examine(user) + +/obj/item/device/assembly/electronic_assembly/verb/toggle() + set src in usr + set category = "Object" + set name = "Open/Close Device Assembly" + set desc = "Open or close device assembly!" + + toggle_open(usr) + + +/obj/item/device/electronic_assembly/device + name = "electronic device" + icon_state = "setup_device" + desc = "It's a tiny electronic device with specific use for attaching to other devices." + var/obj/item/device/assembly/electronic_assembly/holder + w_class = 1 + max_components = IC_COMPONENTS_BASE * 3/4 + max_complexity = IC_COMPLEXITY_BASE * 3/4 + + +/obj/item/device/electronic_assembly/device/New() + ..() + var/obj/item/integrated_circuit/built_in/device_input/input = new(src) + var/obj/item/integrated_circuit/built_in/device_output/output = new(src) + input.assembly = src + output.assembly = src + +/obj/item/device/electronic_assembly/device/check_interactivity(mob/user) + if(!CanInteract(user,src)) + return 0 + return 1 + diff --git a/code/modules/integrated_electronics/core/helpers.dm b/code/modules/integrated_electronics/core/helpers.dm new file mode 100644 index 0000000000000..a0f60b6390eb0 --- /dev/null +++ b/code/modules/integrated_electronics/core/helpers.dm @@ -0,0 +1,85 @@ +/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() + var/i = 0 + for(var/io_entry in io_list_copy) + 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, var/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(isnull(data)) + return + 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 null + return inputs[pin_number] + if(IC_OUTPUT) + if(pin_number > outputs.len) + return null + return outputs[pin_number] + if(IC_ACTIVATOR) + if(pin_number > activators.len) + return null + return activators[pin_number] + return null + +/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 1 + + 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 1 + return 0 + +/mob/living/proc/isSynthetic() + return 0 + +/mob/living/carbon/human/isSynthetic() + // If they are 100% robotic, they count as synthetic. + for(var/obj/item/organ/limb/E in organsystem) + if(E.organtype < ORGAN_ROBOTIC) + return FALSE + return TRUE + +/proc/get_random_colour() + return pick(list("FF0000", "FF7F00", "FFFF00", "00FF00", "0000FF", "4B0082", "8F00FF")) \ No newline at end of file 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 0000000000000..1be2a0af8657c --- /dev/null +++ b/code/modules/integrated_electronics/core/integrated_circuit.dm @@ -0,0 +1,396 @@ +/* + 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/datum/integrated_io/I in inputs) + if(I.linked.len) + to_chat(user, "The '[I]' is connected to [I.get_linked_to_desc()].") + for(var/datum/integrated_io/O in outputs) + if(O.linked.len) + to_chat(user, "The '[O]' is connected to [O.get_linked_to_desc()].") + for(var/datum/integrated_io/activate/A in activators) + 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/New() + displayed_name = name + if(!size) size = w_class + if(size == -1) size = 0 + 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() + for(var/datum/integrated_io/I in inputs) + qdel(I) + for(var/datum/integrated_io/O in outputs) + qdel(O) + for(var/datum/integrated_io/A in activators) + qdel(A) + . = ..() + +/obj/item/integrated_circuit/proc/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/datum/integrated_io/io in inputs + outputs + activators) + io.scramble() + +/obj/item/integrated_circuit/proc/check_interactivity(mob/user) + if(assembly) + return assembly.check_interactivity(user) + else if(!CanInteract(user, src)) + return 0 + return 1 + +/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 = sanitize_text(input("What do you want to name the circuit?", "Rename", src.name) as null|text, MAX_NAME_LEN) + if(src && input && assembly.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/datum/integrated_io/linked in io.linked) +// words += "\[[linked.name]\] + words += "[linked.name] \ + @ [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/datum/integrated_io/linked in io.linked) +// words += "\[[linked.name]\] + words += "[linked.name] \ + @ [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.name] [io.data?"\":"\"]
" + if(io.linked.len) + for(var/datum/integrated_io/linked in io.linked) +// words += "\[[linked.name]\] + words += "[linked.name] \ + @ [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 1 + + var/update = 1 + var/obj/item/device/electronic_assembly/A = src.assembly + var/update_to_assembly = 0 + 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_hand() + + if(href_list["rename"]) + rename_component(usr) + if(href_list["from_assembly"]) + update = 0 + 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"] = 1 + 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"] = 1 + + 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"] = 1 + 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 = 0 + + 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 + . = 1 + update_to_assembly = 1 + + 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 = 1 + 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 = 0 + 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/datum/integrated_io/O in outputs) + O.push_data() + +/obj/item/integrated_circuit/proc/pull_data() + for(var/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + I.disconnect() + for(var/datum/integrated_io/O in outputs) + O.disconnect() + for(var/datum/integrated_io/activate/A in activators) + A.disconnect() diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm new file mode 100644 index 0000000000000..6789834df2d73 --- /dev/null +++ b/code/modules/integrated_electronics/core/pins.dm @@ -0,0 +1,189 @@ +/* + 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 = null + var/weakref/data = null // 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(var/newloc, var/name, var/new_data) + ..() + src.name = name + if(new_data) + src.data = new_data + holder = newloc + if(!istype(holder)) + message_admins("ERROR: An integrated_io ([src.name]) spawned without a valid holder! This is a bug.") + +/datum/integrated_io/Destroy() + disconnect() + data = null + holder = null + . = ..() + +/datum/integrated_io/proc/nano_host() + return holder.nano_host() + + +/datum/integrated_io/proc/data_as_type(var/as_type) + if(!isweakref(data)) + return + var/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/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)) // Anything else is a type we don't want. + if(istext(new_data)) + new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0) + data = new_data + holder.on_data_written() + +/datum/integrated_io/proc/push_data() + for(var/datum/integrated_io/io in linked) + io.write_data_to_pin(data) + +/datum/integrated_io/activate/push_data() + for(var/datum/integrated_io/io in linked) + io.holder.check_then_do_work() + +/datum/integrated_io/proc/pull_data() + for(var/datum/integrated_io/io in linked) + 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. + for(var/datum/integrated_io/their_io in linked) + //While doing that, we iterate them as well, and disconnect ourselves from them. + for(var/datum/integrated_io/their_linked_io in their_io.linked) + 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. + src.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/printer.dm b/code/modules/integrated_electronics/core/printer.dm new file mode 100644 index 0000000000000..75809dbf1867b --- /dev/null +++ b/code/modules/integrated_electronics/core/printer.dm @@ -0,0 +1,230 @@ + +/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 = 4 + var/metal = 0 + var/max_metal = 100 + var/metal_per_sheet = 10 // One sheet equals this much metal. + + 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 = list() + var/current_category = null + var/obj/item/device/electronic_assembly/assembly_to_clone = null + initialize() + +/obj/item/device/integrated_circuit_printer/upgraded + upgraded = TRUE + can_clone = TRUE + +/obj/item/device/integrated_circuit_printer/New() + ..() + initialize() + +/obj/item/device/integrated_circuit_printer/initialize() + ..() + if(!recipe_list.len) + // Unfortunately this needed a lot of loops, but it should only be run once at init. + + // First loop is to seperate the actual circuits from base circuits. + var/list/circuits_to_use = list() + for(var/obj/item/integrated_circuit/IC in all_integrated_circuits) + if((IC.spawn_flags & IC_SPAWN_DEFAULT) || (IC.spawn_flags & IC_SPAWN_RESEARCH)) + circuits_to_use.Add(IC) + + // Second loop is to find all categories. + var/list/found_categories = list() + for(var/obj/item/integrated_circuit/IC in circuits_to_use) + if(!(IC.category_text in found_categories)) + found_categories.Add(IC.category_text) + + // Third loop is to initialize lists by category names, then put circuits matching the category inside. + for(var/category in found_categories) + recipe_list[category] = list() + var/list/current_list = recipe_list[category] + for(var/obj/item/integrated_circuit/IC in circuits_to_use) + if(IC.category_text == category) + current_list.Add(IC) + + // Now for non-circuit things. + var/list/assembly_list = list() + assembly_list.Add( + new /obj/item/device/electronic_assembly/default(null), + new /obj/item/device/electronic_assembly/calc(null), + new /obj/item/device/electronic_assembly/clam(null), + new /obj/item/device/electronic_assembly/simple(null), + new /obj/item/device/electronic_assembly/hook(null), + new /obj/item/device/electronic_assembly/pda(null), + new /obj/item/device/electronic_assembly/medium/default(null), + new /obj/item/device/electronic_assembly/medium/box(null), + new /obj/item/device/electronic_assembly/medium/clam(null), + new /obj/item/device/electronic_assembly/medium/medical(null), + new /obj/item/device/electronic_assembly/medium/gun(null), + new /obj/item/device/electronic_assembly/medium/radio(null), + new /obj/item/device/electronic_assembly/large/default(null), + new /obj/item/device/electronic_assembly/large/scope(null), + new /obj/item/device/electronic_assembly/large/terminal(null), + new /obj/item/device/electronic_assembly/large/arm(null), + new /obj/item/device/electronic_assembly/large/tall(null), + new /obj/item/device/electronic_assembly/large/industrial(null), + new /obj/item/device/electronic_assembly/drone/default(null), + new /obj/item/device/electronic_assembly/drone/arms(null), + new /obj/item/device/electronic_assembly/drone/secbot(null), + new /obj/item/device/electronic_assembly/drone/medbot(null), + new /obj/item/device/electronic_assembly/drone/genbot(null), + new /obj/item/device/electronic_assembly/drone/android(null), + new /obj/item/device/electronic_assembly/wallmount/light(null), + new /obj/item/device/electronic_assembly/wallmount(null), + new /obj/item/device/electronic_assembly/wallmount/heavy(null), + new /obj/item/weapon/implant/integrated_circuit(null), + new /obj/item/device/assembly/electronic_assembly(null) + ) + recipe_list["Assemblies"] = assembly_list + + var/list/tools_list = list() + tools_list.Add( + new /obj/item/device/integrated_electronics/wirer(null), + new /obj/item/device/integrated_electronics/debugger(null), + new /obj/item/device/integrated_electronics/detailer(null) + ) + recipe_list["Tools"] = tools_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.amount >= num) + stack.use(num) + to_chat(user, "You add [num] sheet\s to \the [src].") + metal += num * metal_per_sheet + interact(user) + return TRUE + + if(istype(O,/obj/item/integrated_circuit)) + to_chat(user, "You insert the circuit into \the [src]. ") + user.unEquip(O) + metal = min(metal + O.w_class, max_metal) + qdel(O) + interact(user) + return TRUE + + if(istype(O,/obj/item/weapon/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/weapon/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 = list() + + HTML += "

Integrated Circuit Printer


" + HTML += "Metal: [metal/metal_per_sheet]/[max_metal/metal_per_sheet] sheets.
" + HTML += "Circuits available: [upgraded ? "Regular":"Advanced"]." + HTML += "Assembly Cloning: [can_clone ? "Available": "Unavailable"]." + if(assembly_to_clone) + HTML += "Assembly '[assembly_to_clone.name]' loaded." + HTML += "Crossed out circuits mean that the printer is not sufficentally upgraded to create that circuit.
" + 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/obj/O in current_list) + var/can_build = TRUE + if(istype(O, /obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/IC = O + if((IC.spawn_flags & IC_SPAWN_RESEARCH) && (!(IC.spawn_flags & IC_SPAWN_DEFAULT)) && !upgraded) + can_build = FALSE + if(can_build) + HTML += "\[[O.name]\]: [O.desc]
" + else + HTML += "\[[O.name]\]: [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(..()) + return 1 + + 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 1 + + 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 1 + metal -= cost + new build_type(get_turf(loc)) + + interact(usr) + +// FUKKEN UPGRADE DISKS +/obj/item/weapon/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 = 2 + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4) + +/obj/item/weapon/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/weapon/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 = 5, TECH_DATA = 6) \ 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 0000000000000..baf3d029daebf --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm @@ -0,0 +1,25 @@ +// 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 + 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 0000000000000..bd36d18495ec3 --- /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("!","@","#","$","%","^","&","*") + alphabet_uppercase + 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 0000000000000..2d20560fb6e7d --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/color_pin.dm @@ -0,0 +1,44 @@ +// 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 = get_random_colour(simple = FALSE, lower = 0, upper = 255) + 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 0000000000000..bbb00faa278a7 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/dir_pin.dm @@ -0,0 +1,33 @@ +// 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],\n\ + Up = [UP],\n\ + Down = [DOWN]","[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 alldirs + 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 0000000000000..e3c10b25df4c3 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/list_pin.dm @@ -0,0 +1,149 @@ +// These pins contain a list. Null is not allowed. +/datum/integrated_io/list + name = "list pin" + data = list() + + +/datum/integrated_io/list/ask_for_pin_data(mob/user) + interact(user) + +/datum/integrated_io/list/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/list/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/list/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/list/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/list/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("Which piece of data do you want to remove?", "Remove") as null|anything in my_list + if(target_entry) + my_list.Remove(target_entry) + +/datum/integrated_io/list/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("Which piece of data do you want to edit?", "Edit") as null|anything in my_list + if(target_entry) + var/edited_entry = ask_for_data_type(user, target_entry) + if(edited_entry) + target_entry = edited_entry + +/datum/integrated_io/list/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/list/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("Which piece of data do you want to swap? (1)", "Swap") as null|anything in my_list + + if(first_target) + if(!second_target) + second_target = input("Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target + + if(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/list/proc/clear_list(mob/user) + var/list/my_list = data + my_list.Cut() + +/datum/integrated_io/list/scramble() + var/list/my_list = data + my_list = shuffle(my_list) + push_data() + +/datum/integrated_io/list/write_data_to_pin(var/new_data) + if(islist(new_data)) + var/list/new_list = new_data + data = new_list.Copy() + holder.on_data_written() + +/datum/integrated_io/list/display_pin_type() + return IC_FORMAT_LIST + +/datum/integrated_io/list/Topic(href, href_list) + if(!holder.check_interactivity(usr)) + return + if(..()) + return 1 + + 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 0000000000000..319ac2de06bd2 --- /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 0000000000000..461965f254bcf --- /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 0000000000000..8f297fa340fcc --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/string_pin.dm @@ -0,0 +1,32 @@ +// 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 + new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0) + + if(new_data && 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) + new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0) + 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/string_length = length(data) + var/list/options = list("!","@","#","$","%","^","&","*") + alphabet_uppercase + var/new_data = "" + while(string_length) + new_data += pick(options) + string_length-- + push_data() + +/datum/integrated_io/string/display_pin_type() + return IC_FORMAT_STRING diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm new file mode 100644 index 0000000000000..a77c933befe83 --- /dev/null +++ b/code/modules/integrated_electronics/core/tools.dm @@ -0,0 +1,463 @@ +#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" + item_state = "wirer" + flags = CONDUCT + w_class = 2 + 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 + 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) + user << "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless." + return + if(io.io_type != selected_io.io_type) + 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 + selected_io.linked |= io + io.linked |= selected_io + + 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) + user << "There is nothing connected to \the [selected_io] data channel." + selected_io = null + return + 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) + 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) + 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 + user << "\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \ + [io.name] are not connected." + return + return + +/obj/item/device/integrated_electronics/wirer/attack_self(mob/user) + switch(mode) + if(WIRE) + mode = UNWIRE + if(WIRING) + if(selected_io) + user << "You decide not to wire the data channel." + selected_io = null + mode = WIRE + if(UNWIRE) + mode = WIRE + if(UNWIRING) + if(selected_io) + user << "You decide not to disconnect the data channel." + selected_io = null + mode = UNWIRE + update_icon() + user << "You set \the [src] to [mode]." + +#undef WIRE +#undef WIRING +#undef UNWIRE +#undef UNWIRING + +/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 = CONDUCT + w_class = 2 + var/data_to_write = null + var/accepting_refs = 0 + +/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(!CanInteract(user,src)) + return + + var/new_data = null + switch(type_to_use) + if("string") + accepting_refs = 0 + new_data = input("Now type in a string.","[src] string writing") as null|text + new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0) + if(istext(new_data) && CanInteract(user,src)) + data_to_write = new_data + user << "You set \the [src]'s memory to \"[new_data]\"." + if("number") + accepting_refs = 0 + new_data = input("Now type in a number.","[src] number writing") as null|num + if(isnum(new_data)) + data_to_write = new_data + user << "You set \the [src]'s memory to [new_data]." + if("ref") + accepting_refs = 1 + 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 + 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 = target + visible_message("[user] slides \a [src]'s over \the [target].") + user << "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ + now off." + accepting_refs = 0 + +/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/weakref/w = data_to_write + var/atom/A = w.resolve() + data_to_show = A.name + 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. + + + + +/obj/item/device/multitool + var/datum/integrated_io/selected_io = null + 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) + if(buffer) + icon_state = "multitool_tracking" + else + icon_state = "multitool_red" + else + if(buffer) + icon_state = "multitool_tracking_fail" + 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() + + + + + + + + + + + +/obj/item/weapon/storage/bag/circuits + name = "circuit kit" + desc = "This kit's essential for any circuitry projects." + icon = 'icons/obj/electronic_assemblies.dmi' + icon_state = "circuit_kit" + w_class = 3 + display_contents_with_number = 0 + can_hold = list( + /obj/item/integrated_circuit, + /obj/item/weapon/storage/bag/circuits/mini, + /obj/item/device/electronic_assembly, + /obj/item/device/integrated_electronics, + /obj/item/weapon/crowbar, + /obj/item/weapon/screwdriver + ) + +/obj/item/weapon/storage/bag/circuits/basic/New() + ..() + spawn(2 SECONDS) // So the list has time to initialize. +// for(var/obj/item/integrated_circuit/IC in all_integrated_circuits) +// if(IC.spawn_flags & IC_SPAWN_DEFAULT) +// for(var/i = 1 to 3) +// new IC.type(src) + new /obj/item/weapon/storage/bag/circuits/mini/arithmetic(src) + new /obj/item/weapon/storage/bag/circuits/mini/trig(src) + new /obj/item/weapon/storage/bag/circuits/mini/input(src) + new /obj/item/weapon/storage/bag/circuits/mini/output(src) + new /obj/item/weapon/storage/bag/circuits/mini/memory(src) + new /obj/item/weapon/storage/bag/circuits/mini/logic(src) + new /obj/item/weapon/storage/bag/circuits/mini/time(src) + new /obj/item/weapon/storage/bag/circuits/mini/reagents(src) + new /obj/item/weapon/storage/bag/circuits/mini/transfer(src) + new /obj/item/weapon/storage/bag/circuits/mini/converter(src) +// new /obj/item/weapon/storage/bag/circuits/mini/power(src) + + new /obj/item/device/electronic_assembly(src) + new /obj/item/device/integrated_electronics/wirer(src) + new /obj/item/device/integrated_electronics/debugger(src) + new /obj/item/weapon/crowbar(src) + new /obj/item/weapon/screwdriver(src) + make_exact_fit() + +/obj/item/weapon/storage/bag/circuits/all/New() + ..() + spawn(2 SECONDS) // So the list has time to initialize. +// new /obj/item/weapon/storage/bag/circuits/mini/arithmetic/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/trig/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/input/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/output/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/memory/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/logic/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/smart/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/manipulation/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/time/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/reagents/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/transfer/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/converter/all(src) +// new /obj/item/weapon/storage/bag/circuits/mini/power/all(src) + + new /obj/item/device/electronic_assembly(src) + new /obj/item/device/electronic_assembly/medium(src) + new /obj/item/device/electronic_assembly/large(src) + new /obj/item/device/electronic_assembly/drone(src) + new /obj/item/device/integrated_electronics/wirer(src) + new /obj/item/device/integrated_electronics/debugger(src) + new /obj/item/weapon/crowbar(src) + new /obj/item/weapon/screwdriver(src) + make_exact_fit() + +/obj/item/weapon/storage/bag/circuits/mini/ + name = "circuit box" + desc = "Used to partition categories of circuits, for a neater workspace." + w_class = 2 + display_contents_with_number = 1 + can_hold = list(/obj/item/integrated_circuit) + +/obj/item/weapon/storage/bag/circuits/mini/arithmetic + name = "arithmetic circuit box" + desc = "Warning: Contains math." + icon_state = "box_arithmetic" + +/obj/item/weapon/storage/bag/circuits/mini/arithmetic/New() + ..() + for(var/obj/item/integrated_circuit/arithmetic/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/trig + name = "trig circuit box" + desc = "Danger: Contains more math." + icon_state = "box_trig" + +/obj/item/weapon/storage/bag/circuits/mini/trig/New() + ..() + for(var/obj/item/integrated_circuit/trig/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/input + name = "input circuit box" + desc = "Tell these circuits everything you know." + icon_state = "box_input" + +/obj/item/weapon/storage/bag/circuits/mini/input/New() + ..() + for(var/obj/item/integrated_circuit/input/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/output + name = "output circuit box" + desc = "Circuits to interface with the world beyond itself." + icon_state = "box_output" + +/obj/item/weapon/storage/bag/circuits/mini/output/New() + ..() + for(var/obj/item/integrated_circuit/output/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/memory + name = "memory circuit box" + desc = "Machines can be quite forgetful without these." + icon_state = "box_memory" + +/obj/item/weapon/storage/bag/circuits/mini/memory/New() + ..() + for(var/obj/item/integrated_circuit/memory/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + + +/obj/item/weapon/storage/bag/circuits/mini/logic + name = "logic circuit box" + desc = "May or may not be Turing complete." + icon_state = "box_logic" + +/obj/item/weapon/storage/bag/circuits/mini/logic/New() + ..() + for(var/obj/item/integrated_circuit/logic/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/time + name = "time circuit box" + desc = "No time machine parts, sadly." + icon_state = "box_time" + +/obj/item/weapon/storage/bag/circuits/mini/time/New() + ..() + for(var/obj/item/integrated_circuit/time/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/reagents + name = "reagent circuit box" + desc = "Unlike most electronics, these circuits are supposed to come in contact with liquids." + icon_state = "box_reagents" + +/obj/item/weapon/storage/bag/circuits/mini/reagents/New() + ..() + for(var/obj/item/integrated_circuit/reagent/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/transfer + name = "transfer circuit box" + desc = "Useful for moving data representing something arbitrary to another arbitrary virtual place." + icon_state = "box_transfer" + +/obj/item/weapon/storage/bag/circuits/mini/transfer/New() + ..() + for(var/obj/item/integrated_circuit/transfer/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() + + +/obj/item/weapon/storage/bag/circuits/mini/converter + name = "converter circuit box" + desc = "Transform one piece of data to another type of data with these." + icon_state = "box_converter" + +/obj/item/weapon/storage/bag/circuits/mini/converter/New() + ..() + for(var/obj/item/integrated_circuit/converter/IC in all_integrated_circuits) + if(IC.spawn_flags & IC_SPAWN_DEFAULT) + for(var/i = 1 to 3) + new IC.type(src) + make_exact_fit() \ 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 0000000000000..02f03d48d7c40 --- /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 0000000000000..788003b0f13f1 --- /dev/null +++ b/code/modules/integrated_electronics/passive/power.dm @@ -0,0 +1,166 @@ + +/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 = 1 + +/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 a 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=0 + +/obj/item/integrated_circuit/passive/power/starter/make_energy() + if(assembly.battery) + if(assembly.battery.charge) + if(!is_charge) + activate_pin(1) + is_charge=1 + else + is_charge=0 + else + is_charge=0 + return FALSE + +// For implants. +/obj/item/integrated_circuit/passive/power/metabolic_siphon + name = "metabolic siphon" + desc = "A complicated piece of technology which converts bodily nutriments of a host into electricity." + extended_desc = "The siphon generates 10W of energy, so long as the siphon exists inside a biological entity. The entity will feel an increased \ + appetite and will need to eat more often due to this. This device will fail if used inside synthetic entities." + icon_state = "setup_implant" + complexity = 10 + origin_tech = list(TECH_POWER = 4, TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_BIO = 5) + spawn_flags = IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/passive/power/metabolic_siphon/proc/test_validity(var/mob/living/carbon/human/host) + if(!host || host.isSynthetic() || host.stat == DEAD || host.nutrition <= 10) + return FALSE // Robots and dead people don't have a metabolism. + return TRUE + +/obj/item/integrated_circuit/passive/power/metabolic_siphon/make_energy() + var/mob/living/carbon/human/host = null + if(assembly && istype(assembly, /obj/item/device/electronic_assembly/implant)) + var/obj/item/device/electronic_assembly/implant/implant_assembly = assembly + if(implant_assembly.implant.imp_in) + host = implant_assembly.implant.imp_in + if(host && test_validity(host)) + assembly.give_power(10) + host.nutrition = max(host.nutrition - HUNGER_FACTOR, 0) + +/obj/item/integrated_circuit/passive/power/metabolic_siphon/synthetic + name = "internal energy siphon" + desc = "A small circuit designed to be connected to an internal power wire inside a synthetic entity." + extended_desc = "The siphon generates 10W of energy, so long as the siphon exists inside a synthetic entity. The entity need to recharge \ + more often due to this. This device will fail if used inside organic entities." + icon_state = "setup_implant" + complexity = 10 + origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 4, TECH_DATA = 3) + spawn_flags = IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/passive/power/metabolic_siphon/synthetic/test_validity(var/mob/living/carbon/human/host) + if(!host || !host.isSynthetic() || host.stat == DEAD || host.nutrition <= 10) + return FALSE // This time we don't want a metabolism. + return TRUE + +// 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 = 3 + 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 = 250 +//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 plasma, 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." + flags = OPENCONTAINER + 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" = 50000, "slimejelly" = 25000, "fuel" = 15000, "carbon" = 10000, "ethanol"= 10000, "nutriment" =8000, "blood" = 5000) + +/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) + for(var/I in fuel) + if((assembly.battery.maxcharge-assembly.battery.charge) / 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 = 4 + 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 = 2000 + +/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 0000000000000..192d317baff39 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm @@ -0,0 +1,314 @@ +//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/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + 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/datum/integrated_io/I in inputs) + 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/built_in.dm b/code/modules/integrated_electronics/subtypes/built_in.dm new file mode 100644 index 0000000000000..05053eec4cb06 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/built_in.dm @@ -0,0 +1,28 @@ +/obj/item/integrated_circuit/built_in + 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" + size = -1 + w_class = 1 + removable = FALSE // Determines if a circuit is removable from the assembly. + +/obj/item/integrated_circuit/built_in/device_input + name = "assembly input" + desc = "A built in chip for handling pulses from attached assembly items." + complexity = 0 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'. + activators = list("on pulsed" = IC_PINTYPE_PULSE_OUT) + +/obj/item/integrated_circuit/built_in/device_input/do_work() + activate_pin(1) + +/obj/item/integrated_circuit/built_in/device_output + name = "assembly out" + desc = "A built in chip for pulsing attached assembly items." + complexity = 0 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'. + activators = list("pulse attached" = IC_PINTYPE_PULSE_IN) + +/obj/item/integrated_circuit/built_in/device_output/do_work() + if(istype(assembly, /obj/item/device/electronic_assembly/device)) + var/obj/item/device/electronic_assembly/device/device = assembly + device.holder.pulse() \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm new file mode 100644 index 0000000000000..b9f5c912d4c14 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/converters.dm @@ -0,0 +1,322 @@ +//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 = "\ref[A]" + + 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 an 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 + + +/obj/item/integrated_circuit/converter/refdecode/do_work() + pull_data() + set_pin_data(IC_OUTPUT, 1, weakref(locate(get_pin_data(IC_INPUT, 1)))) + 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) \ No newline at end of file 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 0000000000000..697a2be2593e3 --- /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 = 2 + icon_state = "mux8" + +/obj/item/integrated_circuit/transfer/multiplexer/huge + name = "sixteen multiplexer" + icon_state = "mux16" + w_class = 2 + 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 = 2 + number_of_outputs = 8 + +/obj/item/integrated_circuit/transfer/demultiplexer/huge + name = "sixteen demultiplexer" + icon_state = "dmux16" + w_class = 2 + 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 = 2 + number_of_outputs = 8 + +/obj/item/integrated_circuit/transfer/pulsedemultiplexer/huge + name = "sixteen pulse demultiplexer" + icon_state = "dmux16" + w_class = 2 + 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 0000000000000..583c50c4a3a60 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -0,0 +1,857 @@ +/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.name]'.") + 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.name]' [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) && CanInteract(user, src)) + 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) && CanInteract(user, src)) + 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/adv_med_scanner + + name = "integrated advanced medical analyzer" + desc = "A very small version of the medibot's medical analyzer. 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/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 + 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 of up to 8 meters" + 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 to given sample. If this pin is a string, the locator will search for\ + 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 = range(radius, T) & view(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() + ..() + 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() + if(radio_controller) + radio_controller.remove_object(src,frequency) + frequency = 0 + . = ..() + +/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(!radio_controller) + sleep(20) + if(!radio_controller) + return + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, 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))) + O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2) + +/obj/item/integrated_circuit/input/EPv2 + name = "\improper 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 activaiton 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) + 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) + if(!T) + return + + set_pin_data(IC_OUTPUT, 1, T.x) + set_pin_data(IC_OUTPUT, 2, T.y) + + 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" + flags = HEAR + complexity = 8 + inputs = list() + outputs = list( + "speaker" = IC_PINTYPE_STRING, + "message" = IC_PINTYPE_STRING + ) + activators = list("on message received" = IC_PINTYPE_PULSE_IN, "on translation" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 15 +/* +/obj/item/integrated_circuit/input/microphone/New() + ..() + listening_objects |= src + +/obj/item/integrated_circuit/input/microphone/Destroy() + listening_objects -= src + return ..() +*/ +/obj/item/integrated_circuit/input/microphone/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) + var/datum/integrated_io/V = outputs[1] + var/datum/integrated_io/O = outputs[2] + var/datum/integrated_io/A = activators[1] + if(speaker == src) + return + //msg = lang_treat(msg, M, message_langs, raw_message, spans) + V.data = speaker.GetVoice() + O.data = raw_message + A.push_data() + + for(var/datum/integrated_io/out in outputs) + out.push_data() + + A.push_data() + + + +/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/weapon/storage)) + return FALSE + + set_pin_data(IC_OUTPUT, 1, weakref(A)) + push_data() + activate_pin(1) + return TRUE + +/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 = 1 + 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 watch the battery level of any device in range." + 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 = 1 + 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/weapon/stock_parts/cell/cell = null + if(istype(AM, /obj/item/weapon/stock_parts/cell)) // Is this already a cell? + cell = AM + else // If not, maybe there's a cell inside it? + for(var/obj/item/weapon/stock_parts/cell/C in AM.contents) + if(C) // Find one cell to charge. + cell = C + break + 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) + +/obj/item/integrated_circuit/input/atmo_scanner + name = "integrated atmospheric analyser" + desc = "The same atmospheric analysis module that is integrated into every PDA. \ + This allows the machine to know the composition, temperature and pressure of the surrounding atmosphere." + icon_state = "medscan_adv" + complexity = 9 + inputs = list() + outputs = list( + "pressure" = IC_PINTYPE_NUMBER, + "temperature" = IC_PINTYPE_NUMBER, + "oxygen" = IC_PINTYPE_NUMBER, + "nitrogen" = IC_PINTYPE_NUMBER, + "carbon dioxide" = IC_PINTYPE_NUMBER, + "plasma" = IC_PINTYPE_NUMBER, + "other" = 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) + power_draw_per_use = 60 + +/obj/item/integrated_circuit/input/atmo_scanner/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + if (total_moles) + var/o2_level = environment.oxygen/total_moles + var/n2_level = environment.nitrogen/total_moles + var/co2_level = environment.carbon_dioxide/total_moles + var/plasma_level = environment.toxins/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) + set_pin_data(IC_OUTPUT, 1, pressure) + set_pin_data(IC_OUTPUT, 2, round(environment.temperature-T0C,0.1)) + set_pin_data(IC_OUTPUT, 3, round(o2_level*100,0.1)) + set_pin_data(IC_OUTPUT, 4, round(n2_level*100,0.1)) + set_pin_data(IC_OUTPUT, 5, round(co2_level*100,0.1)) + set_pin_data(IC_OUTPUT, 6, round(plasma_level*100,0.01)) + set_pin_data(IC_OUTPUT, 7, round(unknown_level, 0.01)) + else + set_pin_data(IC_OUTPUT, 1, 0) + set_pin_data(IC_OUTPUT, 2, -273.15) + set_pin_data(IC_OUTPUT, 3, 0) + set_pin_data(IC_OUTPUT, 4, 0) + set_pin_data(IC_OUTPUT, 5, 0) + set_pin_data(IC_OUTPUT, 6, 0) + set_pin_data(IC_OUTPUT, 7, 0) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/pressure_sensor + name = "integrated pressure sensor" + desc = "A tiny pressure sensor module similar to that found in a PDA atmosphere analyser." + icon_state = "medscan_adv" + complexity = 3 + inputs = list() + outputs = list( + "pressure" = 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) + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/pressure_sensor/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + if (total_moles) + set_pin_data(IC_OUTPUT, 1, pressure) + else + set_pin_data(IC_OUTPUT, 1, 0) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/temperature_sensor + name = "integrated temperature sensor" + desc = "A tiny temperature sensor module similar to that found in a PDA atmosphere analyser." + icon_state = "medscan_adv" + complexity = 3 + inputs = list() + outputs = list( + "temperature" = 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) + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/temperature_sensor/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/total_moles = environment.total_moles() + + if (total_moles) + set_pin_data(IC_OUTPUT, 1, round(environment.temperature-T0C,0.1)) + else + set_pin_data(IC_OUTPUT, 1, -273.15) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/oxygen_sensor + name = "integrated oxygen sensor" + desc = "A tiny oxygen sensor module similar to that found in a PDA atmosphere analyser." + icon_state = "medscan_adv" + complexity = 3 + inputs = list() + outputs = list( + "oxygen" = 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) + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/oxygen_sensor/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/total_moles = environment.total_moles() + + if (total_moles) + var/o2_level = environment.oxygen/total_moles + set_pin_data(IC_OUTPUT, 1, round(o2_level*100,0.1)) + else + set_pin_data(IC_OUTPUT, 1, 0) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/co2_sensor + name = "integrated co2 sensor" + desc = "A tiny carbon dioxide sensor module similar to that found in a PDA atmosphere analyser." + icon_state = "medscan_adv" + complexity = 3 + inputs = list() + outputs = list( + "co2" = 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) + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/co2_sensor/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/total_moles = environment.total_moles() + + if (total_moles) + var/co2_level = environment.carbon_dioxide/total_moles + set_pin_data(IC_OUTPUT, 1, round(co2_level*100,0.1)) + else + set_pin_data(IC_OUTPUT, 1, 0) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/nitrogen_sensor + name = "integrated nitrogen sensor" + desc = "A tiny nitrogen sensor module similar to that found in a PDA atmosphere analyser." + icon_state = "medscan_adv" + complexity = 3 + inputs = list() + outputs = list( + "nitrogen" = 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) + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/nitrogen_sensor/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/total_moles = environment.total_moles() + + if (total_moles) + var/n2_level = environment.nitrogen/total_moles + set_pin_data(IC_OUTPUT, 1, round(n2_level*100,0.1)) + else + set_pin_data(IC_OUTPUT, 1, 0) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/plasma_sensor + name = "integrated plasma sensor" + desc = "A tiny plasma gas sensor module similar to that found in a PDA atmosphere analyser." + icon_state = "medscan_adv" + complexity = 3 + inputs = list() + outputs = list( + "plasma" = 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) + power_draw_per_use = 20 + +/obj/item/integrated_circuit/input/plasma_sensor/do_work() + var/turf/T = get_turf(src) + if(!istype(T)) //Invalid input + return + var/datum/gas_mixture/environment = T.return_air() + + var/total_moles = environment.total_moles() + + if (total_moles) + var/plasma_level = environment.toxins/total_moles + set_pin_data(IC_OUTPUT, 1, round(plasma_level*100,0.1)) + else + set_pin_data(IC_OUTPUT, 1, 0) + 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 0000000000000..f2c9e52ccc1d9 --- /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/list + 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/list/pick + name = "pick circuit" + desc = "This circuit will randomly 'pick' an element from a list that is inputted." + extended_desc = "Will output null if the list is empty. Input list is unmodified." + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/list/pick/do_work() + var/result = null + var/list/input_list = get_pin_data(IC_INPUT, 1) // List pins guarantee that there is a list inside, even if just an empty one. + if(input_list.len) + result = pick(input_list) + + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/list/append + name = "append circuit" + desc = "This circuit will add an element to a list." + extended_desc = "The new element will always be at the bottom of the list." + 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/list/append/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/list/output_list = list() + var/new_entry = get_pin_data(IC_INPUT, 2) + output_list = input_list.Copy() + output_list.Add(new_entry) + + set_pin_data(IC_OUTPUT, 1, output_list) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/list/search + name = "search circuit" + desc = "This circuit will give index of desired element in the list." + 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/list/search/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/item = get_pin_data(IC_INPUT, 2) + set_pin_data(IC_OUTPUT, 1, input_list.Find(item)) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/list/at + name = "at circuit" + desc = "This circuit will pick an element from a list by index." + 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/list/at/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/index = get_pin_data(IC_INPUT, 2) + var/item = input_list[index] + set_pin_data(IC_OUTPUT, 1, item) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/list/delete + name = "delete circuit" + desc = "This circuit will delete the element from a list by index." + 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/list/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) + var/j = 0 + for(var/I in input_list) + j = j + 1 + if(j != index) + red_list.Add(I) + set_pin_data(IC_OUTPUT, 1, red_list) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/list/write + name = "write circuit" + desc = "This circuit will write element in list with given index." + extended_desc = "If there is no element with such index, it will give the same list, as before." + 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/list/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) + input_list[index] = item + set_pin_data(IC_OUTPUT, 1, input_list) + push_data() + activate_pin(2) + +obj/item/integrated_circuit/list/len + name = "len circuit" + desc = "This circuit will give length of the list." + inputs = list( + "list" = IC_PINTYPE_LIST, + ) + outputs = list( + "item" = IC_PINTYPE_NUMBER + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/list/len/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + set_pin_data(IC_OUTPUT, 1, input_list.len) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/list/jointext + name = "join text circuit" + desc = "This circuit will add all elements of a list into one string, seperated by a character." + extended_desc = "Default settings will encode the entire list into a string." + 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/list/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) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm new file mode 100644 index 0000000000000..82667634a1c0c --- /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 a 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 a RS latch. If both R and S are true, the 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 a 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 0000000000000..1b28c8f4a5c5e --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -0,0 +1,207 @@ +/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 most ranged weapons, ballistic and energy. \ + 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. Note that the \ + normal limitations to firearms, such as ammunition requirements and firing delays, still hold true if fired by the mechanism." + complexity = 20 + w_class = 3 + size = 3 + inputs = list( + "target X rel" = IC_PINTYPE_NUMBER, + "target Y rel" = IC_PINTYPE_NUMBER + ) + outputs = list() + activators = list( + "fire" = IC_PINTYPE_PULSE_IN + ) + var/obj/item/weapon/gun/installed_gun = null + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 4) + power_draw_per_use = 50 // The targeting mechanism uses this. The actual gun uses its own cell for firing if it's an energy weapon. + +/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/weapon/gun)) + var/obj/item/weapon/gun/gun = O + if(installed_gun) + user << "There's already a weapon installed." + return + user.remove_from_mob(gun) + //user.drop_from_inventory(gun) + installed_gun = gun + size += gun.w_class + gun.forceMove(src) + user << "You slide \the [gun] into the firing mechanism." + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + else + ..() + +/obj/item/integrated_circuit/manipulation/weapon_firing/attack_self(var/mob/user) + if(installed_gun) + installed_gun.forceMove(get_turf(src)) + user << "You slide \the [installed_gun] out of the firing mechanism." + size = initial(size) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + installed_gun = null + else + user << "There's no weapon to remove from the mechanism." + +/obj/item/integrated_circuit/manipulation/weapon_firing/do_work() + if(!installed_gun) + return + + var/datum/integrated_io/target_x = inputs[1] + var/datum/integrated_io/target_y = inputs[2] + + if(src.assembly) + if(isnum(target_x.data)) + target_x.data = round(target_x.data) + if(isnum(target_y.data)) + target_y.data = round(target_y.data) + + var/turf/T = get_turf(src.assembly) + + if(target_x.data == 0 && target_y.data == 0) // Don't shoot ourselves. + return + + // We need to do this in order to enable relative coordinates, as locate() only works for absolute coordinates. + var/i + if(target_x.data > 0) + i = abs(target_x.data) + while(i > 0) + T = get_step(T, EAST) + i-- + else + i = abs(target_x.data) + while(i > 0) + T = get_step(T, WEST) + i-- + + i = 0 + if(target_y.data > 0) + i = abs(target_y.data) + while(i > 0) + T = get_step(T, NORTH) + i-- + else if(target_y.data < 0) + i = abs(target_y.data) + while(i > 0) + T = get_step(T, SOUTH) + i-- + + if(!T) + return + installed_gun.Fire_userless(T) + +/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 = 3 + complexity = 20 +// size = 5 + inputs = list("direction" = IC_PINTYPE_DIR) + outputs = list() + activators = list("step towards dir" = IC_PINTYPE_PULSE_IN) + 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(istype(loc, /obj/item/device/electronic_assembly)) + var/obj/item/device/electronic_assembly/machine = loc + if(!machine.can_move()) + return + if(machine.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)) + step(machine, wanted_dir.data) + +/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 + size = 2 + 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/weapon/grenade/attached_grenade + var/pre_attached_grenade_type + +/obj/item/integrated_circuit/manipulation/grenade/New() + ..() + 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.loc = loc + detach_grenade() + . =..() + +/obj/item/integrated_circuit/manipulation/grenade/attackby(var/obj/item/weapon/grenade/G, var/mob/user) + if(istype(G)) + if(attached_grenade) + to_chat(user, "There is already a grenade attached!") + else if(user.unEquip(G, 1)) + 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 + ..() + +/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_any_hand_if_possible(attached_grenade) || (attached_grenade.loc = loc) + detach_grenade() + else + ..() + +/obj/item/integrated_circuit/manipulation/grenade/do_work() + if(attached_grenade && !attached_grenade.active) + var/datum/integrated_io/detonation_time = inputs[1] + if(isnum(detonation_time.data) && detonation_time.data > 0) + attached_grenade.det_time = Clamp(detonation_time.data, 1, 12) SECONDS + attached_grenade.prime() +// var/atom/holder = loc +// log_and_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/weapon/grenade/G) + attached_grenade = G +// destroyed_event.register(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade) + size += G.w_class + desc += " \An [attached_grenade] is attached to it!" + +/obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade() + if(!attached_grenade) + return +// destroyed_event.unregister(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade) + attached_grenade = null + size = initial(size) + desc = initial(desc) + +/obj/item/integrated_circuit/manipulation/grenade/frag + pre_attached_grenade_type = /obj/item/weapon/grenade + origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 10) + spawn_flags = null // Used for world initializing, see the #defines above. \ 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 0000000000000..0d9e2962453e2 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/memory.dm @@ -0,0 +1,125 @@ +/obj/item/integrated_circuit/memory + name = "memory chip" + desc = "This tiny chip can store one piece of data." + icon_state = "memory1" + 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 + 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 = "memory2" + 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 = 3 + 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" + complexity = 1 + inputs = list() + outputs = list("output pin" = IC_PINTYPE_ANY) + activators = list("push data" = IC_PINTYPE_PULSE_IN) + var/accepting_refs = 0 + 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] + var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") + //if(!CanInteract(user,src)) + // return + + var/new_data = null + switch(type_to_use) + if("string") + accepting_refs = 0 + new_data = input("Now type in a string.","[src] string writing") as null|text + if(istext(new_data)) + O.data = new_data + user << "You set \the [src]'s memory to [O.display_data()]." + if("number") + accepting_refs = 0 + new_data = input("Now type in a number.","[src] number writing") as null|num + if(isnum(new_data)) + O.data = new_data + user << "You set \the [src]'s memory to [O.display_data()]." + if("ref") + accepting_refs = 1 + 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 + 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].") + user << "You set \the [src]'s memory to a reference to [O.display_data()]. The ref scanner is \ + now off." + accepting_refs = 0 \ 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 0000000000000..4e9da97d2ad2c --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -0,0 +1,339 @@ +/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, "\icon[O] [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("\icon[O] [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 = 20 + 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 + cooldown_per_use = 4 SECONDS + 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 + audible_message("\icon[O] \The [O.name] states, \"[text]\"") + +/obj/item/integrated_circuit/output/sound/New() + ..() + 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/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/medbot/msurgeon.ogg', + "radar" = 'sound/voice/medbot/mradar.ogg', + "feel better" = 'sound/voice/medbot/mfeelbetter.ogg', + "patched up" = 'sound/voice/medbot/mpatchedup.ogg', + "injured" = 'sound/voice/medbot/minjured.ogg', + "insult" = 'sound/voice/medbot/minsult.ogg', + "coming" = 'sound/voice/medbot/mcoming.ogg', + "help" = 'sound/voice/medbot/mhelp.ogg', + "live" = 'sound/voice/medbot/mlive.ogg', + "lost" = 'sound/voice/medbot/mlost.ogg', + "flies" = 'sound/voice/medbot/mflies.ogg', + "catch" = 'sound/voice/medbot/mcatch.ogg', + "delicious" = 'sound/voice/medbot/mdelicious.ogg', + "apple" = 'sound/voice/medbot/mapple.ogg', + "no" = 'sound/voice/medbot/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 = 2 + 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 = 5 // Raises to 80 when on. + var/obj/machinery/camera/camera + +/obj/item/integrated_circuit/output/video_camera/New() + ..() + camera = new(src) + on_data_written() + +/obj/item/integrated_circuit/output/video_camera/Destroy() + qdel(camera) + return ..() + +/obj/item/integrated_circuit/output/video_camera/proc/set_camera_status(var/status) + if(camera) + camera.status = status + power_draw_idle = camera.status ? 80 : 5 + 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/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/any_examine(mob/user) + var/text_output = list() + var/initial_name = initial(name) + + // Doing all this work just to have a color-blind friendly output. + text_output += "There is " + if(name == initial_name) + text_output += "\an [name]" + else + text_output += "\an ["\improper[initial_name]"] labeled '[name]'" + text_output += " which is currently [get_pin_data(IC_INPUT, 1) ? "lit ¤" : "unlit."]" + to_chat(user,jointext(text_output,null)) + +/obj/item/integrated_circuit/output/led/red + name = "red LED" + led_color = COLOR_RED + +/obj/item/integrated_circuit/output/led/orange + name = "orange LED" + led_color = COLOR_ORANGE + +/obj/item/integrated_circuit/output/led/yellow + name = "yellow LED" + led_color = COLOR_YELLOW + +/obj/item/integrated_circuit/output/led/green + name = "green LED" + led_color = COLOR_GREEN + +/obj/item/integrated_circuit/output/led/blue + name = "blue LED" + led_color = COLOR_BLUE + +/obj/item/integrated_circuit/output/led/purple + name = "purple LED" + led_color = COLOR_PURPLE + +/obj/item/integrated_circuit/output/led/cyan + name = "cyan LED" + led_color = COLOR_CYAN + +/obj/item/integrated_circuit/output/led/white + name = "white LED" + led_color = COLOR_WHITE + +/obj/item/integrated_circuit/output/led/pink + name = "pink LED" + led_color = COLOR_PINK diff --git a/code/modules/integrated_electronics/subtypes/power.dm b/code/modules/integrated_electronics/subtypes/power.dm new file mode 100644 index 0000000000000..339448a12b9a7 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/power.dm @@ -0,0 +1,91 @@ +/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 = 2 + 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_MAGNETS = 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 = 4 + complexity = 32 + origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 6, TECH_MAGNETS = 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/weapon/stock_parts/cell/cell = null + if(istype(AM, /obj/item/weapon/stock_parts/cell)) // Is this already a cell? + cell = AM + else // If not, maybe there's a cell inside it? + for(var/obj/item/weapon/stock_parts/cell/C in AM.contents) + if(C) // Find one cell to charge. + cell = C + break + 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 * CELLRATE) + cell.update_icon() + + 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(2)) + var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() + sparks.set_up(3, 0, get_turf(src)) + sparks.start() + visible_message("\The [assembly] makes some sparks!") + qdel(sparks) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm new file mode 100644 index 0000000000000..aa12f7dcceea0 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -0,0 +1,355 @@ +/obj/item/integrated_circuit/reagent + category_text = "Reagent" + var/volume = 0 + unacidable = 1 + 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." + flags = OPENCONTAINER + complexity = 20 + cooldown_per_use = 30 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 + +/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() + playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) + var/datum/effect/effect/system/smoke_spread/chem/smoke_system = new() + smoke_system.set_up(reagents, 10, 0, get_turf(src)) + spawn(0) + for(var/i = 1 to 8) + smoke_system.start() + 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." + flags = OPENCONTAINER + 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/direc = 1 + var/transfer_amount = 10 + +/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 + direc = 0 + else + direc = 1 + if(isnum(new_amount)) + new_amount = Clamp(new_amount, 0, volume) + transfer_amount = new_amount + + +/obj/item/integrated_circuit/reagent/injector/do_work() + set waitfor = 0 // Don't sleep in a proc that is called by a processor without this set, otherwise it'll delay the entire thing + var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + if(!istype(AM)) //Invalid input + activate_pin(3) + return + + if(direc == 1) + + if(!istype(AM)) //Invalid input + activate_pin(3) + return + if(!reagents.total_volume) // Empty + activate_pin(3) + return + if(AM.can_be_injected_by(src)) + if(isliving(AM)) + var/mob/living/L = AM + var/turf/T = get_turf(AM) + T.visible_message("[src] is trying to inject [L]!") + sleep(3 SECONDS) + if(!L.can_be_injected_by(src)) + activate_pin(3) + return + var/contained = reagents.get_reagents() + var/trans = reagents.trans_to(L, transfer_amount) + message_admins("[src] injected \the [L] with [trans]u of [contained].") + to_chat(AM, "You feel a tiny prick!") + visible_message("[src] injects [L]!") + else + reagents.trans_to(AM, transfer_amount) + else + + if(reagents.total_volume >= volume) // Full + activate_pin(3) + return + var/obj/target = AM + if(!target.reagents) + activate_pin(3) + return + var/turf/TS = get_turf(src) + var/turf/TT = get_turf(AM) + if(!TS.Adjacent(TT)) + activate_pin(3) + return + var/tramount = Clamp(min(transfer_amount, reagents.maximum_volume - reagents.total_volume), 0, reagents.maximum_volume) + if(ismob(target))//Blood! + if(istype(target, /mob/living/carbon)) + var/mob/living/carbon/T = target + if(!T.dna) + if(T.reagents.trans_to(src, tramount)) + activate_pin(2) + else + activate_pin(3) + return + if(NOCLONE in T.mutations) //target done been et, no more blood in him + if(T.reagents.trans_to(src, tramount)) + activate_pin(2) + else + activate_pin(3) + return + return + var/datum/reagent/B + B = T.take_blood(src,tramount) + if (B) + reagents.reagent_list |= B + reagents.update_total() + on_reagent_change() + reagents.handle_reactions() + B = null + visible_message( "Machine takes a blood sample from [target].") + else + activate_pin(3) + return + + else //if not mob + if(!target.reagents.total_volume) + visible_message( "[target] is empty.") + activate_pin(3) + return + target.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." + flags = OPENCONTAINER + 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/direc = 1 + 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 + direc = 0 + else + direc = 1 + 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)) //Invalid input + return + var/turf/T = get_turf(src) + var/turf/TS = get_turf(source) + var/turf/TT = get_turf(target) + if(TS.Adjacent(T) && TT.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(direc) + if(!target.reagents.get_free_space()) + return + source.reagents.trans_to(target, transfer_amount) + else + if(!source.reagents.get_free_space()) + 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." + flags = OPENCONTAINER + 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." + flags = OPENCONTAINER | NOREACT + complexity = 8 + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) + +/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." + flags = OPENCONTAINER + complexity = 16 + volume = 180 + spawn_flags = IC_SPAWN_RESEARCH + origin_tech = list(TECH_MATERIAL = 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." + flags = OPENCONTAINER + 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_MATERIAL = 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." + flags = OPENCONTAINER + 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/direc = 1 + 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 + direc = 0 + else + direc = 1 + 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.get_free_space()) + return + for(var/datum/reagent/G in source.reagents.reagent_list) + if (!direc) + 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 0000000000000..0e16cd40794ba --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -0,0 +1,34 @@ +/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. + var/desired_dir = get_dir(get_turf(src), get_turf(A)) + + set_pin_data(IC_OUTPUT, 1, desired_dir) + push_data() + activate_pin(2) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm new file mode 100644 index 0000000000000..e5a943657d06f --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -0,0 +1,151 @@ +/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 = 2 SECONDS + activators = list("incoming"= IC_PINTYPE_PULSE_IN,"outgoing" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 2 + +/obj/item/integrated_circuit/time/delay/do_work() + set waitfor = 0 // Don't sleep in a proc that is called by a processor. It'll delay the entire thing + + sleep(delay) + activate_pin(2) + +/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 = 5 SECONDS + 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 = 1 SECONDS + 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, 0, 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/ticks_to_pulse = 4 + var/ticks_completed = 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) + SSobj.processing -= src + . = ..() + +/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 + SSobj.processing |= src + else if(is_running) + is_running = FALSE + SSobj.processing -= src + ticks_completed = 0 + +/obj/item/integrated_circuit/time/ticker/process() + var/process_ticks = SSobj.priority //process_schedule_interval("obj") + ticks_completed += process_ticks + if(ticks_completed >= ticks_to_pulse) + if(ticks_to_pulse >= process_ticks) + ticks_completed -= ticks_to_pulse + else + ticks_completed = 0 + 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 + ticks_to_pulse = 2 + 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 + ticks_to_pulse = 6 + 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() + set_pin_data(IC_OUTPUT, 1, time2text(world.time, "hh:mm:ss") ) + set_pin_data(IC_OUTPUT, 2, text2num(time2text(world.time, "hh") ) ) + set_pin_data(IC_OUTPUT, 3, text2num(time2text(world.time, "mm") ) ) + set_pin_data(IC_OUTPUT, 4, text2num(time2text(world.time, "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 0000000000000..daadf52d42960 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/trig.dm @@ -0,0 +1,140 @@ +//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/integrated_electronics/~defines/~defines.dm b/code/modules/integrated_electronics/~defines/~defines.dm new file mode 100644 index 0000000000000..90aa05acf1938 --- /dev/null +++ b/code/modules/integrated_electronics/~defines/~defines.dm @@ -0,0 +1,36 @@ +#undef IC_INPUT +#undef IC_OUTPUT +#undef IC_ACTIVATOR + +#undef DATA_CHANNEL +#undef PULSE_CHANNEL + +#undef IC_SPAWN_DEFAULT +//#undef IC_SPAWN_RESEARCH // Research designs depend on this unfortunately. + +#undef IC_FORMAT_ANY +#undef IC_FORMAT_STRING +#undef IC_FORMAT_CHAR +#undef IC_FORMAT_COLOR +#undef IC_FORMAT_NUMBER +#undef IC_FORMAT_DIR +#undef IC_FORMAT_BOOLEAN +#undef IC_FORMAT_REF +#undef IC_FORMAT_LIST + +#undef IC_FORMAT_PULSE + +#undef IC_PINTYPE_ANY +#undef IC_PINTYPE_STRING +#undef IC_PINTYPE_CHAR +#undef IC_PINTYPE_COLOR +#undef IC_PINTYPE_NUMBER +#undef IC_PINTYPE_DIR +#undef IC_PINTYPE_BOOLEAN +#undef IC_PINTYPE_REF +#undef IC_PINTYPE_LIST + +#undef IC_PINTYPE_PULSE_IN +#undef IC_PINTYPE_PULSE_OUT + +#undef IC_MAX_LIST_LENGTH \ No newline at end of file diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index c3d4115924cf9..f5e71cee29ca5 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -230,6 +230,78 @@ user.update_inv_r_hand(0) return fired +// Similar to the above proc, but does not require a user, which is ideal for things like turrets. +/obj/item/weapon/gun/proc/Fire_userless(atom/target) + if(!target) + return + + if(world.time < semicd) + return + + var/shoot_time = (burst_size - 1)* fire_delay + semicd = world.time + shoot_time + + var/turf/targloc = get_turf(target) //cache this in case target gets deleted during shooting, e.g. if it was a securitron that got destroyed. + for(var/i in 1 to burst_size) + var/obj/projectile = process_chamber() + if(!projectile) + src.visible_message("*click*") + //handle_click_empty() + break + + if(istype(projectile, /obj/item/projectile)) + var/obj/item/projectile/P = projectile + + //Not supported, maybe later + /* + var/acc = burst_accuracy[min(i, burst_accuracy.len)] + var/disp = dispersion[min(i, dispersion.len)] + + P.accuracy = accuracy + acc + P.dispersion = disp + */ + P.firer = src.name + P.suppressed = suppressed + + P.original = target + P.fire() + + if(suppressed) + playsound(src, fire_sound, 10, 1) + else + playsound(src, fire_sound, 50, 1) + + //if(muzzle_flash) //Not supported, maybe later + //set_light(muzzle_flash) + update_icon() + + //process_accuracy(projectile, user, target, acc, disp) + + // if(pointblank) + // process_point_blank(projectile, user, target) + + // if(process_projectile(projectile, null, target, user.zone_sel.selecting, clickparams)) + // handle_post_fire(null, target, pointblank, reflex) + + // update_icon() + + if(i < burst_size) + sleep(fire_delay) + + if(!(target && target.loc)) + target = targloc + //pointblank = 0 + + //log_and_message_admins("Fired [src].") + + //admin_attack_log(usr, attacker_message="Fired [src]", admin_message="fired a gun ([src]) (MODE: [src.mode_name]) [reflex ? "by reflex" : "manually"].") + + //update timing + semicd = world.time + fire_delay + + //if(muzzle_flash) + //set_light(0) + /obj/item/weapon/gun/attack(mob/M as mob, mob/user) if (M == user && user.zone_sel.selecting == "mouth" && !mouthshoot && ishuman(user)) //Handle gun suicide diff --git a/code/modules/reagents/Chemistry-Reagents-Helpers.dm b/code/modules/reagents/Chemistry-Reagents-Helpers.dm new file mode 100644 index 0000000000000..917d9f4649cc8 --- /dev/null +++ b/code/modules/reagents/Chemistry-Reagents-Helpers.dm @@ -0,0 +1,17 @@ +/atom/movable/proc/can_be_injected_by(var/atom/injector) + if(!Adjacent(get_turf(injector))) + return FALSE + if(!reagents) + return FALSE + if(!reagents.get_free_space()) + return FALSE + return TRUE + +/obj/can_be_injected_by(var/atom/injector) + return is_open_container() && ..() + +/mob/living/can_be_injected_by(var/atom/injector) + return ..() && (can_inject(null, 0, "torso") || can_inject(null, 0, "groin")) + +/datum/reagents/proc/get_free_space() // Returns free space. + return maximum_volume - total_volume \ No newline at end of file diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index dff48a32bea2c..c13d7b0c3600a 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -32,6 +32,7 @@ other types of metals and chemistry for reagents). /datum/design //Datum for object designs, used in construction var/name = "Name" //Name of the created object. var/desc = "Desc" //Description of the created object. + var/item_name = null //An item name before it is modified by various name-modifying procs var/id = "id" //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements. //Reliability modifier of the device at it's starting point. var/reliability = 100 //Reliability of the device. @@ -41,6 +42,33 @@ other types of metals and chemistry for reagents). var/build_path = "" //The file path of the object that gets created var/list/category = null //Primarily used for Mech Fabricators, but can be used for anything +/datum/design/New() + ..() + item_name = name + AssembleDesignInfo() + +//These procs are used in subtypes for assigning names and descriptions dynamically +/datum/design/proc/AssembleDesignInfo() + AssembleDesignName() + AssembleDesignDesc() + return + +/datum/design/proc/AssembleDesignName() + if(!name && build_path) //Get name from build path if posible + var/atom/movable/A = build_path + name = initial(A.name) + item_name = name + return + +/datum/design/proc/AssembleDesignDesc() + if(!desc) //Try to make up a nice description if we don't have one + desc = "Allows for the construction of \a [item_name]." + return + +//Returns a new instance of the item for this design +//This is to allow additional initialization to be performed, including possibly additional contructor arguments. +/datum/design/proc/Fabricate(var/newloc, var/fabricator) + return new build_path(newloc) //A proc to calculate the reliability of a design based on tech levels and innate modifiers. //Input: A list of /datum/tech; Output: The new reliabilty. @@ -418,4 +446,66 @@ other types of metals and chemistry for reagents). build_type = PROTOLATHE materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_PLASMA = 1500, MAT_URANIUM = 200) build_path = /obj/item/weapon/weldingtool/experimental - category = list("Equipment") //fuck you tg \ No newline at end of file + category = list("Equipment") //fuck you tg + + +///////////////////////////////////////// +//////Integrated Circuits//////////////// +///////////////////////////////////////// + +/datum/design/item/wirer + name = "Custom wirer tool" + id = "wirer" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + materials = list(MAT_METAL = 500, MAT_GLASS = 250) + build_type = PROTOLATHE + build_path = /obj/item/device/integrated_electronics/wirer + category = list("Electronics") + +/datum/design/item/debugger + name = "Custom circuit debugger tool" + id = "debugger" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + materials = list(MAT_METAL = 500, MAT_GLASS = 250) + build_type = PROTOLATHE + build_path = /obj/item/device/integrated_electronics/debugger + category = list("Electronics") + +/datum/design/circuit/integrated_circuit/AssembleDesignName() + ..() + name = "Custom circuitry ([item_name])" + +/datum/design/circuit/integrated_circuit/AssembleDesignDesc() + if(!desc) + desc = "Allows for the construction of \a [name] custom circuit." + +/datum/design/circuit/integrated_circuit/arithmetic/AssembleDesignName() + ..() + name = "Custom circuitry \[Arithmetic\] ([item_name])" + +/datum/design/item/custom_circuit_printer + name = "Portable integrated circuit printer" + desc = "A portable(ish) printer for modular machines." + id = "ic_printer" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_DATA = 2) + materials = list(MAT_METAL = 10000) + build_type = PROTOLATHE + build_path = /obj/item/device/integrated_circuit_printer + category = list("Electronics") + +/datum/design/item/custom_circuit_printer_upgrade + name = "Integrated circuit printer upgrade - advanced designs" + desc = "Allows the integrated circuit printer to create advanced circuits" + id = "ic_printer_upgrade_adv" + req_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4) + materials = list(MAT_METAL = 2000) + build_type = PROTOLATHE + build_path = /obj/item/weapon/disk/integrated_circuit/upgrade/advanced + category = list("Electronics") + + +/datum/design/circuit/tcom/exonet_node + name = "exonet node" + id = "tcom-exonet_node" + req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4) + build_path = /obj/item/weapon/circuitboard/telecomms/exonet_node diff --git a/code/world.dm b/code/world.dm index c94d8b833310c..f890481862460 100644 --- a/code/world.dm +++ b/code/world.dm @@ -31,7 +31,8 @@ changelog_hash = md5('html/changelog.html') //used for telling if the changelog has changed recently make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once) - + initialize_integrated_circuits_list() + load_configuration() load_mode() load_motd() diff --git a/icons/obj/assemblies/electronic_setups.dmi b/icons/obj/assemblies/electronic_setups.dmi new file mode 100644 index 0000000000000..f18018c0aec04 Binary files /dev/null and b/icons/obj/assemblies/electronic_setups.dmi differ diff --git a/icons/obj/assemblies/electronic_tools.dmi b/icons/obj/assemblies/electronic_tools.dmi new file mode 100644 index 0000000000000..975f2fc9a8128 Binary files /dev/null and b/icons/obj/assemblies/electronic_tools.dmi differ diff --git a/icons/obj/electronic_assemblies.dmi b/icons/obj/electronic_assemblies.dmi new file mode 100644 index 0000000000000..5799d85866fd8 Binary files /dev/null and b/icons/obj/electronic_assemblies.dmi differ diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi index b416d54b9ef0c..10780cd356aa5 100644 Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ diff --git a/icons/obj/stationobjs.dmi b/icons/obj/stationobjs.dmi index f9a90f670fd56..c20a6d3113d42 100644 Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.dmi differ diff --git a/nano/templates/exonet_node.tmpl b/nano/templates/exonet_node.tmpl new file mode 100644 index 0000000000000..f9d81d4e92f98 --- /dev/null +++ b/nano/templates/exonet_node.tmpl @@ -0,0 +1,40 @@ + + +

Status

+
+
+ Power: +
+
+ {{:helper.link('On', 'power', {'toggle_power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', {'toggle_power' : 1}, data.on ? null : 'selected', data.on ? 'redButton' : null)}} +
+
+ +

Ports

+
+
+ Incoming PDA Messages: +
+
+ {{:helper.link('Open', 'check', {'toggle_PDA_port' : 1}, data.allowPDAs ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_PDA_port' : 1}, data.allowPDAs ? null : 'selected')}} +
+
+
+
+ Incoming Communicators: +
+
+ {{:helper.link('Open', 'check', {'toggle_communicator_port' : 1}, data.allowCommunicators ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_communicator_port' : 1}, data.allowCommunicators ? null : 'selected')}} +
+
+
+
+ Incoming Newscaster Content: +
+
+ {{:helper.link('Open', 'check', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? null : 'selected')}} +
+
diff --git a/sound/voice/medbot/mapple.ogg b/sound/voice/medbot/mapple.ogg new file mode 100644 index 0000000000000..21e26742ca135 Binary files /dev/null and b/sound/voice/medbot/mapple.ogg differ diff --git a/sound/voice/medbot/mcatch.ogg b/sound/voice/medbot/mcatch.ogg new file mode 100644 index 0000000000000..07b8aaab75c46 Binary files /dev/null and b/sound/voice/medbot/mcatch.ogg differ diff --git a/sound/voice/medbot/mcoming.ogg b/sound/voice/medbot/mcoming.ogg new file mode 100644 index 0000000000000..d3eb9e467f4b3 Binary files /dev/null and b/sound/voice/medbot/mcoming.ogg differ diff --git a/sound/voice/medbot/mdelicious.ogg b/sound/voice/medbot/mdelicious.ogg new file mode 100644 index 0000000000000..5158538580edf Binary files /dev/null and b/sound/voice/medbot/mdelicious.ogg differ diff --git a/sound/voice/medbot/mfeelbetter.ogg b/sound/voice/medbot/mfeelbetter.ogg new file mode 100644 index 0000000000000..fdbe57fd2e94a Binary files /dev/null and b/sound/voice/medbot/mfeelbetter.ogg differ diff --git a/sound/voice/medbot/mflies.ogg b/sound/voice/medbot/mflies.ogg new file mode 100644 index 0000000000000..831281ebbee0f Binary files /dev/null and b/sound/voice/medbot/mflies.ogg differ diff --git a/sound/voice/medbot/mhelp.ogg b/sound/voice/medbot/mhelp.ogg new file mode 100644 index 0000000000000..516d5db068d22 Binary files /dev/null and b/sound/voice/medbot/mhelp.ogg differ diff --git a/sound/voice/medbot/minjured.ogg b/sound/voice/medbot/minjured.ogg new file mode 100644 index 0000000000000..0e968b3980c76 Binary files /dev/null and b/sound/voice/medbot/minjured.ogg differ diff --git a/sound/voice/medbot/minsult.ogg b/sound/voice/medbot/minsult.ogg new file mode 100644 index 0000000000000..017292977a137 Binary files /dev/null and b/sound/voice/medbot/minsult.ogg differ diff --git a/sound/voice/medbot/mlive.ogg b/sound/voice/medbot/mlive.ogg new file mode 100644 index 0000000000000..ceb0dec9a348a Binary files /dev/null and b/sound/voice/medbot/mlive.ogg differ diff --git a/sound/voice/medbot/mlost.ogg b/sound/voice/medbot/mlost.ogg new file mode 100644 index 0000000000000..7b332ac346af9 Binary files /dev/null and b/sound/voice/medbot/mlost.ogg differ diff --git a/sound/voice/medbot/mno.ogg b/sound/voice/medbot/mno.ogg new file mode 100644 index 0000000000000..030e43a010937 Binary files /dev/null and b/sound/voice/medbot/mno.ogg differ diff --git a/sound/voice/medbot/mpatchedup.ogg b/sound/voice/medbot/mpatchedup.ogg new file mode 100644 index 0000000000000..1314f6ee47188 Binary files /dev/null and b/sound/voice/medbot/mpatchedup.ogg differ diff --git a/sound/voice/medbot/mradar.ogg b/sound/voice/medbot/mradar.ogg new file mode 100644 index 0000000000000..ad347a6d891c0 Binary files /dev/null and b/sound/voice/medbot/mradar.ogg differ diff --git a/sound/voice/medbot/msurgeon.ogg b/sound/voice/medbot/msurgeon.ogg new file mode 100644 index 0000000000000..a300ee57ef6db Binary files /dev/null and b/sound/voice/medbot/msurgeon.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 5359ed0bdcb6e..238666d94447d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -15,6 +15,7 @@ // BEGIN_INCLUDE #include "_maps\metastation.dm" #include "code\_compile_options.dm" +#include "code\_macros.dm" #include "code\hub.dm" #include "code\world.dm" #include "code\__DATASTRUCTURES\heap.dm" @@ -166,6 +167,7 @@ #include "code\datums\computerfiles.dm" #include "code\datums\datacore.dm" #include "code\datums\datumvars.dm" +#include "code\datums\EPv2.dm" #include "code\datums\gas_mixture.dm" #include "code\datums\hud.dm" #include "code\datums\martial.dm" @@ -178,6 +180,7 @@ #include "code\datums\spell.dm" #include "code\datums\supplypacks.dm" #include "code\datums\uplink_item.dm" +#include "code\datums\weakref.dm" #include "code\datums\diseases\_disease.dm" #include "code\datums\diseases\_MobProcs.dm" #include "code\datums\diseases\anxiety.dm" @@ -422,6 +425,7 @@ #include "code\game\machinery\deployable.dm" #include "code\game\machinery\door_control.dm" #include "code\game\machinery\doppler_array.dm" +#include "code\game\machinery\exonet_node.dm" #include "code\game\machinery\flasher.dm" #include "code\game\machinery\hologram.dm" #include "code\game\machinery\igniter.dm" @@ -720,6 +724,7 @@ #include "code\game\objects\items\weapons\implants\implant_storage.dm" #include "code\game\objects\items\weapons\implants\implantcase.dm" #include "code\game\objects\items\weapons\implants\implantchair.dm" +#include "code\game\objects\items\weapons\implants\implantcircuits.dm" #include "code\game\objects\items\weapons\implants\implanter.dm" #include "code\game\objects\items\weapons\implants\implantpad.dm" #include "code\game\objects\items\weapons\implants\implantuplink.dm" @@ -1132,6 +1137,42 @@ #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\_defines.dm" +#include "code\modules\integrated_electronics\coordinate.dm" +#include "code\modules\integrated_electronics\core\assemblies.dm" +#include "code\modules\integrated_electronics\core\detailer.dm" +#include "code\modules\integrated_electronics\core\device.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\printer.dm" +#include "code\modules\integrated_electronics\core\tools.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\built_in.dm" +#include "code\modules\integrated_electronics\subtypes\converters.dm" +#include "code\modules\integrated_electronics\subtypes\data_transfer.dm" +#include "code\modules\integrated_electronics\subtypes\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\integrated_electronics\~defines\~defines.dm" #include "code\modules\library\lib_items.dm" #include "code\modules\library\lib_machines.dm" #include "code\modules\library\lib_readme.dm" @@ -1520,6 +1561,7 @@ #include "code\modules\reagents\Chemistry-Holder.dm" #include "code\modules\reagents\Chemistry-Machinery.dm" #include "code\modules\reagents\Chemistry-Readme.dm" +#include "code\modules\reagents\Chemistry-Reagents-Helpers.dm" #include "code\modules\reagents\Chemistry-Reagents.dm" #include "code\modules\reagents\Chemistry-Recipes.dm" #include "code\modules\reagents\reagent_containers.dm"