diff --git a/citadel.dme b/citadel.dme index f4cc3716873..becf1edb937 100644 --- a/citadel.dme +++ b/citadel.dme @@ -197,10 +197,12 @@ #include "code\__HELPERS\filters.dm" #include "code\__HELPERS\game.dm" #include "code\__HELPERS\global_lists.dm" +#include "code\__HELPERS\heap.dm" #include "code\__HELPERS\icon_smoothing.dm" #include "code\__HELPERS\icons.dm" #include "code\__HELPERS\mobs.dm" #include "code\__HELPERS\names.dm" +#include "code\__HELPERS\path.dm" #include "code\__HELPERS\piping_colors_lists.dm" #include "code\__HELPERS\roundend.dm" #include "code\__HELPERS\sanitize_values.dm" @@ -345,7 +347,6 @@ #include "code\controllers\subsystem\bellies_vr.dm" #include "code\controllers\subsystem\character_setup.dm" #include "code\controllers\subsystem\chat.dm" -#include "code\controllers\subsystem\circuits.dm" #include "code\controllers\subsystem\dcs.dm" #include "code\controllers\subsystem\emergency_shuttle.dm" #include "code\controllers\subsystem\events.dm" @@ -399,9 +400,11 @@ #include "code\controllers\subsystem\persistence\bunker.dm" #include "code\controllers\subsystem\persistence\objects.dm" #include "code\controllers\subsystem\processing\chemistry.dm" +#include "code\controllers\subsystem\processing\circuits.dm" #include "code\controllers\subsystem\processing\fastprocess.dm" #include "code\controllers\subsystem\processing\instruments.dm" #include "code\controllers\subsystem\processing\obj.dm" +#include "code\controllers\subsystem\processing\pathfinder.dm" #include "code\controllers\subsystem\processing\processing.dm" #include "code\controllers\subsystem\processing\projectiles.dm" #include "code\controllers\subsystem\processing\status_effects.dm" @@ -2347,6 +2350,7 @@ #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\saved_circuits.dm" #include "code\modules\integrated_electronics\core\tools.dm" #include "code\modules\integrated_electronics\core\assemblies\clothing.dm" #include "code\modules\integrated_electronics\core\assemblies\device.dm" @@ -2356,9 +2360,11 @@ #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\index_pin.dm" +#include "code\modules\integrated_electronics\core\special_pins\lists_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\selfref_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" @@ -3527,6 +3533,7 @@ #include "code\modules\reagents\Chemistry-Reagents.dm" #include "code\modules\reagents\Chemistry-Recipes.dm" #include "code\modules\reagents\Drink-Recipes.dm" +#include "code\modules\reagents\exposedprocs.dm" #include "code\modules\reagents\Food-Recipes.dm" #include "code\modules\reagents\Misc-Recipes.dm" #include "code\modules\reagents\reagent_containers.dm" diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index e08dccd578d..5d065028e15 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -1,4 +1,4 @@ -///MARK ALL FLAG CHANGES IN _globals/bitfields.dm! +///MARK ALL FLAG CHANGES IN _globalvars/bitfields.dm! ///All flags should go in here if possible. /// For convenience. #define ALL (~0) diff --git a/code/__DEFINES/_lists.dm b/code/__DEFINES/_lists.dm index a33b3b1197d..08d2c33c3f5 100644 --- a/code/__DEFINES/_lists.dm +++ b/code/__DEFINES/_lists.dm @@ -33,6 +33,7 @@ #define reverseList(L) reverseRange(L.Copy()) #define SAFEPICK(L) (length(L)? pick(L) : null) +#define SAFEFIND(L, S) (length(L)? (L.Find(S)) : null) #define SAFEACCESS(L, I) (isnum(I)? (SAFEINDEXACCESS(L, I)) : ((I in L)? L[I] : null)) #define SAFEINDEXACCESS(L, I) (ISINRANGE(I, 1, length(L))? L[I] : null) // Returns the key based on the index diff --git a/code/__DEFINES/chemistry.dm b/code/__DEFINES/chemistry.dm index 24b58a83257..62c8956d9e9 100644 --- a/code/__DEFINES/chemistry.dm +++ b/code/__DEFINES/chemistry.dm @@ -84,6 +84,11 @@ var/list/cheartstopper = list("potassium_chloride") // Thi ///Max length of a custom pill/condiment/whatever #define MAX_CUSTOM_NAME_LEN 64 +///Syringe +#define SYRINGE_DRAW 0 +#define SYRINGE_INJECT 1 +#define SYRINGE_BROKEN 2 + //reagents_holder_flags defines ///Makes it possible to add reagents through droppers and syringes. #define INJECTABLE (1<<0) diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index 8e2ece86edd..c18782dfaf1 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -154,6 +154,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define INIT_ORDER_AI -22 #define INIT_ORDER_OPENSPACE -50 #define INIT_ORDER_PERSISTENCE -95 +#define INIT_ORDER_PATH -98 #define INIT_ORDER_ICON_SMOOTHING -99 ///Should be last to ensure chat remains smooth during init. #define INIT_ORDER_CHAT -100 diff --git a/code/__DEFINES/inventory/slots.dm b/code/__DEFINES/inventory/slots.dm index 570cb8a10c3..db4bf3e06c9 100644 --- a/code/__DEFINES/inventory/slots.dm +++ b/code/__DEFINES/inventory/slots.dm @@ -1,6 +1,8 @@ /// Takes 40ds = 4s to strip someone. -#define HUMAN_STRIP_DELAY 40 +#define HUMAN_STRIP_DELAY 4 SECONDS +/// Takes 2s to use a UI element in somebody else's hands. +#define HUMAN_INTERACT_DELAY 2 SECONDS // Item inventory slot bitmasks. #define SLOT_OCLOTHING 0x1 diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index f9df86cde9c..2dbd55d636f 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -3,6 +3,7 @@ #define INVESTIGATE_ATMOS "atmos" #define INVESTIGATE_BOTANY "botany" #define INVESTIGATE_CARGO "cargo" +#define INVESTIGATE_CIRCUIT "circuit" #define INVESTIGATE_CRAFTING "crafting" #define INVESTIGATE_EXONET "exonet" #define INVESTIGATE_EXPERIMENTOR "experimentor" diff --git a/code/__HELPERS/atom_movables.dm b/code/__HELPERS/atom_movables.dm index b838f868e64..d63569fae2e 100644 --- a/code/__HELPERS/atom_movables.dm +++ b/code/__HELPERS/atom_movables.dm @@ -41,3 +41,41 @@ if(!include_own_turf) turfs -= get_turf(src) src.throw_at(pick(turfs), maxrange, speed, src) + +//same as do_mob except for movables and it allows both to drift and doesn't draw progressbar +/proc/do_atom(atom/movable/user , atom/movable/target, time = 30, uninterruptible = 0,datum/callback/extra_checks = null) + if(!user || !target) + return TRUE + var/user_loc = user.loc + + var/drifting = FALSE + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = TRUE + + var/target_drifting = FALSE + if(!target.Process_Spacemove(0) && target.inertia_dir) + target_drifting = TRUE + + var/target_loc = target.loc + + var/endtime = world.time+time + . = TRUE + while (world.time < endtime) + stoplag(1) + if(QDELETED(user) || QDELETED(target)) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = FALSE + user_loc = user.loc + + if(target_drifting && !target.inertia_dir) + target_drifting = FALSE + target_loc = target.loc + + if((!drifting && user.loc != user_loc) || (!target_drifting && target.loc != target_loc) || (extra_checks && !extra_checks.Invoke())) + . = FALSE + break diff --git a/code/__HELPERS/heap.dm b/code/__HELPERS/heap.dm new file mode 100644 index 00000000000..eabcb0e0dc7 --- /dev/null +++ b/code/__HELPERS/heap.dm @@ -0,0 +1,81 @@ +////////////////////// +//datum/heap object +////////////////////// + +/datum/heap + var/list/L + var/cmp + +/datum/heap/New(compare) + L = new() + cmp = compare + +/datum/heap/Destroy(force, ...) + for(var/i in L) // because this is before the list helpers are loaded + qdel(i) + L = null + return ..() + +/datum/heap/proc/is_empty() + return !length(L) + +//insert and place at its position a new node in the heap +/datum/heap/proc/insert(atom/A) + + L.Add(A) + swim(length(L)) + +//removes and returns the first element of the heap +//(i.e the max or the min dependant on the comparison function) +/datum/heap/proc/pop() + if(!length(L)) + return 0 + . = L[1] + + L[1] = L[length(L)] + L.Cut(length(L)) + if(length(L)) + sink(1) + + +//Get a node up to its right position in the heap +/datum/heap/proc/swim(index) + var/parent = round(index * 0.5) + + while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0)) + L.Swap(index,parent) + index = parent + parent = round(index * 0.5) + +//Get a node down to its right position in the heap +/datum/heap/proc/sink(index) + var/g_child = get_greater_child(index) + + while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0)) + L.Swap(index,g_child) + index = g_child + g_child = get_greater_child(index) + +//Returns the greater (relative to the comparison proc) of a node children +//or 0 if there's no child +/datum/heap/proc/get_greater_child(index) + if(index * 2 > length(L)) + return 0 + + if(index * 2 + 1 > length(L)) + return index * 2 + + if(call(cmp)(L[index * 2],L[index * 2 + 1]) < 0) + return index * 2 + 1 + else + return index * 2 + +//Replaces a given node so it verify the heap condition +/datum/heap/proc/resort(atom/A) + var/index = L.Find(A) + + swim(index) + sink(index) + +/datum/heap/proc/List() + . = L.Copy() diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index f7641dad1fb..04368b5752a 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -217,7 +217,7 @@ Proc for attack log creation, because really why not if (progbar) qdel(progbar) -/proc/do_after(mob/user, delay, atom/target = null, needhand = TRUE, progress = TRUE, incapacitation_flags = INCAPACITATION_DEFAULT, ignore_movement = FALSE, max_distance = null) +/proc/do_after(mob/user, delay, atom/target = null, needhand = TRUE, progress = TRUE, incapacitation_flags = INCAPACITATION_DEFAULT, ignore_movement = FALSE, max_distance = null, ignore_resist = FALSE) if(!user) return 0 if(!delay) @@ -230,7 +230,7 @@ Proc for attack log creation, because really why not var/obj/mecha/M = null - if(istype(user.loc, /obj/mecha)) + if(ismecha(user.loc)) original_loc = get_turf(original_loc) M = user.loc diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm new file mode 100644 index 00000000000..72ed8da8190 --- /dev/null +++ b/code/__HELPERS/path.dm @@ -0,0 +1,381 @@ +/** + * This file contains the stuff you need for using JPS (Jump Point Search) pathing, an alternative to A* that skips + * over large numbers of uninteresting tiles resulting in much quicker pathfinding solutions. Mind that diagonals + * cost the same as cardinal moves currently, so paths may look a bit strange, but should still be optimal. + */ + +/** + * This is the proc you use whenever you want to have pathfinding more complex than "try stepping towards the thing". + * If no path was found, returns an empty list, which is important for bots like medibots who expect an empty list rather than nothing. + * + * Arguments: + * * caller: The movable atom that's trying to find the path + * * end: What we're trying to path to. It doesn't matter if this is a turf or some other atom, we're gonna just path to the turf it's on anyway + * * max_distance: The maximum number of steps we can take in a given path to search (default: 30, 0 = infinite) + * * mintargetdistance: Minimum distance to the target before path returns, could be used to get near a target, but not right to it - for an AI mob with a gun, for example. + * * id: An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant + * * simulated_only: Whether we consider turfs without atmos simulation (AKA do we want to ignore space) + * * exclude: If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf + * * skip_first: Whether or not to delete the first item in the path. This would be done because the first item is the starting tile, which can break movement for some creatures. + */ +/proc/get_path_to(caller, end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude, skip_first=TRUE) + if(!caller || !get_turf(end)) + return + + var/l = SSpathfinder.mobs.getfree(caller) + while(!l) + stoplag(3) + l = SSpathfinder.mobs.getfree(caller) + + var/list/path + var/datum/pathfind/pathfind_datum = new(caller, end, id, max_distance, mintargetdist, simulated_only, exclude) + path = pathfind_datum.search() + qdel(pathfind_datum) + + SSpathfinder.mobs.found(l) + if(!path) + path = list() + if(length(path) > 0 && skip_first) + path.Cut(1,2) + return path + +/** + * A helper macro to see if it's possible to step from the first turf into the second one, minding things like door access and directional windows. + * Note that this can only be used inside the [datum/pathfind][pathfind datum] since it uses variables from said datum. + * If you really want to optimize things, optimize this, cuz this gets called a lot. + */ +#define CAN_STEP(cur_turf, next) (next && !next.density && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && !cur_turf.LinkBlockedWithAccess(next,caller, id) && (next != avoid)) +/// Another helper macro for JPS, for telling when a node has forced neighbors that need expanding +#define STEP_NOT_HERE_BUT_THERE(cur_turf, dirA, dirB) ((!CAN_STEP(cur_turf, get_step(cur_turf, dirA)) && CAN_STEP(cur_turf, get_step(cur_turf, dirB)))) + +/// The JPS Node datum represents a turf that we find interesting enough to add to the open list and possibly search for new tiles from +/datum/jps_node + /// The turf associated with this node + var/turf/tile + /// The node we just came from + var/datum/jps_node/previous_node + /// The A* node weight (f_value = number_of_tiles + heuristic) + var/f_value + /// The A* node heuristic (a rough estimate of how far we are from the goal) + var/heuristic + /// How many steps it's taken to get here from the start (currently pulling double duty as steps taken & cost to get here, since all moves incl diagonals cost 1 rn) + var/number_tiles + /// How many steps it took to get here from the last node + var/jumps + /// Nodes store the endgoal so they can process their heuristic without a reference to the pathfind datum + var/turf/node_goal + +/datum/jps_node/New(turf/our_tile, datum/jps_node/incoming_previous_node, jumps_taken, turf/incoming_goal) + tile = our_tile + jumps = jumps_taken + if(incoming_goal) // if we have the goal argument, this must be the first/starting node + node_goal = incoming_goal + else if(incoming_previous_node) // if we have the parent, this is from a direct lateral/diagonal scan, we can fill it all out now + previous_node = incoming_previous_node + number_tiles = previous_node.number_tiles + jumps + node_goal = previous_node.node_goal + heuristic = get_dist(tile, node_goal) + f_value = number_tiles + heuristic + // otherwise, no parent node means this is from a subscan lateral scan, so we just need the tile for now until we call [datum/jps/proc/update_parent] on it + +/datum/jps_node/Destroy(force, ...) + previous_node = null + return ..() + +/datum/jps_node/proc/update_parent(datum/jps_node/new_parent) + previous_node = new_parent + node_goal = previous_node.node_goal + jumps = get_dist(tile, previous_node.tile) + number_tiles = previous_node.number_tiles + jumps + heuristic = get_dist(tile, node_goal) + f_value = number_tiles + heuristic + +/// TODO: Macro this to reduce proc overhead +/proc/HeapPathWeightCompare(datum/jps_node/a, datum/jps_node/b) + return b.f_value - a.f_value + +/// The datum used to handle the JPS pathfinding, completely self-contained +/datum/pathfind + /// The thing that we're actually trying to path for + var/atom/movable/caller + /// The turf where we started at + var/turf/start + /// The turf we're trying to path to (note that this won't track a moving target) + var/turf/end + /// The open list/stack we pop nodes out from (TODO: make this a normal list and macro-ize the heap operations to reduce proc overhead) + var/datum/heap/open + ///An assoc list that serves as the closed list & tracks what turfs came from where. Key is the turf, and the value is what turf it came from + var/list/sources + /// The list we compile at the end if successful to pass back + var/list/path + + // general pathfinding vars/args + /// An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant + var/obj/item/card/id/id + /// How far away we have to get to the end target before we can call it quits + var/mintargetdist = 0 + /// I don't know what this does vs , but they limit how far we can search before giving up on a path + var/max_distance = 30 + /// Space is big and empty, if this is TRUE then we ignore pathing through unsimulated tiles + var/simulated_only + /// A specific turf we're avoiding, like if a mulebot is being blocked by someone t-posing in a doorway we're trying to get through + var/turf/avoid + +/datum/pathfind/New(atom/movable/caller, atom/goal, id, max_distance, mintargetdist, simulated_only, avoid) + src.caller = caller + end = get_turf(goal) + open = new /datum/heap(/proc/HeapPathWeightCompare) + sources = new() + src.id = id + src.max_distance = max_distance + src.mintargetdist = mintargetdist + src.simulated_only = simulated_only + src.avoid = avoid + +/** + * search() is the proc you call to kick off and handle the actual pathfinding, and kills the pathfind datum instance when it's done. + * + * If a valid path was found, it's returned as a list. If invalid or cross-z-level params are entered, or if there's no valid path found, we + * return null, which [/proc/get_path_to] translates to an empty list (notable for simple bots, who need empty lists) + */ +/datum/pathfind/proc/search() + start = get_turf(caller) + if(!start || !end) + stack_trace("Invalid A* start or destination") + return + if(start.z != end.z || start == end ) //no pathfinding between z levels + return + if(max_distance && (max_distance < get_dist(start, end))) //if start turf is farther than max_distance from end turf, no need to do anything + return + + //initialization + var/datum/jps_node/current_processed_node = new (start, -1, 0, end) + open.insert(current_processed_node) + sources[start] = start // i'm sure this is fine + + //then run the main loop + while(!open.is_empty() && !path) + if(!caller) + return + current_processed_node = open.pop() //get the lower f_value turf in the open list + if(max_distance && (current_processed_node.number_tiles > max_distance))//if too many steps, don't process that path + continue + + var/turf/current_turf = current_processed_node.tile + for(var/scan_direction in list(EAST, WEST, NORTH, SOUTH)) + lateral_scan_spec(current_turf, scan_direction, current_processed_node) + + for(var/scan_direction in list(NORTHEAST, SOUTHEAST, NORTHWEST, SOUTHWEST)) + diag_scan_spec(current_turf, scan_direction, current_processed_node) + + CHECK_TICK + + //we're done! reverse the path to get it from start to finish + if(path) + for(var/i = 1 to round(0.5 * length(path))) + path.Swap(i, length(path) - i + 1) + + sources = null + qdel(open) + return path + +/// Called when we've hit the goal with the node that represents the last tile, then sets the path var to that path so it can be returned by [datum/pathfind/proc/search] +/datum/pathfind/proc/unwind_path(datum/jps_node/unwind_node) + path = new() + var/turf/iter_turf = unwind_node.tile + path.Add(iter_turf) + + while(unwind_node.previous_node) + var/dir_goal = get_dir(iter_turf, unwind_node.previous_node.tile) + for(var/i = 1 to unwind_node.jumps) + iter_turf = get_step(iter_turf,dir_goal) + path.Add(iter_turf) + unwind_node = unwind_node.previous_node + +/** + * For performing lateral scans from a given starting turf. + * + * These scans are called from both the main search loop, as well as subscans for diagonal scans, and they treat finding interesting turfs slightly differently. + * If we're doing a normal lateral scan, we already have a parent node supplied, so we just create the new node and immediately insert it into the heap, ezpz. + * If we're part of a subscan, we still need for the diagonal scan to generate a parent node, so we return a node datum with just the turf and let the diag scan + * proc handle transferring the values and inserting them into the heap. + * + * Arguments: + * * original_turf: What turf did we start this scan at? + * * heading: What direction are we going in? Obviously, should be cardinal + * * parent_node: Only given for normal lateral scans, if we don't have one, we're a diagonal subscan. +*/ +/datum/pathfind/proc/lateral_scan_spec(turf/original_turf, heading, datum/jps_node/parent_node) + var/steps_taken = 0 + + var/turf/current_turf = original_turf + var/turf/lag_turf = original_turf + + while(TRUE) + if(path) + return + lag_turf = current_turf + current_turf = get_step(current_turf, heading) + steps_taken++ + if(!CAN_STEP(lag_turf, current_turf)) + return + + if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist))) + var/datum/jps_node/final_node = new(current_turf, parent_node, steps_taken) + sources[current_turf] = original_turf + if(parent_node) // if this is a direct lateral scan we can wrap up, if it's a subscan from a diag, we need to let the diag make their node first, then finish + unwind_path(final_node) + return final_node + else if(sources[current_turf]) // already visited, essentially in the closed list + return + else + sources[current_turf] = original_turf + + if(parent_node && parent_node.number_tiles + steps_taken > max_distance) + return + + var/interesting = FALSE // have we found a forced neighbor that would make us add this turf to the open list? + + switch(heading) + if(NORTH) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST)) + interesting = TRUE + if(SOUTH) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST)) + interesting = TRUE + if(EAST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST)) + interesting = TRUE + if(WEST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST)) + interesting = TRUE + + if(interesting) + var/datum/jps_node/newnode = new(current_turf, parent_node, steps_taken) + if(parent_node) // if we're a diagonal subscan, we'll handle adding ourselves to the heap in the diag + open.insert(newnode) + return newnode + +/** + * For performing diagonal scans from a given starting turf. + * + * Unlike lateral scans, these only are called from the main search loop, so we don't need to worry about returning anything, + * though we do need to handle the return values of our lateral subscans of course. + * + * Arguments: + * * original_turf: What turf did we start this scan at? + * * heading: What direction are we going in? Obviously, should be diagonal + * * parent_node: We should always have a parent node for diagonals +*/ +/datum/pathfind/proc/diag_scan_spec(turf/original_turf, heading, datum/jps_node/parent_node) + var/steps_taken = 0 + var/turf/current_turf = original_turf + var/turf/lag_turf = original_turf + + while(TRUE) + if(path) + return + lag_turf = current_turf + current_turf = get_step(current_turf, heading) + steps_taken++ + if(!CAN_STEP(lag_turf, current_turf)) + return + + if(current_turf == end || (mintargetdist && (get_dist(current_turf, end) <= mintargetdist))) + var/datum/jps_node/final_node = new(current_turf, parent_node, steps_taken) + sources[current_turf] = original_turf + unwind_path(final_node) + return + else if(sources[current_turf]) // already visited, essentially in the closed list + return + else + sources[current_turf] = original_turf + + if(parent_node.number_tiles + steps_taken > max_distance) + return + + var/interesting = FALSE // have we found a forced neighbor that would make us add this turf to the open list? + var/datum/jps_node/possible_child_node // otherwise, did one of our lateral subscans turn up something? + + switch(heading) + if(NORTHWEST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, WEST) || lateral_scan_spec(current_turf, NORTH)) + if(NORTHEAST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, EAST) || lateral_scan_spec(current_turf, NORTH)) + if(SOUTHWEST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, SOUTH) || lateral_scan_spec(current_turf, WEST)) + if(SOUTHEAST) + if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST)) + interesting = TRUE + else + possible_child_node = (lateral_scan_spec(current_turf, SOUTH) || lateral_scan_spec(current_turf, EAST)) + + if(interesting || possible_child_node) + var/datum/jps_node/newnode = new(current_turf, parent_node, steps_taken) + open.insert(newnode) + if(possible_child_node) + possible_child_node.update_parent(newnode) + open.insert(possible_child_node) + if(possible_child_node.tile == end || (mintargetdist && (get_dist(possible_child_node.tile, end) <= mintargetdist))) + unwind_path(possible_child_node) + return + +/** + * For seeing if we can actually move between 2 given turfs while accounting for our access and the caller's pass_flags + * + * Arguments: + * * caller: The movable, if one exists, being used for mobility checks to see what tiles it can reach + * * ID: An ID card that decides if we can gain access to doors that would otherwise block a turf + * * simulated_only: Do we only worry about turfs with simulated atmos, most notably things that aren't space? +*/ +/turf/proc/LinkBlockedWithAccess(turf/destination_turf, caller, ID) + if(destination_turf.x != x && destination_turf.y != y) //diagonal + var/in_dir = get_dir(destination_turf,src) // eg. northwest (1+8) = 9 (00001001) + var/first_step_direction_a = in_dir & 3 // eg. north (1+8)&3 (0000 0011) = 1 (0000 0001) + var/first_step_direction_b = in_dir & 12 // eg. west (1+8)&12 (0000 1100) = 8 (0000 1000) + + for(var/first_step_direction in list(first_step_direction_a,first_step_direction_b)) + var/turf/midstep_turf = get_step(destination_turf,first_step_direction) + var/way_blocked = midstep_turf.density || LinkBlockedWithAccess(midstep_turf,caller,ID) || midstep_turf.LinkBlockedWithAccess(destination_turf,caller,ID) + if(!way_blocked) + return FALSE + return TRUE + + var/actual_dir = get_dir(src, destination_turf) + + // Source border object checks + for(var/obj/structure/window/iter_window in src) + if(!iter_window.CanAStarPass(ID, actual_dir)) + return TRUE + + for(var/obj/machinery/door/window/iter_windoor in src) + if(!iter_windoor.CanAStarPass(ID, actual_dir)) + return TRUE + + for(var/obj/structure/railing/iter_rail in src) + if(!iter_rail.CanAStarPass(ID, actual_dir)) + return TRUE + + for(var/obj/machinery/door/firedoor/border_only/firedoor in src) + if(!firedoor.CanAStarPass(ID, actual_dir)) + return TRUE + + // Destination blockers check + var/reverse_dir = get_dir(destination_turf, src) + for(var/obj/iter_object in destination_turf) + if(!iter_object.CanAStarPass(ID, reverse_dir, caller)) + return TRUE + + return FALSE + +#undef CAN_STEP +#undef STEP_NOT_HERE_BUT_THERE diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index fd9da43921d..3ee28dee2f0 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -360,6 +360,14 @@ new_text += copytext(text, i, i+1) return new_text +/proc/text2charlist(text) + var/char = "" + var/lentext = length(text) + . = list() + for(var/i = 1, i <= lentext, i += length(char)) + char = text[i] + . += char + //Used in preferences' SetFlavorText and human's set_flavor verb //Previews a string of len or less length proc/TextPreview(var/string,var/len=40) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 63b414ea946..5118363fee4 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -289,6 +289,31 @@ /proc/url2htmlloader(url) return {""} +// Converts a string into ascii then converts it into hexadecimal. +/proc/strtohex(str) + if(!istext(str)||!str) + return + var/r + var/c + for(var/i = 1 to length(str)) + c= text2ascii(str,i) + r+= num2hex(c) + return r + +// Decodes hexadecimal ascii into a raw byte string. +// If safe=TRUE, returns null on incorrect input strings instead of CRASHing +/proc/hextostr(str, safe=FALSE) + if(!istext(str)||!str) + return + var/r + var/c + for(var/i = 1 to length(str)/2) + c = hex2num(copytext(str,i*2-1,i*2+1), safe) + if(isnull(c)) + return null + r += ascii2text(c) + return r + //This is a weird one: //It returns a list of all var names found in the string diff --git a/code/controllers/subsystem/circuits.dm b/code/controllers/subsystem/processing/circuits.dm similarity index 87% rename from code/controllers/subsystem/circuits.dm rename to code/controllers/subsystem/processing/circuits.dm index 5ea8b5e1113..3c33186952d 100644 --- a/code/controllers/subsystem/circuits.dm +++ b/code/controllers/subsystem/processing/circuits.dm @@ -2,10 +2,13 @@ // This is for custom circuits, mostly the initialization of global properties about them. // Might make this also process them in the future if its better to do that than using the obj SSticker. // -SUBSYSTEM_DEF(circuit) +PROCESSING_SUBSYSTEM_DEF(circuit) name = "Circuit" init_order = INIT_ORDER_CIRCUIT subsystem_flags = SS_NO_FIRE + + var/cipherkey + var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs var/list/cached_components = list() // Associative list of [component_path]:[component] pairs var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs @@ -14,14 +17,15 @@ SUBSYSTEM_DEF(circuit) var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs // var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3 -/datum/controller/subsystem/circuit/Recover() +/datum/controller/subsystem/processing/circuit/Recover() subsystem_flags |= SS_NO_INIT // Make extra sure we don't initialize twice. -/datum/controller/subsystem/circuit/Initialize(timeofday) +/datum/controller/subsystem/processing/circuit/Initialize(timeofday) + SScircuit.cipherkey = uppertext(random_string(2000+rand(0,10), GLOB.alphabet)) circuits_init() return ..() -/datum/controller/subsystem/circuit/proc/circuits_init() +/datum/controller/subsystem/processing/circuit/proc/circuits_init() //Cached lists for free performance for(var/path in typesof(/obj/item/integrated_circuit)) var/obj/item/integrated_circuit/IC = path @@ -46,23 +50,26 @@ SUBSYSTEM_DEF(circuit) circuit_fabricator_recipe_list["Assemblies"] = list( + /obj/item/electronic_assembly/tiny/default, + /obj/item/electronic_assembly/tiny/cylinder, + /obj/item/electronic_assembly/tiny/scanner, + /obj/item/electronic_assembly/tiny/hook, + /obj/item/electronic_assembly/tiny/box, + /obj/item/electronic_assembly/tiny/dildo, /obj/item/electronic_assembly/default, /obj/item/electronic_assembly/calc, /obj/item/electronic_assembly/clam, /obj/item/electronic_assembly/simple, /obj/item/electronic_assembly/hook, /obj/item/electronic_assembly/pda, - /obj/item/electronic_assembly/tiny/default, - /obj/item/electronic_assembly/tiny/cylinder, - /obj/item/electronic_assembly/tiny/scanner, - /obj/item/electronic_assembly/tiny/hook, - /obj/item/electronic_assembly/tiny/box, + /obj/item/electronic_assembly/dildo, /obj/item/electronic_assembly/medium/default, /obj/item/electronic_assembly/medium/box, /obj/item/electronic_assembly/medium/clam, /obj/item/electronic_assembly/medium/medical, /obj/item/electronic_assembly/medium/gun, /obj/item/electronic_assembly/medium/radio, + /obj/item/electronic_assembly/medium/dildo, /obj/item/electronic_assembly/large/default, /obj/item/electronic_assembly/large/scope, /obj/item/electronic_assembly/large/terminal, @@ -94,5 +101,7 @@ SUBSYSTEM_DEF(circuit) circuit_fabricator_recipe_list["Tools"] = list( /obj/item/integrated_electronics/wirer, /obj/item/integrated_electronics/debugger, - /obj/item/integrated_electronics/detailer + /obj/item/integrated_electronics/analyzer, + /obj/item/integrated_electronics/detailer, + /obj/item/card/data ) diff --git a/code/controllers/subsystem/processing/pathfinder.dm b/code/controllers/subsystem/processing/pathfinder.dm new file mode 100644 index 00000000000..ed9ef0b3b78 --- /dev/null +++ b/code/controllers/subsystem/processing/pathfinder.dm @@ -0,0 +1,48 @@ +SUBSYSTEM_DEF(pathfinder) + name = "Pathfinder" + init_order = INIT_ORDER_PATH + subsystem_flags = SS_NO_FIRE + var/datum/flowcache/mobs + var/datum/flowcache/circuits + var/static/space_type_cache + +/datum/controller/subsystem/pathfinder/Initialize() + space_type_cache = typecacheof(/turf/space) + mobs = new(10) + circuits = new(3) + return ..() + +/datum/flowcache + var/lcount + var/run + var/free + var/list/flow + +/datum/flowcache/New(n) + . = ..() + lcount = n + run = 0 + free = 1 + flow = new/list(lcount) + +/datum/flowcache/proc/getfree(atom/M) + if(run < lcount) + run += 1 + while(flow[free]) + CHECK_TICK + free = (free % lcount) + 1 + var/t = addtimer(CALLBACK(src, /datum/flowcache.proc/toolong, free), 150, TIMER_STOPPABLE) + flow[free] = t + flow[t] = M + return free + else + return 0 + +/datum/flowcache/proc/toolong(l) + log_game("Pathfinder route took longer than 150 ticks, src bot [flow[flow[l]]]") + found(l) + +/datum/flowcache/proc/found(l) + deltimer(flow[l]) + flow[l] = null + run -= 1 diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 97f073e3a3c..814f44980c4 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1350,6 +1350,10 @@ About the new airlock wires panel: return 0 return ..(M) +// Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off) +/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + return !density || (check_access(ID) && !locked && inoperable()) + /obj/machinery/door/airlock/Initialize(mapload, obj/structure/door_assembly/assembly) //if assembly is given, create the new door from the assembly if (assembly && istype(assembly)) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 7c6c1750cca..e63896ccdfd 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -501,6 +501,10 @@ return 1 */ +// For prosperity, in case border doors get reimplemented. +/obj/machinery/door/firedoor/border_only/CanAStarPass(obj/item/card/id/ID, to_dir) + return !density || (dir != to_dir) + /obj/machinery/door/firedoor/multi_tile icon = 'icons/obj/doors/DoorHazard2x1.dmi' width = 2 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 8de6c59531e..907e1bbf5df 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -99,6 +99,10 @@ return ATMOS_PASS_NOT_BLOCKED return density? ATMOS_PASS_AIR_BLOCKED : ATMOS_PASS_ZONE_BLOCKED +//used in the AStar algorithm to determinate if the turf the door is on is passable +/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir) + return !density || (dir != to_dir) || (check_access(ID) && inoperable()) + /obj/machinery/door/window/CheckExit(atom/movable/mover as mob|obj, turf/target as turf) if(istype(mover) && mover.checkpass(PASSGLASS)) return 1 diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index c0dff5da495..b344e1dec73 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -125,6 +125,24 @@ /obj/proc/hides_under_flooring() return 0 + /** + * This proc is used for telling whether something can pass by this object in a given direction, for use by the pathfinding system. + * + * Trying to generate one long path across the station will call this proc on every single object on every single tile that we're seeing if we can move through, likely + * multiple times per tile since we're likely checking if we can access said tile from multiple directions, so keep these as lightweight as possible. + * + * Arguments: + * * ID- An ID card representing what access we have (and thus if we can open things like airlocks or windows to pass through them). The ID card's physical location does not matter, just the reference + * * to_dir- What direction we're trying to move in, relevant for things like directional windows that only block movement in certain directions + * * caller- The movable we're checking pass flags for, if we're making any such checks + **/ +/obj/proc/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + if(ismovable(caller)) + var/atom/movable/AM = caller + if(AM.checkpass(pass_flags)) + return TRUE + . = !density + /obj/proc/hear_talk(mob/M as mob, text, verb, datum/language/speaking) if(talking_atom) talking_atom.catchMessage(text, M) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 42174c18b14..dabdacff04b 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -292,6 +292,11 @@ girder_material.place_dismantled_product(get_turf(src), 2) qdel(src) +/obj/structure/girder/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + . = !density + if(istype(caller)) + . = . || (caller.pass_flags & PASSGRILLE) + /obj/structure/girder/attack_hand(mob/user as mob) if (HULK in user.mutations) visible_message("[user] smashes [src] apart!") diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index 8e37c381635..f3deaebbc62 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -29,6 +29,19 @@ else return +/obj/structure/plasticflaps/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + if(isliving(caller)) + if(isbot(caller)) + return TRUE + + var/mob/living/living_caller = caller + if(!living_caller.can_ventcrawl() && living_caller.mob_size > MOB_TINY) + return FALSE + + if(caller?.pulling) + return CanAStarPass(ID, to_dir, caller.pulling) + return TRUE //diseases, stings, etc can pass + /obj/structure/plasticflaps/CanAllowThrough(atom/A, turf/T) if(istype(A) && A.checkpass(PASSGLASS)) return prob(60) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 21f2358254c..385233820be 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -389,6 +389,15 @@ setDir(turn(dir, 270)) updateSilicate() +//! Does this work? idk. Let's call it TBI. +/obj/structure/window/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + if(!density) + return TRUE + if((is_fulltile()) || (dir == to_dir)) + return FALSE + + return TRUE + /obj/structure/window/Initialize(mapload, start_dir, constructed = FALSE) . = ..(mapload) if (start_dir) diff --git a/code/modules/integrated_electronics/_defines.dm b/code/modules/integrated_electronics/_defines.dm index 80b740d45ab..fa9d87ffc9f 100644 --- a/code/modules/integrated_electronics/_defines.dm +++ b/code/modules/integrated_electronics/_defines.dm @@ -1,3 +1,13 @@ +// Base cost modifiers. + +#define IC_COMPONENTS_BASE 25 +#define IC_COMPLEXITY_BASE 75 +#define IC_MATERIAL_MODIFIER 1 // TBI when materials get updated. +/// Calculates the cost of a given assembly. +#define IC_ASSEMBLY_COST(ea) (round((ea.max_components + ea.max_complexity) / 4)) + +// Base pin types. + #define IC_INPUT "input" #define IC_OUTPUT "output" #define IC_ACTIVATOR "activator" @@ -6,6 +16,12 @@ #define DATA_CHANNEL "data channel" #define PULSE_CHANNEL "pulse channel" +// Categories that help differentiate circuits that can do different types of actions. + +#define IC_ACTION_MOVEMENT (1<<0) // If the circuit can move the assembly +#define IC_ACTION_COMBAT (1<<1) // If the circuit can cause harm +#define IC_ACTION_LONG_RANGE (1<<2) // If the circuit communicate with something outside of the assembly + // Displayed along with the pin name to show what type of pin it is. #define IC_FORMAT_ANY "\" #define IC_FORMAT_STRING "\" @@ -16,8 +32,12 @@ #define IC_FORMAT_BOOLEAN "\" #define IC_FORMAT_REF "\" #define IC_FORMAT_LIST "\" +#define IC_FORMAT_INDEX "\" +#define IC_FORMAT_SELFREF "\" #define IC_FORMAT_PULSE "\" +#define IC_FORMAT_INPUT "\" +#define IC_FORMAT_OUTPUT "\" // Used inside input/output list to tell the constructor what pin to make. #define IC_PINTYPE_ANY /datum/integrated_io @@ -28,42 +48,26 @@ #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_LIST /datum/integrated_io/lists +#define IC_PINTYPE_INDEX /datum/integrated_io/index +#define IC_PINTYPE_SELFREF /datum/integrated_io/selfref #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 +// Circuit limiters. + +#define IC_MAX_LIST_LENGTH 500 +#define IC_DEFAULT_COOLDOWN 1 +#define IC_SMOKE_REAGENTS_MINIMUM_UNITS 10 + +// Printer limits. + +#define MAX_CIRCUIT_CLONE_TIME 3 MINUTES //circuit slow-clones can only take up this amount of time to complete var/list/all_integrated_circuits = list() +//Mostly deprecated, currently only used for circuit bags in integrated_electronics\core\tools.dm /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/integrated_electronics/electronic_components.dmi' - icon_state = "template" - w_class = ITEMSIZE_TINY - var/obj/item/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 SECOND // 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/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index d65dae8af6a..35b3f85e0ad 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -1,49 +1,148 @@ -#define IC_COMPONENTS_BASE 20 -#define IC_COMPLEXITY_BASE 60 - // Here is where the base definition lives. // Specific subtypes are in their own folder. /obj/item/electronic_assembly name = "electronic assembly" + obj_flags = CAN_BE_HIT desc = "It's a case, for building small electronics with." w_class = ITEMSIZE_SMALL icon = 'icons/obj/integrated_electronics/electronic_setups.dmi' icon_state = "setup_small" + item_flags = NOBLUDGEON show_messages = TRUE + datum_flags = DF_USE_TAG + var/list/assembly_components = list() + var/list/ui_circuit_props = list() + /// Players who built the circuit can scan it as a ghost. + var/list/ckeys_allowed_to_scan = list() var/max_components = IC_COMPONENTS_BASE var/max_complexity = IC_COMPLEXITY_BASE var/opened = FALSE - /// If true, wrenching it will anchor it. - var/can_anchor = FALSE /// Internal cell which most circuits need to work. var/obj/item/cell/device/battery = null /// Set every tick, to display how much power is being drawn in total. var/net_power = 0 + /// Can it be charged in a recharger? + var/can_charge = TRUE + /// Can it fire/throw weapons when the assembly is being held? + var/can_fire_equipped = FALSE + var/charge_sections = 4 + var/charge_tick = FALSE + var/charge_delay = 4 + var/use_cyborg_cell = TRUE + /// Time until circuit cn perform another external action + var/ext_next_use = 0 + var/atom/collw + var/obj/item/card/id/access_card + /// Which circuit flags are allowed + var/allowed_circuit_action_flags = IC_ACTION_COMBAT | IC_ACTION_LONG_RANGE + /// Number of combat cicuits in the assembly, used for diagnostic hud + var/combat_circuits = 0 + /// Number of long range cicuits in the assembly, used for diagnostic hud + var/long_range_circuits = 0 + /// Used by the AR circuit to change the hud icon. + var/prefered_hud_icon = "hudstat" + /// Circuit creator ckey if any + var/creator + var/static/next_assembly_id = 0 +// TBI var/hud_possible = list(DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD, DIAG_CIRCUIT_HUD) //diagnostic hud overlays + /// If true, wrenching it will anchor it. + var/can_anchor = TRUE + /// Holds a ref to the circuit which is currently bolting the assembly, if applicable. + var/anchored_by = null + /// Incremented by panel locking circuits. + var/panel_locked = null var/detail_color = COLOR_ASSEMBLY_BLACK + /// Cost set to default during init if unset. + var/cost = 0 +/obj/item/electronic_assembly/GenerateTag() + tag = "assembly_[next_assembly_id++]" + +/obj/item/electronic_assembly/examine(mob/user) + . = ..() + if(can_anchor) + . += "The anchoring bolts [anchored ? "are" : "can be"] wrenched in place and the maintenance panel [opened ? "can be" : "is"] screwed in place." + else + . += "The maintenance panel [opened ? "can be" : "is"] screwed in place." + + // Not using internal examine? Why? TBI? Will test how horrible/spammy it is. + for(var/I in assembly_components) + var/obj/item/integrated_circuit/IC = I + var/text = IC.external_examine(user) + if(text) + . += text + + if(Adjacent(user)) + var/datum/tgui/ui = SStgui.get_open_ui(src.loc, src) + if(ui) + ui_interact(user, ui, ui) + else ui_interact(user) + + if(isobserver(user)) + if(isobserver(user) && ckeys_allowed_to_scan[user.ckey] || IsAdminGhost(user)) + . += "You can scan this circuit." + ui_interact(user) + +/obj/item/electronic_assembly/Bump(atom/AM) + collw = AM + .=..() + if((istype(collw, /obj/machinery/door/airlock) || istype(collw, /obj/machinery/door/window)) && (!isnull(access_card))) + var/obj/machinery/door/D = collw + if(D.check_access(access_card)) + D.open() /obj/item/electronic_assembly/Initialize(mapload) battery = new(src) + src.max_components = round(max_components) + src.max_complexity = round(max_complexity) + if(!cost) + cost = IC_ASSEMBLY_COST(src) START_PROCESSING(SSobj, src) - return ..() +/* TBI //sets up diagnostic hud view + prepare_huds() + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) + diag_hud.add_to_hud(src) + diag_hud_set_circuithealth() + diag_hud_set_circuitcell() + diag_hud_set_circuitstat() + diag_hud_set_circuittracking() +*/ + access_card = new /obj/item/card/id(src) + . =..() /obj/item/electronic_assembly/Destroy() battery = null // It will be qdel'd by ..() if still in our contents STOP_PROCESSING(SSobj, src) +// for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) +// TBI diag_hud.remove_from_hud(src) + QDEL_NULL(access_card) return ..() /obj/item/electronic_assembly/process(delta_time) handle_idle_power() - + check_pulling() +/* TBI diag hud + //updates diagnostic hud + diag_hud_set_circuithealth() + diag_hud_set_circuitcell() +*/ /obj/item/electronic_assembly/proc/handle_idle_power() net_power = 0 // Reset this. This gets increased/decreased with [give/draw]_power() outside of this loop. - // First we handle passive sources. Most of these make power so they go first. - for(var/obj/item/integrated_circuit/passive/power/P in contents) - P.handle_passive_energy() + // First we generate power. + for(var/obj/item/integrated_circuit/passive/power/P in assembly_components) + P.make_energy() - // Now we handle idle power draw. - for(var/obj/item/integrated_circuit/IC in contents) + // Now spend it. + for(var/I in assembly_components) + var/obj/item/integrated_circuit/IC = I + /* Uncomment for debugging purposes. */ + if(!IC) + to_world(SPAN_DEBUGERROR("Bad assembly_components entry in [src]. Has remove() been called incorrectly?")) + var/x = assembly_components.Find(null) + assembly_components.Cut(x,++x) + return + //*/ if(IC.power_draw_idle) if(!draw_power(IC.power_draw_idle)) IC.power_fail() @@ -69,10 +168,11 @@ var/total_parts = 0 var/total_complexity = 0 - for(var/obj/item/integrated_circuit/part in contents) + for(var/obj/item/integrated_circuit/part in assembly_components) total_parts += part.size total_complexity = total_complexity + part.complexity + data["opened"] = opened data["total_parts"] = total_parts data["max_components"] = max_components data["total_complexity"] = total_complexity @@ -81,21 +181,27 @@ data["battery_charge"] = round(battery?.charge, 0.1) data["battery_max"] = round(battery?.maxcharge, 0.1) data["net_power"] = DYNAMIC_CELL_UNITS_TO_W(net_power, 1) - + data["circuit_props"] = ui_circuit_props // This works because lists are always passed by reference in BYOND, so modifying unremovable_circuits // after setting data["unremovable_circuits"] = unremovable_circuits also modifies data["unremovable_circuits"] // Same for the removable one - var/list/unremovable_circuits = list() - data["unremovable_circuits"] = unremovable_circuits - var/list/removable_circuits = list() - data["removable_circuits"] = removable_circuits - for(var/obj/item/integrated_circuit/circuit in contents) + //var/list/unremovable_circuits = list() + //data["unremovable_circuits"] = unremovable_circuits + //var/list/removable_circuits = list() + //data["removable_circuits"] = removable_circuits + //var/list/available_inputs = list() + //data["available_inputs"] = available_inputs + /* + for(var/obj/item/integrated_circuit/circuit in assembly_components) var/list/target = circuit.removable ? removable_circuits : unremovable_circuits - target.Add(list(list( - "name" = circuit.displayed_name, - "ref" = REF(circuit), - ))) - + target. + if(istype(circuit, /obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/input = circuit + if(input.can_be_asked_input) + available_inputs.Add(list(list( + "name" = circuit.displayed_name, + "ref" = REF(circuit), + )))*/ return data /obj/item/electronic_assembly/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) @@ -103,7 +209,6 @@ return TRUE var/obj/held_item = usr.get_active_hand() - switch(action) //Actual assembly actions if("rename") @@ -111,9 +216,6 @@ return TRUE if("remove_cell") - if(!battery) - to_chat(usr, SPAN_WARNING("There's no power cell to remove from \the [src].")) - return FALSE var/turf/T = get_turf(src) battery.forceMove(T) playsound(T, 'sound/items/Crowbar.ogg', 50, TRUE) @@ -123,21 +225,21 @@ // Circuit actions if("open_circuit") - var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents + var/obj/item/integrated_circuit/C = locate(params["ref"]) in assembly_components if(!istype(C)) return C.ui_interact(usr, null, ui) return TRUE if("rename_circuit") - var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents + var/obj/item/integrated_circuit/C = locate(params["ref"]) in assembly_components if(!istype(C)) return C.rename_component(usr) return TRUE if("scan_circuit") - var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents + var/obj/item/integrated_circuit/C = locate(params["ref"]) in assembly_components if(!istype(C)) return if(istype(held_item, /obj/item/integrated_electronics/debugger)) @@ -151,37 +253,73 @@ return TRUE if("remove_circuit") - var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents + var/obj/item/integrated_circuit/C = locate(params["ref"]) in assembly_components if(!istype(C)) return - C.remove(usr) + C.remove(usr, TRUE, index = params["index"]) return TRUE if("bottom_circuit") - var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents + var/obj/item/integrated_circuit/C = locate(params["ref"]) in assembly_components if(!istype(C)) return // Puts it at the bottom of our contents // Note, this intentionally does *not* use forceMove, because forceMove will stop if it detects the same loc + ui_circuit_props.Cut(params["index"], 1 + params["index"]) + ui_circuit_props.Add(list(list("name" = C.displayed_name,"ref" = REF(C),"removable" = C.removable,"input" = C.can_be_asked_input))) + assembly_components.Cut(params["index"], 1 + params["index"]) + assembly_components.Add(C) C.loc = null C.loc = src - return FALSE -// End TGUI + if("input_selection") + var/obj/item/integrated_circuit/input/C = locate(params["ref"]) in assembly_components + if(C.ask_for_input(usr,held_item,usr.a_intent)) + return TRUE + else + var/obj/item/integrated_circuit/D = C + D.attackby_react(usr,held_item,usr.a_intent) + return TRUE + + return FALSE + +// Input window data. + +// End TGUI +/* TBI diag hud +/obj/item/electronic_assembly/pickup(mob/living/user) + . = ..() + //update diagnostic hud when picked up, true is used to force the hud to be hidden + diag_hud_set_circuithealth(TRUE) + diag_hud_set_circuitcell(TRUE) + diag_hud_set_circuitstat(TRUE) + diag_hud_set_circuittracking(TRUE) + +/obj/item/electronic_assembly/dropped(mob/user) + . = ..() + //update diagnostic hud when dropped + diag_hud_set_circuithealth() + diag_hud_set_circuitcell() + diag_hud_set_circuitstat() + diag_hud_set_circuittracking() + +*/ /obj/item/electronic_assembly/verb/rename() set name = "Rename Circuit" set category = "Object" set desc = "Rename your circuit, useful to stay organized." var/mob/M = usr + var/input = tgui_input_text(usr, "What do you want to name this?", "Rename", src.name, MAX_NAME_LEN) if(!check_interactivity(M)) return - - var/input = sanitizeSafe(input(usr, "What do you want to name this?", "Rename", src.name) as null|text, MAX_NAME_LEN) if(src && input) to_chat(M, SPAN_NOTICE("The machine now has a label reading '[input]'.")) name = input +/obj/item/electronic_assembly/proc/add_allowed_scanner(ckey) + ckeys_allowed_to_scan[ckey] = TRUE + /obj/item/electronic_assembly/proc/can_move() return FALSE @@ -210,26 +348,18 @@ if(id_card) return id_card -/obj/item/electronic_assembly/examine(mob/user) - . = ..() - if(Adjacent(user)) - for(var/obj/item/integrated_circuit/IC in contents) - . += IC.external_examine(user) - if(opened) - ui_interact(user) - -/obj/item/electronic_assembly/proc/get_part_complexity() +/obj/item/electronic_assembly/proc/return_total_complexity() . = 0 - for(var/obj/item/integrated_circuit/part in contents) + for(var/obj/item/integrated_circuit/part in assembly_components) . += part.complexity -/obj/item/electronic_assembly/proc/get_part_size() +/obj/item/electronic_assembly/proc/return_total_size() . = 0 - for(var/obj/item/integrated_circuit/part in contents) + for(var/obj/item/integrated_circuit/part in assembly_components) . += part.size // Returns true if the circuit made it inside. -/obj/item/electronic_assembly/proc/add_circuit(var/obj/item/integrated_circuit/IC, var/mob/user) +/obj/item/electronic_assembly/proc/try_add_component(var/obj/item/integrated_circuit/IC, var/mob/user) if(!opened) to_chat(user, SPAN_WARNING("\The [src] isn't opened, so you can't put anything inside. Try using a crowbar.")) return FALSE @@ -238,8 +368,8 @@ to_chat(user, SPAN_WARNING("\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() + var/total_part_size = return_total_size() + var/total_complexity = return_total_complexity() if((total_part_size + IC.size) > max_components) to_chat(user, SPAN_WARNING("You can't seem to add the '[IC.name]', as there's insufficient space.")) @@ -247,32 +377,50 @@ if((total_complexity + IC.complexity) > max_complexity) to_chat(user, SPAN_WARNING("You can't seem to add the '[IC.name]', since this setup's too complicated for the case.")) return FALSE - + if((allowed_circuit_action_flags & IC.action_flags) != IC.action_flags) + to_chat(user, SPAN_WARNING("You can't seem to add the '[IC.name]', since the case doesn't support the circuit type.")) + return FALSE if(!IC.forceMove(src)) return FALSE - IC.assembly = src + to_chat(user, SPAN_NOTICE("You slide [IC] inside [src].")) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + add_allowed_scanner(user.ckey) + investigate_log("had [IC]([IC.type]) inserted by [key_name(user)].", INVESTIGATE_CIRCUIT) + add_component(IC) return TRUE -// Non-interactive version of above that always succeeds, intended for build-in circuits that get added on assembly initialization. -/obj/item/electronic_assembly/proc/force_add_circuit(var/obj/item/integrated_circuit/IC) - IC.forceMove(src) +// Actually puts the circuit inside, doesn't perform any checks. +/obj/item/electronic_assembly/proc/add_component(var/obj/item/integrated_circuit/IC) + IC.forceMove(get_object()) IC.assembly = src + // Build TGUI lists here for efficiency. We don't need to do that every time the UI updates. + ui_circuit_props.Add(list(list("name" = IC.displayed_name,"ref" = REF(IC),"removable" = IC.removable,"input" = IC.can_be_asked_input))) + assembly_components |= IC + /* TBI diag hud + //increment numbers for diagnostic hud + if(component.action_flags & IC_ACTION_COMBAT) + combat_circuits += 1; + if(component.action_flags & IC_ACTION_LONG_RANGE) + long_range_circuits += 1; + + //diagnostic hud update + diag_hud_set_circuitstat() + diag_hud_set_circuittracking() + */ /obj/item/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) + . = ..() + for(var/obj/item/integrated_circuit/input/S in assembly_components) + if(S.sense(target,user,proximity)) visible_message(SPAN_NOTICE("\The [user] waves \the [src] around [target].")) -/obj/item/electronic_assembly/attackby(var/obj/item/I, var/mob/user) +/obj/item/electronic_assembly/attackby(var/obj/item/I, var/mob/user, intent) if(can_anchor && I.is_wrench()) + if(anchored_by) + to_chat(user, SPAN_WARNING(pick("You fail to get purchase on [anchored_by]'s bolts.","[src]'s [anchored_by] protests!","The bolts defeat your paltry [user.get_species_name()] attempts to loosen them."))) + return anchored = !anchored to_chat(user, SPAN_NOTICE("You've [anchored ? "" : "un"]secured \the [src] to \the [get_turf(src)].")) if(anchored) @@ -285,9 +433,7 @@ else if(istype(I, /obj/item/integrated_circuit)) if(!user.unEquip(I) && !istype(user, /mob/living/silicon/robot)) //Robots cannot de-equip items in grippers. return FALSE - if(add_circuit(I, user)) - to_chat(user, SPAN_NOTICE("You slide \the [I] inside \the [src].")) - playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) + if(try_add_component(I, user)) ui_interact(user) return TRUE @@ -304,6 +450,9 @@ return TRUE else if(I.is_screwdriver()) + if(panel_locked) + to_chat(user, SPAN_NOTICE("The screws are hidden.")) + return FALSE playsound(get_turf(src), 'sound/items/Screwdriver.ogg', 50, 1) opened = !opened to_chat(user, SPAN_NOTICE("You [opened ? "opened" : "closed"] \the [src].")) @@ -314,60 +463,44 @@ if(opened) ui_interact(user) else - to_chat(user, SPAN_WARNING("\The [src] isn't opened, so you can't fiddle with the internal components. Try using a crowbar.")) + to_chat(user, SPAN_WARNING("\The [src] isn't opened, so you can't fiddle with the internal components. Try using a screwdriver.")) + return TRUE else if(istype(I, /obj/item/integrated_electronics/detailer)) var/obj/item/integrated_electronics/detailer/D = I detail_color = D.detail_color update_icon() + return TRUE else if(istype(I, /obj/item/cell/device)) if(!opened) to_chat(user, SPAN_WARNING("\The [src] isn't opened, so you can't put anything inside. Try using a crowbar.")) + for(var/obj/item/integrated_circuit/input/S in assembly_components) + S.attackby_react(I,user,user.a_intent) return FALSE if(battery) to_chat(user, SPAN_WARNING("\The [src] already has \a [battery] inside. Remove it first if you want to replace it.")) + for(var/obj/item/integrated_circuit/input/S in assembly_components) + S.attackby_react(I,user,user.a_intent) return FALSE var/obj/item/cell/device/cell = I user.drop_item(cell) cell.forceMove(src) battery = cell + // TBI diag_hud_set_circuitstat() //update diagnostic hud playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) to_chat(user, SPAN_NOTICE("You slot \the [cell] inside \the [src]'s power supplier.")) ui_interact(user) return TRUE - else - return ..() + else for(var/obj/item/integrated_circuit/S in assembly_components) + if(S.attackby_react(I,user,user.a_intent)) + return TRUE + return ..() /obj/item/electronic_assembly/attack_self(mob/user) if(!check_interactivity(user)) return - if(opened) - ui_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 = tgui_input_list(user, "What do you want to interact with?", "Interaction", input_selection) - if(selection) - var/index = input_selection.Find(selection) - choice = available_inputs[index] - - if(choice) - choice.ask_for_input(user) + ui_interact(user) /obj/item/electronic_assembly/attack_robot(mob/user as mob) if(Adjacent(user)) @@ -376,7 +509,9 @@ return ..() /obj/item/electronic_assembly/emp_act(severity) - ..() + . = ..() +// if(. & EMP_PROTECT_CONTENTS) +// return for(var/atom/movable/AM in contents) AM.emp_act(severity) @@ -414,14 +549,57 @@ /obj/item/electronic_assembly/on_loc_moved(oldloc) . = ..() - for(var/obj/O in contents) - O.on_loc_moved(oldloc) + for(var/obj/item/integrated_circuit/IC in assembly_components) + IC.ext_moved(oldloc, dir) + if(light) //Update lighting objects (From light circuits). + update_light() -/obj/item/electronic_assembly/Moved(var/oldloc) +/obj/item/electronic_assembly/Moved(oldloc, dir) . = ..() - for(var/obj/O in contents) - O.on_loc_moved(oldloc) + for(var/obj/item/integrated_circuit/IC in assembly_components) + IC.ext_moved(oldloc, dir) + if(light) //Update lighting objects (From light circuits). + update_light() +/obj/item/electronic_assembly/stop_pulling() + for(var/I in assembly_components) + var/obj/item/integrated_circuit/IC = I + IC.stop_pulling() + ..() + + +// Returns the object that is supposed to be used in attack messages, location checks, etc. +// Override in children for special behavior. +/obj/item/electronic_assembly/proc/get_object() + return src + +// Returns the location to be used for dropping items. +// Same as the regular drop_location(), but with checks being run on acting_object if necessary. +/obj/item/integrated_circuit/drop_location() + var/atom/movable/acting_object = get_object() + + // plz no infinite loops + if(acting_object == src) + return ..() + + return acting_object.drop_location() + +/obj/item/electronic_assembly/attack_tk(mob/user) + if(anchored) + return + ..() + +/obj/item/electronic_assembly/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) + if(anchored) + attack_self(user) + return + ..() +/* TBI: Hand recog, can_trigger_gun +/obj/item/electronic_assembly/can_trigger_gun(mob/living/user) //sanity checks against pocket death weapon circuits + if(!can_fire_equipped || !user.is_holding(src)) + return FALSE + return ..() +*/ /obj/item/electronic_assembly/proc/on_anchored() for(var/obj/item/integrated_circuit/IC in contents) IC.on_anchored() diff --git a/code/modules/integrated_electronics/core/assemblies/clothing.dm b/code/modules/integrated_electronics/core/assemblies/clothing.dm index e9379fda662..33ab9a5fa8c 100644 --- a/code/modules/integrated_electronics/core/assemblies/clothing.dm +++ b/code/modules/integrated_electronics/core/assemblies/clothing.dm @@ -1,6 +1,6 @@ -// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities -// E.g. Glasses have less room than something worn over the chest. +// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities +// E.g. Glasses have less room than something worn over the chest. // Note that the electronic assembly is INSIDE the object that actually gets worn, in a similar way to implants. /obj/item/electronic_assembly/clothing @@ -15,6 +15,9 @@ /obj/item/electronic_assembly/clothing/ui_host() return clothing.ui_host() +/obj/item/electronic_assembly/clothing/ui_action_click() + clothing.action_circuit.do_work() + /obj/item/electronic_assembly/clothing/update_icon() ..() clothing.icon_state = icon_state @@ -35,65 +38,57 @@ // This is defined higher up, in /clothing to avoid lots of copypasta. /obj/item/clothing - var/obj/item/electronic_assembly/clothing/IC = null + var/obj/item/electronic_assembly/clothing/EA = null var/obj/item/integrated_circuit/built_in/action_button/action_circuit = null // This gets pulsed when someone clicks the button on the hud. /obj/item/clothing/emp_act(severity) - if(IC) - IC.emp_act(severity) + if(EA) + EA.emp_act(severity) ..() /obj/item/clothing/examine(mob/user) - if(IC) - IC.examine(user) + if(EA) + EA.examine(user) . = ..() /obj/item/clothing/attackby(obj/item/I, mob/user) - if(IC) - // This needs to be done in a better way... - if(I.is_crowbar() || I.is_screwdriver() || istype(I, /obj/item/integrated_circuit) || istype(I, /obj/item/cell/device) || istype(I, /obj/item/integrated_electronics) ) - IC.attackby(I, user) - else - ..() + if(EA) + return EA.attackby(I, user) ? null : ..() + ..() /obj/item/clothing/attack_self(mob/user) - if(IC) - if(IC.opened) - IC.attack_self(user) - else - action_circuit.do_work() + if(EA && EA.opened) + EA.attack_self(user) else ..() /obj/item/clothing/Moved(oldloc) - if(IC) - IC.on_loc_moved(oldloc) - else - ..() + EA ? EA.on_loc_moved(oldloc) : ..() /obj/item/clothing/on_loc_moved(oldloc) - if(IC) - IC.on_loc_moved(oldloc) - else - ..() + EA ? EA.on_loc_moved(oldloc) : ..() // Does most of the repeatative setup. /obj/item/clothing/proc/setup_integrated_circuit(new_type) // Set up the internal circuit holder. - IC = new new_type(src) - IC.clothing = src - IC.name = name + EA = new new_type(src) + EA.clothing = src + EA.name = name + + // Clothing assemblies can be triggered by clicking on the HUD. This allows that to occur. + action_circuit = new(src.EA) + EA.add_component(action_circuit) + var/obj/item/integrated_circuit/built_in/self_sensor/S = new(src.EA) + EA.add_component(S) + EA.action_button_name = "Activate [name]" + - // Clothing assemblies can be triggered by clicking on the HUD. This allows that to occur. - action_circuit = new(src.IC) - IC.force_add_circuit(action_circuit) - action_button_name = "Activate [name]" /obj/item/clothing/Destroy() - if(IC) - IC.clothing = null + if(EA) + EA.clothing = null action_circuit = null // Will get deleted by qdel-ing the IC assembly. - qdel(IC) + qdel(EA) return ..() // Specific subtypes. @@ -101,7 +96,7 @@ // Jumpsuit. /obj/item/clothing/under/circuitry name = "electronic jumpsuit" - desc = "It's a wearable case for electronics. This on is a black jumpsuit with wiring weaved into the fabric." + desc = "It's a wearable case for electronics. This one is a black jumpsuit with wiring woven into the fabric." icon_state = "circuitry" worn_state = "circuitry" @@ -113,7 +108,7 @@ // Gloves. /obj/item/clothing/gloves/circuitry name = "electronic gloves" - desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \ + desc = "A wearable case for electronics comprising a pair of black gloves, with wires woven through the fabric. A small \ device with a screen is attached to the left glove." icon_state = "circuitry" item_state = "circuitry" @@ -123,22 +118,21 @@ return ..() // Watch. -/obj/item/clothing/gloves/ewatch +/obj/item/clothing/gloves/ewatch/circuitry name = "electronic watch" - desc = "It's a wearable case for electronics. This one is a digital watch, with an antenna and button array attatched to it.\ + desc = "A wearable case for electronics; a digital watch with an antenna and button array attatched to it.\ Practical and stylish!" icon_state = "communicator" item_state = "ewatch" -/obj/item/clothing/gloves/circuitry/Initialize(mapload) +/obj/item/clothing/gloves/ewatch/circuitry/Initialize(mapload) setup_integrated_circuit(/obj/item/electronic_assembly/clothing/small) return ..() // Glasses. /obj/item/clothing/glasses/circuitry name = "electronic goggles" - desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \ - Could this augment your vision?" // Sadly it won't, or at least not yet. + desc = "A wearable case for electronics; a pair of goggles, with exposed wiring. Could this augment your vision?" // Sadly it won't, or at least not yet. icon_state = "circuitry" item_state = "night" // The on-mob sprite would be identical anyways. @@ -149,8 +143,7 @@ // Shoes /obj/item/clothing/shoes/circuitry name = "electronic boots" - desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \ - cover." + desc = "A wearable case for electronics comprising a pair of boots with sleek maintenance hatches on the inside leg." icon_state = "circuitry" item_state = "circuitry" @@ -161,8 +154,7 @@ // Head /obj/item/clothing/head/circuitry name = "electronic headwear" - desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \ - goes around the collar, with a heads-up-display attached on the right." + desc = "A a very technical-looking wearable case for electronics that clasps around the collar with a heads-up-display attached on the right." icon_state = "circuitry" item_state = "circuitry" @@ -173,7 +165,7 @@ // Ear /obj/item/clothing/ears/circuitry name = "electronic earwear" - desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset." + desc = "A wearable case for electronics, bulkier than your average headset." icon = 'icons/obj/clothing/ears.dmi' icon_state = "circuitry" item_state = "circuitry" @@ -185,8 +177,7 @@ // Exo-slot /obj/item/clothing/suit/circuitry name = "electronic chestpiece" - desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \ - almost looks professionally made, however the wiring popping out betrays that idea." + desc = "A wearable case for electronics that sits over the chest and back. The sheer bulk of it gives it an imposing presence." icon_state = "circuitry" item_state = "circuitry" diff --git a/code/modules/integrated_electronics/core/assemblies/device.dm b/code/modules/integrated_electronics/core/assemblies/device.dm index d66ea88c71b..6c11aaf69c9 100644 --- a/code/modules/integrated_electronics/core/assemblies/device.dm +++ b/code/modules/integrated_electronics/core/assemblies/device.dm @@ -1,6 +1,6 @@ /obj/item/assembly/electronic_assembly name = "electronic device" - desc = "It's a case for building electronics with. It can be attached to other small devices." + desc = "It's a case for building electronics with. It can be attached to other small devices." icon_state = "setup_device" var/opened = 0 @@ -45,14 +45,14 @@ /obj/item/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) + for(var/obj/item/integrated_circuit/built_in/device_input/I in EA.assembly_components) I.do_work() return /obj/item/assembly/electronic_assembly/examine(mob/user) . = ..() if(EA) - for(var/obj/item/integrated_circuit/IC in EA.contents) + for(var/obj/item/integrated_circuit/IC in EA.assembly_components) IC.external_examine(user) /obj/item/assembly/electronic_assembly/verb/toggle() @@ -73,7 +73,6 @@ max_components = IC_COMPONENTS_BASE * 3/4 max_complexity = IC_COMPLEXITY_BASE * 3/4 - /obj/item/electronic_assembly/device/Initialize(mapload) . = ..() var/obj/item/integrated_circuit/built_in/device_input/input = new(src) diff --git a/code/modules/integrated_electronics/core/assemblies/generic.dm b/code/modules/integrated_electronics/core/assemblies/generic.dm index 8f071f621b7..aa03e2cfea2 100644 --- a/code/modules/integrated_electronics/core/assemblies/generic.dm +++ b/code/modules/integrated_electronics/core/assemblies/generic.dm @@ -1,35 +1,5 @@ // Generic subtypes without a lot of special code. -// Small assemblies. - -/obj/item/electronic_assembly/default - name = "type-a electronic assembly" - -/obj/item/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/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/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/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/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." - // Tiny assemblies. /obj/item/electronic_assembly/tiny @@ -46,22 +16,62 @@ /obj/item/electronic_assembly/tiny/cylinder name = "type-b electronic device" icon_state = "setup_device_cylinder" - desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." + desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." /obj/item/electronic_assembly/tiny/scanner name = "type-c electronic device" icon_state = "setup_device_scanner" - desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." + desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." /obj/item/electronic_assembly/tiny/hook name = "type-d electronic device" icon_state = "setup_device_hook" - desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." + desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." /obj/item/electronic_assembly/tiny/box name = "type-e electronic device" icon_state = "setup_device_box" - desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." + desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." + +/obj/item/electronic_assembly/tiny/dildo + name = "type-f electronic device" + icon_state = "setup_dildo_small" + desc = "It's a case, for building tiny-sized electronics with. This one has a phallic design." + +// Small assemblies. + +/obj/item/electronic_assembly/default + name = "type-a electronic assembly" + +/obj/item/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/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/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/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/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/electronic_assembly/dildo + name = "type-g electronic assembly" + icon_state = "setup_dildo_medium" + desc = "It's a case, for building small electronics with. This one has a phallic design." // Medium assemblies. @@ -79,25 +89,25 @@ /obj/item/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." + desc = "It's a case, for building medium-sized electronics with. This one has a boxy design." /obj/item/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." + desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design." /obj/item/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." + desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus." /obj/item/electronic_assembly/medium/gun name = "type-e electronic mechanism" icon_state = "setup_medium_gun" item_state = "circuitgun" - desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, \ + desc = "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." -// can_fire_equipped = TRUE + can_fire_equipped = TRUE item_icons = list( slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', @@ -106,7 +116,12 @@ /obj/item/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." + desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio." + +/obj/item/electronic_assembly/medium/dildo + name = "type-g electronic mechanism" + icon_state = "setup_dildo_large" + desc = "It's a case, for building medium-sized electronics with. This one has a phallic design." // Large assemblies. @@ -125,32 +140,32 @@ /obj/item/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." + desc = "It's a case, for building large electronics with. This one resembles an oscilloscope." /obj/item/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." + desc = "It's a case, for building large electronics with. This one resembles a computer terminal." /obj/item/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." + desc = "It's a case, for building large electronics with. This one resembles a robotic arm." /obj/item/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." + desc = "It's a case, for building large electronics with. This one has a tall design." /obj/item/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." + desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery." /obj/item/electronic_assembly/large/vendor name = "type-g electronic machine" icon_state = "setup_large_vendor" - desc = "It's a case, for building large electronics with. This one resembles a vending machine." + desc = "It's a case, for building large electronics with. This one resembles a vending machine." // Drone assemblies, which can move with the locomotion circuit. @@ -158,9 +173,10 @@ name = "electronic drone" icon_state = "setup_drone" desc = "It's a case, for building mobile electronics with." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 1.5 - max_complexity = IC_COMPLEXITY_BASE * 1.5 + w_class = ITEMSIZE_LARGE + max_components = IC_COMPONENTS_BASE * 3 + max_complexity = IC_COMPLEXITY_BASE * 3 + allowed_circuit_action_flags = IC_ACTION_MOVEMENT | IC_ACTION_COMBAT | IC_ACTION_LONG_RANGE can_anchor = FALSE /obj/item/electronic_assembly/drone/can_move() @@ -172,34 +188,37 @@ /obj/item/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." + desc = "It's a case, for building mobile electronics with. This one is armed and dangerous." /obj/item/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." + desc = "It's a case, for building mobile electronics with. This one resembles a Securitron." /obj/item/electronic_assembly/drone/medibot 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." + desc = "It's a case, for building mobile electronics with. This one resembles a Medibot." /obj/item/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." + desc = "It's a case, for building mobile electronics with. This one has a generic bot design." /obj/item/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." + desc = "It's a case, for building mobile electronics with. This one has a hominoid design." + w_class = ITEMSIZE_COST_HUGE + max_components = IC_COMPONENTS_BASE * 5 + max_complexity = IC_COMPLEXITY_BASE * 5 // Wall mounted assemblies. /obj/item/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 \ + desc = "It's a case, for building medium-sized electronics with. It has a magnetized \ backing to allow it to stick to walls." w_class = ITEMSIZE_NORMAL max_components = IC_COMPONENTS_BASE * 2 @@ -243,7 +262,7 @@ /obj/item/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 \ + desc = "It's a case, for building large electronics with. It has a magnetized backing \ to allow it to stick to walls." w_class = ITEMSIZE_LARGE max_components = IC_COMPONENTS_BASE * 4 @@ -252,7 +271,7 @@ /obj/item/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 \ + desc = "It's a case, for building small electronics with. It has a magnetized backing \ to allow it to stick to walls." w_class = ITEMSIZE_SMALL max_components = IC_COMPONENTS_BASE @@ -261,7 +280,7 @@ /obj/item/electronic_assembly/wallmount/tiny name = "tiny wall-mounted electronic assembly" icon_state = "setup_wallmount_tiny" - desc = "It's a case, for building tiny electronics with. It has a magnetized backing \ + desc = "It's a case, for building tiny electronics with. It has a magnetized backing \ to allow it to stick to walls." w_class = ITEMSIZE_TINY max_components = IC_COMPONENTS_BASE / 2 diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm index 479f4128853..78b931ccf6e 100644 --- a/code/modules/integrated_electronics/core/detailer.dm +++ b/code/modules/integrated_electronics/core/detailer.dm @@ -14,7 +14,7 @@ "orange" = COLOR_ASSEMBLY_ORANGE, "beige" = COLOR_ASSEMBLY_BEIGE, "brown" = COLOR_ASSEMBLY_BROWN, - MAT_GOLD = COLOR_ASSEMBLY_GOLD, + "gold" = COLOR_ASSEMBLY_GOLD, "yellow" = COLOR_ASSEMBLY_YELLOW, "gurkha" = COLOR_ASSEMBLY_GURKHA, "light green" = COLOR_ASSEMBLY_LGREEN, diff --git a/code/modules/integrated_electronics/core/helpers.dm b/code/modules/integrated_electronics/core/helpers.dm index 2f32ba90fb8..15996bf94d1 100644 --- a/code/modules/integrated_electronics/core/helpers.dm +++ b/code/modules/integrated_electronics/core/helpers.dm @@ -1,75 +1,145 @@ -/obj/item/integrated_circuit/proc/setup_io(var/list/io_list, var/io_type, var/list/io_default_list) +/obj/item/integrated_circuit/proc/setup_io(list/io_list, io_type, list/io_default_list, pin_type) var/list/io_list_copy = io_list.Copy() io_list.Cut() - var/i = 1 - - for(var/io_entry in io_list_copy) + for(var/i in 1 to io_list_copy.len) + var/io_entry = io_list_copy[i] var/default_data = null var/io_type_override = null + // Override the default data. - if(io_default_list && io_default_list.len) // List containing special pin types that need to be added. + if(length(io_default_list)) // List containing special pin types that need to be added. default_data = io_default_list["[i]"] // This is deliberately text because the index is a number in text form. + // Override the pin type. if(io_list_copy[io_entry]) io_type_override = io_list_copy[io_entry] if(io_type_override) - io_list.Add(new io_type_override(src, io_entry, default_data)) + io_list.Add(new io_type_override(src, io_entry, default_data, pin_type,i)) else - io_list.Add(new io_type(src, io_entry, default_data)) - i++ + io_list.Add(new io_type(src, io_entry, default_data, pin_type,i)) -/obj/item/integrated_circuit/proc/set_pin_data(var/pin_type, var/pin_number, datum/new_data) + +/obj/item/integrated_circuit/proc/set_pin_data(pin_type, pin_number, datum/new_data) + if(islist(new_data)) + for(var/i in 1 to length(new_data)) + if (istype(new_data) && !isweakref(new_data)) + new_data[i] = WEAKREF(new_data[i]) if (istype(new_data) && !isweakref(new_data)) new_data = WEAKREF(new_data) var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number) return pin.write_data_to_pin(new_data) -/obj/item/integrated_circuit/proc/get_pin_data(var/pin_type, var/pin_number) +/obj/item/integrated_circuit/proc/get_pin_data(pin_type, 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) +/obj/item/integrated_circuit/proc/get_pin_data_as_type(pin_type, pin_number, as_type) + var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number) return pin.data_as_type(as_type) -/obj/item/integrated_circuit/proc/activate_pin(var/pin_number) +/// Returns a ref to the first linked pin. +/obj/item/integrated_circuit/proc/get_pin_linked_src(pin_type, pin_number) + var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number) + if(!pin.linked.len) return null + var/datum/integrated_io/pin_link = pin.linked[1] + return pin_link.holder + +/obj/item/integrated_circuit/proc/activate_pin(pin_number) var/datum/integrated_io/activate/A = activators[pin_number] A.push_data() +/obj/item/integrated_circuit/proc/get_pin_ref(pin_type, pin_number) + switch(pin_type) + if(IC_INPUT) + if(pin_number > inputs.len) + return + return inputs[pin_number] + if(IC_OUTPUT) + if(pin_number > outputs.len) + return + return outputs[pin_number] + if(IC_ACTIVATOR) + if(pin_number > activators.len) + return + return activators[pin_number] + return + /datum/integrated_io/proc/get_data() - if(isnull(data)) - return + if(islist(data)) + for(var/i in 1 to length(data)) + if(isweakref(data[i])) + data[i] = data[i].resolve() if(isweakref(data)) return data.resolve() return data -/obj/item/integrated_circuit/proc/get_pin_ref(var/pin_type, var/pin_number) +/// Returns a list of parameters necessary to locate a pin in the assembly: component number, pin type and pin number +/// Components list can be supplied from the outside, for use in savefiles +/datum/integrated_io/proc/get_pin_parameters(list/components) + if(!holder) + return + + if(!components) + if(!holder.assembly) + return + components = holder.assembly.assembly_components + + var/component_number = components.Find(holder) + + var/list/pin_holder_list switch(pin_type) if(IC_INPUT) - if(pin_number > inputs.len) - return null - return inputs[pin_number] + pin_holder_list = holder.inputs if(IC_OUTPUT) - if(pin_number > outputs.len) - return null - return outputs[pin_number] + pin_holder_list = holder.outputs if(IC_ACTIVATOR) - if(pin_number > activators.len) - return null - return activators[pin_number] - return null + pin_holder_list = holder.activators + else + return -/obj/item/integrated_circuit/proc/handle_wire(var/datum/integrated_io/pin, var/obj/item/integrated_electronics/tool) - if(istype(tool, /obj/item/integrated_electronics/wirer)) - var/obj/item/integrated_electronics/wirer/wirer = tool - if(pin) - wirer.wire(pin, usr) - return 1 + var/pin_number = pin_holder_list.Find(src) - else if(istype(tool, /obj/item/integrated_electronics/debugger)) - var/obj/item/integrated_electronics/debugger/debugger = tool - if(pin) - debugger.write_data(pin, usr) - return 1 - return 0 + return list(component_number, pin_type, pin_number) + + +/// Locates a pin in the assembly when given component number, pin type and pin number +/// Components list can be supplied from the outside, for use in savefiles +/obj/item/electronic_assembly/proc/get_pin_ref(component_number, pin_type, pin_number, list/components) + if(!components) + components = assembly_components + + if(component_number > components.len) + return + + var/obj/item/integrated_circuit/component = components[component_number] + return component.get_pin_ref(pin_type, pin_number) + + +/// Same as get_pin_ref, but takes in a list of 3 parameters (same format as get_pin_parameters) +/// and performs extra sanity checks on parameters list and index numbers +/obj/item/electronic_assembly/proc/get_pin_ref_list(list/parameters, list/components) + if(!components) + components = assembly_components + + if(!islist(parameters) || parameters.len != 3) + return + + // Those are supposed to be list indexes, check them for sanity + if(!isnum(parameters[1]) || parameters[1] % 1 || parameters[1] < 1) + return + + if(!isnum(parameters[3]) || parameters[3] % 1 || parameters[3] < 1) + return + + return get_pin_ref(parameters[1], parameters[2], parameters[3], components) + +/// Used to obfuscate object refs imported/exported as strings. +/// Not very secure, but if someone still finds a way to abuse refs, they deserve it. +/proc/XorEncrypt(string, key) + if(!string || !key ||!istext(string)||!istext(key)) + return + var/r + for(var/i = 1 to length(string)) + r += ascii2text(text2ascii(string,i) ^ text2ascii(key,(i-1)%length(string)+1)) + return r diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm index e8ec9c185ef..a6a98b40529 100644 --- a/code/modules/integrated_electronics/core/integrated_circuit.dm +++ b/code/modules/integrated_electronics/core/integrated_circuit.dm @@ -3,12 +3,60 @@ 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 + name = "integrated circuit" + desc = "It's a tiny chip! This one doesn't seem to do much, however." + icon = 'icons/obj/integrated_electronics/electronic_components.dmi' + icon_state = "template" + w_class = ITEMSIZE_TINY + /// Reference to the assembly holding this circuit, if any. + var/obj/item/electronic_assembly/assembly = null + var/extended_desc = null + var/can_be_asked_input = 0 + var/list/inputs = 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/inputs_default = list() + var/list/outputs = list() + /// Ditto, for output. + var/list/outputs_default = list() + var/list/activators = list() + ///Uses world.time + var/next_use = 0 + ///This acts as a limitation on building machines, more resource-intensive components cost more 'space'. + var/complexity = 1 + var/size = null + ///This acts as a limitation on building machines, bigger components cost more 'space'. -1 for size 0 + var/cost = 1 + /// Circuits are limited in how many times they can be work()'d by this variable. + var/cooldown_per_use = IC_DEFAULT_COOLDOWN + /// Circuits are limited in how many times they can be work()'d with external world by this variable. + var/ext_cooldown = 0 + /// How much power is drawn when work()'d. + var/power_draw_per_use = 0 + /// How much power is drawn when doing nothing. + var/power_draw_idle = 0 + /// Used for world initializing, see the #defines above. + var/spawn_flags = null + /// Used for telling circuits that can do certain actions from other circuits. + var/action_flags = NONE + /// To show up on circuit printer, and perhaps other places. + var/category_text = "NO CATEGORY THIS IS A BUG" + /// Determines if a circuit is removable from the assembly. + var/removable = TRUE + var/displayed_name = "" + /// Allows additional multitool functionality + var/allow_multitool = 1 + /obj/item/integrated_circuit/examine(mob/user) . = ..() external_examine(user) ui_interact(user) -// This should be used when someone is examining while the case is opened. +/// Can be called via electronic_assembly/attackby() +/obj/item/integrated_circuit/proc/additem(var/obj/item/I, var/mob/living/user) + attackby(I, 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) @@ -20,37 +68,42 @@ a creative player the means to solve many problems. Circuits are held inside an 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) + to_chat(user, any_examine(user)) ui_interact(user) -// This should be used when someone is examining from an 'outside' perspective, e.g. reading a screen or LED. +/// 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) + return any_examine(user) /obj/item/integrated_circuit/proc/any_examine(mob/user) return +/obj/item/integrated_circuit/proc/attackby_react(var/atom/movable/A,mob/user) + return + +/obj/item/integrated_circuit/proc/sense(var/atom/movable/A,mob/user,prox) + return + /obj/item/integrated_circuit/Initialize(mapload) - . = ..() displayed_name = name if(!size) size = w_class + if(!cost) + cost = size * IC_MATERIAL_MODIFIER 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) + setup_io(inputs, /datum/integrated_io, inputs_default, IC_INPUT) + setup_io(outputs, /datum/integrated_io, outputs_default, IC_OUTPUT) + setup_io(activators, /datum/integrated_io/activate, null, IC_ACTIVATOR) + . = ..() /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) + QDEL_LIST(inputs) + QDEL_LIST(outputs) + QDEL_LIST(activators) . = ..() /obj/item/integrated_circuit/emp_act(severity) @@ -58,7 +111,7 @@ a creative player the means to solve many problems. Circuits are held inside an io.scramble() /obj/item/integrated_circuit/proc/check_interactivity(mob/user) - return ui_status(user, GLOB.physical_state) == UI_INTERACTIVE + return ui_status(user, GLOB.physical_state) && !assembly || assembly.opened == UI_INTERACTIVE /obj/item/integrated_circuit/verb/rename_component() set name = "Rename Circuit" @@ -66,11 +119,8 @@ a creative player the means to solve many problems. Circuits are held inside an set desc = "Rename your circuit, useful to stay organized." var/mob/M = usr - if(!check_interactivity(M)) - return - - var/input = sanitizeSafe(input(usr, "What do you want to name the circuit?", "Rename", src.name) as null|text, MAX_NAME_LEN) - if(src && input && assembly.check_interactivity(M)) + var/input = tgui_input_text(usr, "What do you want to name this circuit?", "Rename", src.name, MAX_NAME_LEN) + if(src && input) to_chat(M, SPAN_NOTICE("The circuit '[src.name]' is now labeled '[input]'.")) displayed_name = input @@ -100,6 +150,8 @@ a creative player the means to solve many problems. Circuits are held inside an data["power_draw_idle"] = power_draw_idle data["power_draw_per_use"] = power_draw_per_use data["extended_desc"] = extended_desc + data["cooldown_per_use"] = cooldown_per_use + data["ext_cooldown"] = ext_cooldown var/list/inputs_list = list() var/list/outputs_list = list() @@ -218,27 +270,32 @@ a creative player the means to solve many problems. Circuits are held inside an examined.ui_interact(usr) if("remove") - remove(usr) + remove(usr, index = params["index"]) return return FALSE -/obj/item/integrated_circuit/proc/remove(mob/user) +/obj/item/integrated_circuit/proc/remove(mob/user, silent, index) var/obj/item/electronic_assembly/A = assembly - if(!A) + if(!A && !silent) to_chat(user, SPAN_WARNING("This circuit is not in an assembly!")) return - if(!removable) + if(!removable && !silent) to_chat(user, SPAN_WARNING("\The [src] seems to be permanently attached to the case.")) return var/obj/item/electronic_assembly/ea = loc power_fail() disconnect_all() + // Remove from helper and TGUI lists + A.ui_circuit_props.Cut(index, index + 1) + A.assembly_components.Cut(index, index + 1) + var/turf/T = get_turf(src) forceMove(T) assembly = null - playsound(T, 'sound/items/Crowbar.ogg', 50, TRUE) - to_chat(user, SPAN_NOTICE("You pop \the [src] out of the case, and slide it out.")) + if(!silent) + playsound(T, 'sound/items/Crowbar.ogg', 50, TRUE) + to_chat(user, SPAN_NOTICE("You pop \the [src] out of the case, and slide it out.")) if(istype(ea)) ea.ui_interact(user) @@ -248,6 +305,7 @@ a creative player the means to solve many problems. Circuits are held inside an for(var/datum/integrated_io/O in outputs) O.push_data() +//! Don't use this! Very bad! Takes all data from attached pins regardless of pulse state. Make sure you understand the consequences of this if you insist on using it. /obj/item/integrated_circuit/proc/pull_data() for(var/datum/integrated_io/I in inputs) I.push_data() @@ -256,11 +314,11 @@ a creative player the means to solve many problems. Circuits are held inside an if(assembly) return assembly.draw_power(power_draw_idle) -// Override this for special behaviour when there's no power left. +/// 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(). +/// 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. @@ -268,27 +326,89 @@ a creative player the means to solve many problems. Circuits are held inside an return TRUE // Battery has enough. return FALSE // Not enough power. -/obj/item/integrated_circuit/proc/check_then_do_work(var/ignore_power = FALSE) +/obj/item/integrated_circuit/proc/check_then_do_work(ord, ignore_power = FALSE) if(world.time < next_use) // All intergrated circuits have an internal cooldown, to protect from spam. - return + return FALSE + if(assembly && ext_cooldown && (world.time < assembly.ext_next_use)) // Some circuits have external cooldown, to protect from spam. + return FALSE if(power_draw_per_use && !ignore_power) if(!check_power()) power_fail() - return + return FALSE next_use = world.time + cooldown_per_use - do_work() + if(assembly) + assembly.ext_next_use = world.time + ext_cooldown + do_work(ord) + return TRUE -/obj/item/integrated_circuit/proc/do_work() +/obj/item/integrated_circuit/proc/do_work(ord) 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() + var/datum/integrated_io/I + for(var/i in inputs) + I = i + I.disconnect_all() + + for(var/i in outputs) + I = i + I.disconnect_all() + + for(var/i in activators) + I = i + I.disconnect_all() + +/obj/item/integrated_circuit/proc/ext_moved(oldLoc, dir) + return + + +/// Returns the object that is supposed to be used in attack messages, location checks, etc. +/obj/item/integrated_circuit/proc/get_object() + // If the component is located in an assembly, let assembly determine it. + if(assembly) + return assembly.get_object() + else + return src // If not, the component is acting on its own. + + +/// Returns the location to be used for dropping items. +/// Same as the regular drop_location(), but with proc being run on assembly if there is any. +/obj/item/integrated_circuit/drop_location() + // If the component is located in an assembly, let the assembly figure that one out. + if(assembly) + return assembly.drop_location() + else + return ..() // If not, the component is acting on its own. + + +/// Checks if the target object is reachable. Useful for various manipulators and manipulator-like objects. +/obj/item/integrated_circuit/proc/check_target(atom/target, exclude_contents = FALSE, exclude_components = FALSE, exclude_self = FALSE, exclude_outside = FALSE) + if(!target) + return FALSE + + var/atom/movable/acting_object = get_object() + + if(exclude_self && target == acting_object) + return FALSE + if(exclude_components && assembly) + if(target in assembly.assembly_components) + return FALSE + if(target == assembly.battery) + return FALSE + if(!exclude_outside && target.Adjacent(acting_object) && isturf(target.loc)) + return TRUE + if(!exclude_contents && (target in acting_object.GetAllContents())) + return TRUE + if(target in acting_object.loc) + return TRUE + return FALSE +/* TBI: Hand recog +/obj/item/integrated_circuit/can_trigger_gun(mob/living/user) + if(!user.is_holding(src)) + return FALSE + return ..() +*/ /obj/item/integrated_circuit/proc/on_anchored() return diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm index facaa00c1e3..d73149d580c 100644 --- a/code/modules/integrated_electronics/core/pins.dm +++ b/code/modules/integrated_electronics/core/pins.dm @@ -20,25 +20,32 @@ D [1]/ || */ /datum/integrated_io var/name = "input/output" - var/obj/item/integrated_circuit/holder = null - var/datum/weakref/data = null // This is a weakref, to reduce typecasts. Note that oftentimes numbers and text may also occupy this. + var/obj/item/integrated_circuit/holder + var/datum/weakref/data // This is a weakref, to reduce typecasts. Note that oftentimes numbers and text may also occupy this. var/list/linked = list() var/io_type = DATA_CHANNEL + var/pin_type // IC_INPUT, IC_OUTPUT, IC_ACTIVATOR - used in saving assembly wiring + var/ord + +/datum/integrated_io/New(loc, _name, _data, _pin_type,_ord) + name = _name + if(_data) + data = _data + if(_pin_type) + pin_type = _pin_type + if(_ord) + ord = _ord + + holder = loc -/datum/integrated_io/New(var/newloc, var/name, var/new_data) - ..() - src.name = name - if(!isnull(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.") + message_admins("ERROR: An integrated_io ([name]) spawned without a valid holder! This is a bug.") /datum/integrated_io/Destroy() - disconnect() + disconnect_all() data = null holder = null - . = ..() + return ..() /datum/integrated_io/ui_host() return holder.ui_host() @@ -84,8 +91,7 @@ list[]( if(isweakref(input)) var/datum/weakref/w = input var/atom/A = w.resolve() - //return A ? "([A.name] \[Ref\])" : "(null)" // For refs, we want just the name displayed. - return A ? "(\ref[A] \[Ref\])" : "(null)" + return A ? "([A.name] \[Ref\])" : "(null)" // For refs, we want just the name displayed. return "([input])" // Nothing special needed for numbers or other stuff. @@ -110,30 +116,67 @@ list[]( /datum/integrated_io/activate/scramble() push_data() -/datum/integrated_io/proc/write_data_to_pin(var/new_data) +/* TBI Screw multitools, they're all over the place. +/datum/integrated_io/proc/handle_wire(datum/integrated_io/linked_pin, obj/item/tool, action, mob/living/user) + if(tool.tool_behaviour == TOOL_MULTITOOL) + switch(action) + if("wire") + tool.wire(src, user) + return TRUE + if("unwire") + if(linked_pin) + tool.unwire(src, linked_pin, user) + return TRUE + if("data") + ask_for_pin_data(user) + return TRUE + + else if(istype(tool, /obj/item/integrated_electronics/wirer)) + var/obj/item/integrated_electronics/wirer/wirer = tool + if(linked_pin) + wirer.wire(linked_pin, user) + else + wirer.wire(src, user) + + else if(istype(tool, /obj/item/integrated_electronics/debugger)) + var/obj/item/integrated_electronics/debugger/debugger = tool + debugger.write_data(src, user) + return TRUE + + return FALSE +*/ + +/datum/integrated_io/proc/write_data_to_pin(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 = sanitizeSafe(new_data, MAX_MESSAGE_LEN, 0, 0) data = new_data holder.on_data_written() + else if(islist(new_data)) + var/list/new_list = new_data + data = new_list.Copy(max(1,new_list.len - IC_MAX_LIST_LENGTH+1),0) + holder.on_data_written() /datum/integrated_io/proc/push_data() - for(var/datum/integrated_io/io in linked) + for(var/k in 1 to linked.len) + var/datum/integrated_io/io = linked[k] io.write_data_to_pin(data) /datum/integrated_io/activate/push_data() - for(var/datum/integrated_io/io in linked) - io.holder.check_then_do_work() + for(var/k in 1 to linked.len) + var/datum/integrated_io/io = linked[k] + io.holder.check_then_do_work(io.ord) -/datum/integrated_io/proc/pull_data() - for(var/datum/integrated_io/io in linked) - write_data_to_pin(io.data) +/datum/integrated_io/proc/pull_data() //! Bad! Why would you ever use this? Don't do it!! + for(var/k in 1 to linked.len) + var/datum/integrated_io/io = linked[k] + write_data_to_pin(io.data) //! Pulls input from any linked pins on pulse, overwriting input you probably want. /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) @@ -145,6 +188,19 @@ list[]( continue //Now that we're removed from them, we gotta remove them from us. src.linked.Remove(their_io) +*/ +/datum/integrated_io/proc/connect_pin(datum/integrated_io/pin) + pin.linked |= src + linked |= pin + +// Iterates over every linked pin and disconnects them. +/datum/integrated_io/proc/disconnect_all() + for(var/pin in linked) + disconnect_pin(pin) + +/datum/integrated_io/proc/disconnect_pin(datum/integrated_io/pin) + pin.linked.Remove(src) + linked.Remove(pin) /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 = tgui_input_list(usr, "Please choose a type to use.","[src] type setting", allowed_data_types) @@ -173,12 +229,13 @@ list[]( return !isnull(data) // This proc asks for the data to write, then writes it. -/datum/integrated_io/proc/ask_for_pin_data(mob/user, obj/item/I) +/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) + holder.investigate_log(" was manually pulsed by [key_name(user)].", INVESTIGATE_CIRCUIT) + holder.check_then_do_work(ord, ignore_power = TRUE) to_chat(user, "You pulse \the [holder]'s [src] pin.") /datum/integrated_io/activate diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm index 699c9946bb8..5087a1f71d3 100644 --- a/code/modules/integrated_electronics/core/printer.dm +++ b/code/modules/integrated_electronics/core/printer.dm @@ -1,35 +1,69 @@ - /obj/item/integrated_circuit_printer name = "integrated circuit printer" desc = "A portable(ish) machine made to print tiny modular circuitry out of metal." icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' icon_state = "circuit_printer" w_class = ITEMSIZE_LARGE - var/metal = 0 - var/max_metal = 100 + var/cur_metal = 0 + var/max_metal = 250 /// One sheet equals this much metal. var/metal_per_sheet = 10 - /// If true, metal is infinite. - var/debug = FALSE /// When hit with an upgrade disk, will turn true, allowing it to print the higher tier circuits. var/upgraded = FALSE - /// Same for above, but will allow the printer to duplicate a specific assembly. (Not implemented) - var/can_clone = FALSE -// var/static/list/recipe_list = list() - /// Not implemented :( - var/obj/item/electronic_assembly/assembly_to_clone = null + /// Same for above, but will allow the printer to duplicate a specific assembly. + var/can_clone = TRUE + /// If this is false, then cloning will take an amount of deciseconds equal to the metal cost divided by 100. + var/fast_clone = FALSE + /// If true, metal is infinite. + var/debug = FALSE + var/static/list/recipe_list = list() + /// If the printer is currently creating a circuit + var/cloning = FALSE + /// If an assembly is being emptied into this printer + var/recycling = FALSE + /// Currently loaded save, in form of list + var/list/program var/dirty_items = FALSE +/obj/item/integrated_circuit_printer/examine(mob/user) + . = ..() + ui_interact(user) + /obj/item/integrated_circuit_printer/upgraded upgraded = TRUE can_clone = TRUE + fast_clone = TRUE /obj/item/integrated_circuit_printer/debug name = "fractal integrated circuit printer" desc = "A portable(ish) machine that makes modular circuitry seemingly out of thin air." + debug = TRUE upgraded = TRUE can_clone = TRUE - debug = TRUE + fast_clone = TRUE + w_class = WEIGHT_CLASS_TINY +/* TBI: Requires material containers +/obj/item/integrated_circuit_printer/Initialize(mapload) + . = ..() + var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(/datum/material/iron), MINERAL_MATERIAL_AMOUNT * 25, TRUE, list(/obj/item/stack, /obj/item/integrated_circuit, /obj/item/electronic_assembly)) + materials.precise_insertion = TRUE +*/ +/obj/item/integrated_circuit_printer/proc/print_program(mob/user) + visible_message(SPAN_NOTICE("[src] has finished printing its assembly!")) + playsound(src, 'sound/items/poster_being_created.ogg', 50, TRUE) + var/obj/item/electronic_assembly/assembly = SScircuit.load_electronic_assembly(get_turf(src), program) + assembly.creator = key_name(user) + assembly.investigate_log("was printed by [assembly.creator].", INVESTIGATE_CIRCUIT) + cloning = FALSE + +/obj/item/integrated_circuit_printer/proc/check_max_metal(inc) + if(cur_metal + inc > max_metal) + inc = CEILING(src.cur_metal + inc - src.max_metal, src.metal_per_sheet) / 10 + var/obj/item/stack/material/steel/S = new /obj/item/stack/material/steel(loc, inc, TRUE) + to_chat(usr, SPAN_NOTICE("[src] ejects [S.amount] [S.singular_name]\s!")) + src.cur_metal -= src.metal_per_sheet * inc + return TRUE + return FALSE /obj/item/integrated_circuit_printer/attack_robot(mob/user as mob) if(Adjacent(user)) @@ -44,21 +78,23 @@ if(debug) to_chat(user, SPAN_WARNING("\The [src] does not need any material.")) return - var/num = min((max_metal - metal) / metal_per_sheet, stack.amount) + var/num = round(min((max_metal - cur_metal) / metal_per_sheet, stack.amount)) if(num < 1) to_chat(user, SPAN_WARNING("\The [src] is too full to add more metal.")) return if(stack.use(max(1, round(num)))) // We don't want to create stacks that aren't whole numbers to_chat(user, SPAN_NOTICE("You add [num] sheet\s to \the [src].")) - metal += num * metal_per_sheet + cur_metal += num * metal_per_sheet attack_self(user) return TRUE if(istype(O,/obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/IC = O to_chat(user, SPAN_NOTICE("You insert the circuit into \the [src].")) - user.unEquip(O) - metal = min(metal + O.w_class, max_metal) - qdel(O) + user.unEquip(IC) + check_max_metal(IC.cost) + cur_metal += IC.cost + qdel(IC) attack_self(user) return TRUE @@ -73,14 +109,63 @@ return TRUE if(istype(O,/obj/item/disk/integrated_circuit/upgrade/clone)) - if(can_clone) + if(fast_clone) to_chat(user, SPAN_WARNING("\The [src] already has this upgrade.")) return TRUE to_chat(user, SPAN_NOTICE("You install \the [O] into \the [src].")) can_clone = TRUE + fast_clone = TRUE attack_self(user) return TRUE + if(istype(O, /obj/item/electronic_assembly)) + var/obj/item/electronic_assembly/EA = O //microtransactions not included + if(recycling) + return + if(!EA.opened) + to_chat(user, SPAN_WARNING("You can't reach [EA]'s components to remove them!")) + return + if(EA.battery) + to_chat(user, SPAN_WARNING("Remove [EA]'s power cell first!")) + return + var/inc = 0 + for(var/V in EA.assembly_components) // Start looking for recyclable components. + var/obj/item/integrated_circuit/IC = V + if(IC.removable) // If found, don't destroy the assembly later. + ++inc + to_chat(user, SPAN_NOTICE("You begin recycling [EA]'s components...")) + playsound(src, 'sound/items/electronic_assembly_emptying.ogg', 50, TRUE) + if(!do_after(user, 30, target = src) && !recycling) //short channel so you don't accidentally start emptying out a complex assembly + recycling = TRUE + break + if (inc == 1) + for(var/V in EA.assembly_components) + var/obj/item/integrated_circuit/IC = V + if(IC.removable) + if(!do_after(user, 5, target = user)) + recycling = FALSE + return + if(IC.removable) + playsound(src, 'sound/items/crowbar.ogg', 50, TRUE) + check_max_metal(IC.cost) + cur_metal += IC.cost + IC.remove(usr, TRUE, inc) + qdel(IC) + else ++inc + to_chat(user, SPAN_NOTICE("You recycle all the components[EA.assembly_components.len ? " you could " : " "]from [EA]!")) + playsound(src, 'sound/items/electronic_assembly_empty.ogg', 50, TRUE) + recycling = FALSE + return TRUE + if(inc == 0) + if(!do_after(user, 10, target = user)) + recycling = FALSE + return + to_chat(user, SPAN_NOTICE("You recycle the [EA]!")) + check_max_metal(EA.cost) + cur_metal += EA.cost + user.remove_from_mob(EA) + qdel(EA) + return TRUE return ..() /obj/item/integrated_circuit_printer/vv_edit_var(var_name, var_value) @@ -119,9 +204,9 @@ for(var/path in circuit_list) var/obj/O = path var/can_build = TRUE + var/obj/item/integrated_circuit/IC = path if(ispath(path, /obj/item/integrated_circuit)) - var/obj/item/integrated_circuit/IC = path if((initial(IC.spawn_flags) & IC_SPAWN_RESEARCH) && (!(initial(IC.spawn_flags) & IC_SPAWN_DEFAULT)) && !upgraded) can_build = FALSE @@ -129,9 +214,11 @@ if(ispath(path, /obj/item/electronic_assembly)) var/obj/item/electronic_assembly/E = path cost = round((initial(E.max_complexity) + initial(E.max_components)) / 4) + else if(ispath(path, /obj/item/integrated_circuit)) + // var/obj/item/integrated_circuit/I = path + cost = initial(IC.cost) else - var/obj/item/I = path - cost = initial(I.w_class) + cost = initial(O.w_class) items.Add(list(list( "name" = initial(O.name), @@ -150,13 +237,22 @@ /obj/item/integrated_circuit_printer/ui_data(mob/user, datum/tgui/ui, datum/ui_state/state) var/list/data = ..() - data["metal"] = metal + data["metal"] = cur_metal data["max_metal"] = max_metal data["metal_per_sheet"] = metal_per_sheet data["debug"] = debug data["upgraded"] = upgraded data["can_clone"] = can_clone - data["assembly_to_clone"] = assembly_to_clone + data["program"] = program + /* + data["used_space"] = program.Find("used_space") + data["max_space"] = program.Find("max_space") + data["complexity"] = program.Find("complexity") + data["max_complexity"] = program.Find("max_complexity") + data["metal_cost"] = program.Find("metal_cost") + data["unsupported_circuit"] = program.Find("unsupported_circuit") + data["requires_upgrades"] = program.Find("requires_upgrades") +*/ return data @@ -166,19 +262,11 @@ switch(action) if("build") + var/cost = text2num(params["cost"]) var/build_type = text2path(params["build"]) if(!build_type || !ispath(build_type)) return TRUE - var/cost = 1 - - if(ispath(build_type, /obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/E = build_type - cost = round( (initial(E.max_complexity) + initial(E.max_components) ) / 4) - else - var/obj/item/I = build_type - cost = initial(I.w_class) - var/in_some_category = FALSE for(var/category in SScircuit.circuit_fabricator_recipe_list) if(build_type in SScircuit.circuit_fabricator_recipe_list[category]) @@ -190,15 +278,80 @@ if(!debug) if(!Adjacent(usr)) to_chat(usr, SPAN_NOTICE("You are too far away from \the [src].")) - if(metal - cost < 0) + if(cur_metal - cost < 0) to_chat(usr, SPAN_WARNING("You need [cost] metal to build that!.")) return TRUE - metal -= cost + cur_metal -= cost var/obj/item/built = new build_type(get_turf(loc)) usr.put_in_hands(built) to_chat(usr, SPAN_NOTICE("[capitalize(built.name)] printed.")) playsound(src, 'sound/items/jaws_pry.ogg', 50, TRUE) return TRUE + if("load_blueprint") //! This should be done through disks and modular comps! For now we have copy-paste here. + var/new_input = input(usr, "Enter blueprint:", "loading",null) as null|text + if(!new_input) + program = null + return + if(istext(new_input)) + to_chat(usr, SPAN_NOTICE("[new_input] load blueprint pressed")) + + var/validation = SScircuit.validate_electronic_assembly(new_input) + // Validation error codes are returned as text. + if(istext(validation)) + to_chat(usr, SPAN_WARNING("Error: [validation]")) + return + else if(islist(validation)) + program = validation + to_chat(usr, SPAN_NOTICE("This is a valid program for [program["assembly"]["type"]].")) + if(program["requires_upgrades"]) + if(upgraded) + to_chat(usr, SPAN_NOTICE("It uses advanced component designs.")) + else + to_chat(usr, SPAN_WARNING("It uses unknown component designs. Printer upgrade is required to proceed.")) + if(program["unsupported_circuit"]) + to_chat(usr, SPAN_WARNING("This program uses components not supported by the specified assembly. Please change the assembly type in the save file to a supported one.")) + to_chat(usr, SPAN_NOTICE("Used space: [program["used_space"]]/[program["max_space"]].")) + to_chat(usr, SPAN_NOTICE("Complexity: [program["complexity"]]/[program["max_complexity"]].")) + to_chat(usr, SPAN_NOTICE("Metal cost: [program["metal_cost"]].")) + return TRUE + if("clone") + if(!program || cloning) + return + if(program["requires_upgrades"] && !upgraded && !debug) + to_chat(usr, SPAN_WARNING("This program uses unknown component designs. Printer upgrade is required to proceed.")) + return + if(program["unsupported_circuit"] && !debug) + to_chat(usr, SPAN_WARNING("This program uses components not supported by the specified assembly. Please change the assembly type in the save file to a supported one.")) + return + else if(fast_clone) + if(debug || cur_metal < program["metal_cost"]) + cloning = TRUE + print_program(usr) + return TRUE + else + to_chat(usr, SPAN_WARNING("You need [program["metal_cost"]] metal to build that!")) + else + if(cur_metal < program["metal_cost"]) + to_chat(usr, SPAN_WARNING("You need [program["metal_cost"]] metal to build that!")) + return + var/cloning_time = round(program["metal_cost"] / 15) + cloning_time = min(cloning_time, MAX_CIRCUIT_CLONE_TIME) + cloning = TRUE + cur_metal -= program["metal_cost"] + to_chat(usr, SPAN_NOTICE("You begin printing a custom assembly. This will take approximately [DisplayTimeText(cloning_time)]. You can still print \ + off normal parts during this time.")) + playsound(src, 'sound/items/poster_being_created.ogg', 50, TRUE) + addtimer(CALLBACK(src, .proc/print_program, usr), cloning_time) + return TRUE + + if("cancel") + if(!cloning || !program) + return + to_chat(usr, SPAN_NOTICE("Cloning has been canceled. Metal cost has been refunded.")) + cloning = FALSE + cur_metal += program["metal_cost"] + check_max_metal(cur_metal) + return TRUE // FUKKEN UPGRADE DISKS @@ -215,7 +368,6 @@ name = "integrated circuit printer upgrade disk - advanced designs" desc = "Install this into your integrated circuit printer to enhance it. This one adds new, advanced designs to the printer." -// To be implemented later. /obj/item/disk/integrated_circuit/upgrade/clone name = "integrated circuit printer upgrade disk - circuit cloner" desc = "Install this into your integrated circuit printer to enhance it. This one allows the printer to duplicate assemblies." diff --git a/code/modules/integrated_electronics/core/saved_circuits.dm b/code/modules/integrated_electronics/core/saved_circuits.dm new file mode 100644 index 00000000000..fa05365cd7c --- /dev/null +++ b/code/modules/integrated_electronics/core/saved_circuits.dm @@ -0,0 +1,361 @@ +// Helpers for saving/loading integrated circuits. + + +// Saves type, modified name and modified inputs (if any) to a list +// The list is converted to JSON down the line. +//"Special" is not verified at any point except for by the circuit itself. +/obj/item/integrated_circuit/proc/save() + var/list/component_params = list() + var/init_name = initial(name) + + // Save initial name used for differentiating assemblies + component_params["type"] = init_name + + // Save the modified name. + if(init_name != displayed_name) + component_params["name"] = displayed_name + + // Saving input values + if(length(inputs)) + var/list/saved_inputs = list() + + for(var/index in 1 to inputs.len) + var/datum/integrated_io/input = inputs[index] + + // Don't waste space saving the default values + if(input.data == inputs_default["[index]"]) + continue + if(input.data == initial(input.data)) + continue + + var/list/input_value = list(index, FALSE, input.data) + // Index, Type, Value + // FALSE is default type used for num/text/list/null + // TODO: support for special input types, such as internal refs and maybe typepaths + + if(islist(input.data) || isnum(input.data) || istext(input.data) || isnull(input.data)) + saved_inputs.Add(list(input_value)) + + if(saved_inputs.len) + component_params["inputs"] = saved_inputs + + var/special = save_special() + if(!isnull(special)) + component_params["special"] = special + + return component_params + +/obj/item/integrated_circuit/proc/save_special() + return + +// Verifies a list of component parameters +// Returns null on success, error name on failure +/obj/item/integrated_circuit/proc/verify_save(list/component_params) + var/init_name = initial(name) + // Validate name + if(component_params["name"] && component_params["name"] != sanitizeName(component_params["name"])) + return "Bad component name at [init_name]." + + // Validate input values + if(component_params["inputs"]) + var/list/loaded_inputs = component_params["inputs"] + if(!islist(loaded_inputs)) + return "Malformed input values list at [init_name]." + + var/inputs_amt = length(inputs) + + // Too many inputs? Inputs for input-less component? This is not good. + if(!inputs_amt || inputs_amt < length(loaded_inputs)) + return "Input values list out of bounds at [init_name]." + + for(var/list/input in loaded_inputs) + if(input.len != 3) + return "Malformed input data at [init_name]." + + var/input_id = input[1] + var/input_type = input[2] + //var/input_value = input[3] + + // No special type support yet. + if(input_type) + return "Unidentified input type at [init_name]!" + // TODO: support for special input types, such as typepaths and internal refs + + // Input ID is a list index, make sure it's sane. + if(!isnum(input_id) || input_id % 1 || input_id > inputs_amt || input_id < 1) + return "Invalid input index at [init_name]." + + +// Loads component parameters from a list +// Doesn't verify any of the parameters it loads, this is the job of verify_save() +/obj/item/integrated_circuit/proc/load(list/component_params) + // Load name + if(component_params["name"]) + displayed_name = html_encode(component_params["name"]) + + // Load input values + if(component_params["inputs"]) + var/list/loaded_inputs = component_params["inputs"] + + for(var/list/input in loaded_inputs) + var/index = input[1] + //var/input_type = input[2] + var/input_value = input[3] + + var/datum/integrated_io/pin = inputs[index] + // The pins themselves validate the data. + pin.write_data_to_pin(istext(input_value)? html_encode(input_value) : input_value) + // TODO: support for special input types, such as internal refs and maybe typepaths + + if(!isnull(component_params["special"])) + load_special(component_params["special"]) + +/obj/item/integrated_circuit/proc/load_special(special_data) + return + +// Saves type and modified name (if any) to a list +// The list is converted to JSON down the line. +/obj/item/electronic_assembly/proc/save() + var/list/assembly_params = list() + + // Save initial name used for differentiating assemblies + assembly_params["type"] = initial(name) + + // Save modified name + if(initial(name) != name) + assembly_params["name"] = name + + // Save modified description + if(initial(desc) != desc) + assembly_params["desc"] = desc + + // Save modified color + if(initial(detail_color) != detail_color) + assembly_params["detail_color"] = detail_color + + return assembly_params + + +// Verifies a list of assembly parameters +// Returns null on success, error name on failure +/obj/item/electronic_assembly/proc/verify_save(list/assembly_params) + // Validate name and color + if(assembly_params["name"] && assembly_params["name"] != sanitizeName(assembly_params["name"])) + return "Bad assembly name." + if(assembly_params["desc"] && !reject_bad_text(assembly_params["desc"])) + return "Bad assembly description." + if(assembly_params["detail_color"] && !reject_bad_text(assembly_params["detail_color"], 7)) + return "Bad assembly color." + +// Loads assembly parameters from a list +// Doesn't verify any of the parameters it loads, this is the job of verify_save() +/obj/item/electronic_assembly/proc/load(list/assembly_params) + // Load modified name, if any. + if(assembly_params["name"]) + name = html_encode(assembly_params["name"]) + + // Load modified description, if any. + if(assembly_params["desc"]) + desc = html_encode(assembly_params["desc"]) + + if(assembly_params["detail_color"]) + detail_color = assembly_params["detail_color"] + + update_icon() + +// Attempts to save an assembly into a save file format. +// Returns null if assembly is not complete enough to be saved. +/datum/controller/subsystem/processing/circuit/proc/save_electronic_assembly(obj/item/electronic_assembly/assembly) + // No components? Don't even try to save it. + if(!length(assembly.assembly_components)) + return + + + var/list/blocks = list() + + // Block 1. Assembly. + blocks["assembly"] = assembly.save() + // (implant assemblies are not yet supported) + + + // Block 2. Components. + var/list/components = list() + for(var/c in assembly.assembly_components) + var/obj/item/integrated_circuit/component = c + components.Add(list(component.save())) + blocks["components"] = components + + + // Block 3. Wires. + var/list/wires = list() + var/list/saved_wires = list() + + for(var/c in assembly.assembly_components) + var/obj/item/integrated_circuit/component = c + var/list/all_pins = component.inputs + component.outputs + component.activators + + for(var/p in all_pins) + var/datum/integrated_io/pin = p + var/list/params = pin.get_pin_parameters() + var/text_params = params.Join() + + for(var/p2 in pin.linked) + var/datum/integrated_io/pin2 = p2 + var/list/params2 = pin2.get_pin_parameters() + var/text_params2 = params2.Join() + + // Check if we already saved an opposite version of this wire + // (do not save the same wire twice) + if((text_params2 + "=" + text_params) in saved_wires) + continue + + // If not, add a wire "hash" for future checks and save it + saved_wires.Add(text_params + "=" + text_params2) + wires.Add(list(list(params, params2))) + + if(wires.len) + blocks["wires"] = wires + + return json_encode(blocks) + + + +// Checks assembly save and calculates some of the parameters. +// Returns assembly (type: list) if the save is valid. +// Returns error code (type: text) if loading has failed. +// The following parameters area calculated during validation and added to the returned save list: +// "requires_upgrades", "unsupported_circuit", "metal_cost", "complexity", "max_complexity", "used_space", "max_space" +/datum/controller/subsystem/processing/circuit/proc/validate_electronic_assembly(program) + var/list/blocks = json_decode(program) + if(!blocks) + return + + var/error + + + // Block 1. Assembly. + var/list/assembly_params = blocks["assembly"] + + if(!islist(assembly_params) || !length(assembly_params)) + return "Invalid assembly data." // No assembly, damaged assembly or empty assembly + + // Validate type, get a temporary component + var/assembly_path = all_assemblies[assembly_params["type"]] + var/obj/item/electronic_assembly/assembly = cached_assemblies[assembly_path] + if(!assembly) + return "Invalid assembly type." + + // Check assembly save data for errors + error = assembly.verify_save(assembly_params) + if(error) + return error + + + // Read space & complexity limits and start keeping track of them + blocks["complexity"] = 0 + blocks["max_complexity"] = assembly.max_complexity + blocks["used_space"] = 0 + blocks["max_space"] = assembly.max_components + + // Start keeping track of total metal cost + blocks["metal_cost"] = assembly.cost + + + // Block 2. Components. + if(!islist(blocks["components"]) || !length(blocks["components"])) + return "Invalid components list." // No components or damaged components list + + var/list/assembly_components = list() + for(var/C in blocks["components"]) + var/list/component_params = C + + if(!islist(component_params) || !length(component_params)) + return "Invalid component data." + + // Validate type, get a temporary component + var/component_path = all_components[component_params["type"]] + var/obj/item/integrated_circuit/component = cached_components[component_path] + if(!component) + return "Invalid component type." + + // Add temporary component to assembly_components list, to be used later when verifying the wires + assembly_components.Add(component) + + // Check component save data for errors + error = component.verify_save(component_params) + if(error) + return error + + // Update estimated assembly complexity, taken space and material cost + blocks["complexity"] += component.complexity + blocks["used_space"] += component.size + blocks["metal_cost"] += component.w_class + + // Check if the assembly requires printer upgrades + if(!(component.spawn_flags & IC_SPAWN_DEFAULT)) + blocks["requires_upgrades"] = TRUE + + // Check if the assembly supports the circucit + if((component.action_flags & assembly.allowed_circuit_action_flags) != component.action_flags) + blocks["unsupported_circuit"] = TRUE + + + // Check complexity and space limitations + if(blocks["used_space"] > blocks["max_space"]) + return "Used space overflow." + if(blocks["complexity"] > blocks["max_complexity"]) + return "Complexity overflow." + + + // Block 3. Wires. + if(blocks["wires"]) + if(!islist(blocks["wires"])) + return "Invalid wiring list." // Damaged wires list + + for(var/w in blocks["wires"]) + var/list/wire = w + + if(!islist(wire) || wire.len != 2) + return "Invalid wire data." + + var/datum/integrated_io/IO = assembly.get_pin_ref_list(wire[1], assembly_components) + var/datum/integrated_io/IO2 = assembly.get_pin_ref_list(wire[2], assembly_components) + if(!IO || !IO2) + return "Invalid wire data." + + if(initial(IO.io_type) != initial(IO2.io_type)) + return "Wire type mismatch." + + return blocks + + +// Loads assembly (in form of list) into an object and returns it. +// No sanity checks are performed, save file is expected to be validated by validate_electronic_assembly +/datum/controller/subsystem/processing/circuit/proc/load_electronic_assembly(loc, list/blocks) + + // Block 1. Assembly. + var/list/assembly_params = blocks["assembly"] + var/obj/item/electronic_assembly/assembly_path = all_assemblies[assembly_params["type"]] + var/obj/item/electronic_assembly/assembly = new assembly_path(null) + assembly.load(assembly_params) + + + + // Block 2. Components. + for(var/component_params in blocks["components"]) + var/obj/item/integrated_circuit/component_path = all_components[component_params["type"]] + var/obj/item/integrated_circuit/component = new component_path(assembly) + assembly.add_component(component) + component.load(component_params) + + + // Block 3. Wires. + if(blocks["wires"]) + for(var/w in blocks["wires"]) + var/list/wire = w + var/datum/integrated_io/IO = assembly.get_pin_ref_list(wire[1]) + var/datum/integrated_io/IO2 = assembly.get_pin_ref_list(wire[2]) + IO.connect_pin(IO2) + + assembly.forceMove(loc) + return assembly diff --git a/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm index 7e769d862fd..1e01bfc8c3c 100644 --- a/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm @@ -20,6 +20,6 @@ return IC_FORMAT_BOOLEAN /datum/integrated_io/boolean/display_data(var/input) - if(data == TRUE) - return "(True)" - return "(False)" + if(data) + return "(TRUE)" + return "(FALSE)" diff --git a/code/modules/integrated_electronics/core/special_pins/color_pin.dm b/code/modules/integrated_electronics/core/special_pins/color_pin.dm index f1816a83de9..239015c3326 100644 --- a/code/modules/integrated_electronics/core/special_pins/color_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/color_pin.dm @@ -15,11 +15,11 @@ 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 + var/friends = copytext_char(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) + var/hex = copytext_char(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++ @@ -31,7 +31,12 @@ /datum/integrated_io/color/scramble() if(!is_valid()) return - var/new_data = get_random_colour(simple = FALSE, lower = 0, upper = 255) + var/new_data + for(var/i=1;i<=3;i++) + var/temp_col = "[num2hex(rand(0,255))]" + if(length(temp_col )<2) + temp_col = "0[temp_col]" + new_data += temp_col data = new_data push_data() diff --git a/code/modules/integrated_electronics/core/special_pins/index_pin.dm b/code/modules/integrated_electronics/core/special_pins/index_pin.dm new file mode 100644 index 00000000000..b71acb26778 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/index_pin.dm @@ -0,0 +1,21 @@ +// These pins can only contain integer numbers between 0 and IC_MAX_LIST_LENGTH. Null is allowed. +/datum/integrated_io/index + name = "index pin" + data = 1 + +/datum/integrated_io/index/ask_for_pin_data(mob/user) + var/new_data = input(user, "Please type in an index.","[src] index writing") as num + if(isnum(new_data) && holder.check_interactivity(user)) + to_chat(user, SPAN_NOTICE("You input [new_data] into the pin.")) + write_data_to_pin(new_data) + +/datum/integrated_io/index/write_data_to_pin(new_data) + if(isnull(new_data)) + new_data = 0 + + if(isnum(new_data)) + data = clamp(round(new_data), 0, IC_MAX_LIST_LENGTH) + holder.on_data_written() + +/datum/integrated_io/index/display_pin_type() + return IC_FORMAT_INDEX diff --git a/code/modules/integrated_electronics/core/special_pins/list_pin.dm b/code/modules/integrated_electronics/core/special_pins/lists_pin.dm similarity index 50% rename from code/modules/integrated_electronics/core/special_pins/list_pin.dm rename to code/modules/integrated_electronics/core/special_pins/lists_pin.dm index 40bf71deb93..3532d68d706 100644 --- a/code/modules/integrated_electronics/core/special_pins/list_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/lists_pin.dm @@ -1,127 +1,132 @@ // These pins contain a list. Null is not allowed. -/datum/integrated_io/list +/datum/integrated_io/lists name = "list pin" data = list() - -/datum/integrated_io/list/ask_for_pin_data(mob/user) +/datum/integrated_io/lists/ask_for_pin_data(mob/user) interact(user) -/datum/integrated_io/list/proc/interact(mob/user) +/datum/integrated_io/lists/proc/interact(mob/user) var/list/my_list = data var/t = "

[src]


" t += "List length: [my_list.len]
" - t += "\[Refresh\] | " - t += "\[Add\] | " - t += "\[Swap\] | " - t += "\[Clear\]
" + t += "\[Refresh\] | " + t += "\[Add\] | " + t += "\[Remove\] | " + t += "\[Edit\] | " + 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") + 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) +/datum/integrated_io/lists/proc/add_to_list(mob/user, var/new_entry) if(!new_entry && user) new_entry = ask_for_data_type(user) if(is_valid(new_entry)) Add(new_entry) -/datum/integrated_io/list/proc/Add(var/new_entry) +/datum/integrated_io/lists/proc/Add(var/new_entry) var/list/my_list = data if(my_list.len > IC_MAX_LIST_LENGTH) my_list.Cut(Start=1,End=2) my_list.Add(new_entry) -/datum/integrated_io/list/proc/remove_from_list_by_position(mob/user, var/position) +/datum/integrated_io/lists/proc/remove_from_list_by_position(mob/user, var/position) var/list/my_list = data if(!my_list.len) to_chat(user, "The list is empty, there's nothing to remove.") return if(!position) return - var/target_entry = my_list.Find(position) + var/target_entry = my_list[position] if(target_entry) my_list.Remove(target_entry) -/datum/integrated_io/list/proc/remove_from_list(mob/user, var/target_entry) +/datum/integrated_io/lists/proc/remove_from_list(mob/user, var/target_entry) var/list/my_list = data if(!my_list.len) to_chat(user, "The list is empty, there's nothing to remove.") return if(!target_entry) - target_entry = tgui_input_list(usr, "Which piece of data do you want to remove?", "Remove", my_list) - if(target_entry) + target_entry = input(user, "Which piece of data do you want to remove?", "Remove") as null|anything in my_list + if(holder.check_interactivity(user) && target_entry) my_list.Remove(target_entry) -/datum/integrated_io/list/proc/edit_in_list(mob/user, var/target_entry) +/datum/integrated_io/lists/proc/edit_in_list(mob/user, var/target_entry) var/list/my_list = data if(!my_list.len) to_chat(user, "The list is empty, there's nothing to modify.") return if(!target_entry) - target_entry = tgui_input_list(usr, "Which piece of data do you want to edit?", "Edit", my_list) - if(target_entry) + target_entry = input(user, "Which piece of data do you want to edit?", "Edit") as null|anything in my_list + if(holder.check_interactivity(user) && target_entry) var/edited_entry = ask_for_data_type(user, target_entry) if(edited_entry) - target_entry = edited_entry + my_list[my_list.Find(target_entry)] = edited_entry -/datum/integrated_io/list/proc/edit_in_list_by_position(mob/user, var/position) +/datum/integrated_io/lists/proc/edit_in_list_by_position(mob/user, var/position) var/list/my_list = data if(!my_list.len) to_chat(user, "The list is empty, there's nothing to modify.") return if(!position) return - var/target_entry = my_list.Find(position) + var/target_entry = my_list[position] if(target_entry) var/edited_entry = ask_for_data_type(user, target_entry) if(edited_entry) - target_entry = edited_entry + my_list[position] = edited_entry -/datum/integrated_io/list/proc/swap_inside_list(mob/user, var/first_target, var/second_target) +/datum/integrated_io/lists/proc/swap_inside_list(mob/user, var/first_target, var/second_target) var/list/my_list = data if(my_list.len <= 1) to_chat(user, "The list is empty, or too small to do any meaningful swapping.") return if(!first_target) - first_target = tgui_input_list(usr, "Which piece of data do you want to swap? (1)", "Swap", my_list) + first_target = input(user, "Which piece of data do you want to swap? (1)", "Swap") as null|anything in my_list - if(first_target) + if(holder.check_interactivity(user) && first_target) if(!second_target) - second_target = tgui_input_list(usr, "Which piece of data do you want to swap? (2)", "Swap", my_list - first_target) + second_target = input(user, "Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target - if(second_target) + if(holder.check_interactivity(user) && second_target) var/first_pos = my_list.Find(first_target) var/second_pos = my_list.Find(second_target) my_list.Swap(first_pos, second_pos) -/datum/integrated_io/list/proc/clear_list(mob/user) +/datum/integrated_io/lists/proc/clear_list(mob/user) var/list/my_list = data my_list.Cut() -/datum/integrated_io/list/scramble() +/datum/integrated_io/lists/scramble() var/list/my_list = data my_list = shuffle(my_list) push_data() -/datum/integrated_io/list/write_data_to_pin(var/new_data) +/datum/integrated_io/lists/write_data_to_pin(var/new_data) if(islist(new_data)) var/list/new_list = new_data - data = new_list.Copy() + data = new_list.Copy(max(1,new_list.len - IC_MAX_LIST_LENGTH+1),0) + holder.on_data_written() + else if(isnull(new_data)) // Clear the list + var/list/my_list = data + my_list.Cut() holder.on_data_written() -/datum/integrated_io/list/display_pin_type() +/datum/integrated_io/lists/display_pin_type() return IC_FORMAT_LIST -/datum/integrated_io/list/Topic(href, href_list, state = GLOB.always_state) +/datum/integrated_io/lists/Topic(href, href_list) if(!holder.check_interactivity(usr)) return if(..()) - return 1 + return TRUE if(href_list["add"]) add_to_list(usr) @@ -146,3 +151,4 @@ 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 index 8bc5af0e097..a1fd85589b2 100644 --- a/code/modules/integrated_electronics/core/special_pins/number_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/number_pin.dm @@ -1,7 +1,6 @@ // 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(usr, "Please type in a number.","[src] number writing") as null|num diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm index 96ab5326477..f64e15f2255 100644 --- a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm @@ -2,15 +2,8 @@ /datum/integrated_io/ref name = "ref pin" -/datum/integrated_io/ref/ask_for_pin_data(mob/user, obj/item/I) - if(istype(I, /obj/item/multitool)) - var/obj/item/multitool/tool = I - write_data_to_pin(tool.weakref_wiring) - else if(istype(I, /obj/item/integrated_electronics/debugger)) - var/obj/item/integrated_electronics/debugger/tool = I - write_data_to_pin(tool.data_to_write) - else - write_data_to_pin(null) +/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)) @@ -19,3 +12,13 @@ /datum/integrated_io/ref/display_pin_type() return IC_FORMAT_REF + +/datum/integrated_io/ref/connect_pin(datum/integrated_io/pin) + ..(pin) + if(istype(pin,/datum/integrated_io/selfref)) + write_data_to_pin(pin.data) + +/datum/integrated_io/ref/disconnect_pin(datum/integrated_io/pin) + ..(pin) + if(istype(pin,/datum/integrated_io/selfref)) + write_data_to_pin(null) diff --git a/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm new file mode 100644 index 00000000000..79dcf9e6c8c --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm @@ -0,0 +1,27 @@ +// This pin only contains its own weakref and can't be changed +/datum/integrated_io/selfref + name = "selfref pin" + +/datum/integrated_io/selfref/New() + ..() + write_data_to_pin(src) + +/datum/integrated_io/selfref/ask_for_pin_data(mob/user) // You can't clear it, it's self reference. + + +/datum/integrated_io/selfref/write_data_to_pin(var/new_data) // You can't write anything else but itself onto it + if(data) + return + data = WEAKREF(holder) + holder.on_data_written() + +/datum/integrated_io/selfref/display_pin_type() + return IC_FORMAT_REF + +/datum/integrated_io/selfref/connect_pin(datum/integrated_io/pin) + pin.write_data_to_pin(data) + ..(pin) + +/datum/integrated_io/selfref/disconnect_pin(datum/integrated_io/pin) + ..(pin) + pin.write_data_to_pin(null) diff --git a/code/modules/integrated_electronics/core/special_pins/string_pin.dm b/code/modules/integrated_electronics/core/special_pins/string_pin.dm index a0428a12de0..a863df07075 100644 --- a/code/modules/integrated_electronics/core/special_pins/string_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/string_pin.dm @@ -20,12 +20,10 @@ /datum/integrated_io/string/scramble() if(!is_valid()) return - var/string_length = length(data) - var/list/options = list("!","@","#","$","%","^","&","*") + alphabet_uppercase + var/list/options = list("!","@","#","$","%","^","&","*","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") var/new_data = "" - while(string_length) + for(var/i in 1 to length(data)) new_data += pick(options) - string_length-- push_data() /datum/integrated_io/string/display_pin_type() diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm index fd2c54cdb58..16807f6e54e 100644 --- a/code/modules/integrated_electronics/core/tools.dm +++ b/code/modules/integrated_electronics/core/tools.dm @@ -6,7 +6,7 @@ /obj/item/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. \ + 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/integrated_electronics/electronic_tools.dmi' @@ -23,67 +23,59 @@ if(!io.holder.assembly) to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.") return - if(mode == WIRE) - selected_io = io - to_chat(user, "You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.") - mode = WIRING - update_icon() - else if(mode == WIRING) - if(!selected_io) + switch(mode) + if(WIRE) + selected_io = io + to_chat(user, "You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.") + mode = WIRING + update_icon() + if(WIRING) + if(io == selected_io) + to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") + return + if(io.io_type != selected_io.io_type) + to_chat(user, "Those two types of channels are incompatible. 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.connect_pin(io) + + to_chat(user, "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].") mode = WIRE - .() - if(io == selected_io) - to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") - return - if(io.io_type != selected_io.io_type) - to_chat(user, "Those two types of channels are incompatable. The first is a [selected_io.io_type], \ - while the second is a [io.io_type].") - return - if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) - to_chat(user, "Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.") - return - selected_io.linked |= io - io.linked |= selected_io - - to_chat(user, "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].") - mode = WIRE - update_icon() - selected_io.holder.interact(user) // This is to update the UI. - selected_io = null - - else if(mode == UNWIRE) - selected_io = io - if(!io.linked.len) - to_chat(user, "There is nothing connected to \the [selected_io] data channel.") - selected_io = null - return - to_chat(user, "You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel.") - mode = UNWIRING - update_icon() - return - - else if(mode == UNWIRING) - if(!selected_io) - mode = UNWIRE - .() - if(io == selected_io) - to_chat(user, "You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \ - the same pin is rather moot.") - return - if(selected_io in io.linked) - io.linked.Remove(selected_io) - selected_io.linked.Remove(io) - to_chat(user, "You disconnect \the [selected_io.holder]'s [selected_io.name] from \ - \the [io.holder]'s [io.name].") + update_icon() selected_io.holder.interact(user) // This is to update the UI. selected_io = null - mode = UNWIRE + + if(UNWIRE) + selected_io = io + if(!io.linked.len) + to_chat(user, "There is nothing connected to \the [selected_io] data channel.") + selected_io = null + return + to_chat(user, "You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel.") + mode = UNWIRING update_icon() - else - to_chat(user, "\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \ - [io.name] are not connected.") return - return + + if(UNWIRING) + if(io == selected_io) + to_chat(user, "You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \ + the same pin is rather moot.") + return + if(selected_io in io.linked) + selected_io.disconnect_pin(io) + to_chat(user, "You disconnect \the [selected_io.holder]'s [selected_io.name] from \ + \the [io.holder]'s [io.name].") + selected_io.holder.interact(user) // This is to update the UI. + selected_io = null + mode = UNWIRE + update_icon() + else + to_chat(user, "\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \ + [io.name] are not connected.") + return /obj/item/integrated_electronics/wirer/attack_self(mob/user) switch(mode) @@ -115,36 +107,47 @@ settings to specific circuits, or for debugging purposes. It can also pulse activation pins." icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' icon_state = "debugger" - w_class = 2 + w_class = ITEMSIZE_SMALL var/data_to_write = null - var/accepting_refs = 0 + var/accepting_refs = FALSE + var/copy_values = FALSE /obj/item/integrated_electronics/debugger/attack_self(mob/user) - var/type_to_use = tgui_input_list(usr, "Please choose a type to use.","[src] type setting", list("string","number","ref", "null")) + var/type_to_use = tgui_input_list(usr, "Please choose a type to use.","[src] type setting", list("string","number","ref", "copy", "list", "null")) if(!CanInteract(user, GLOB.physical_state)) return var/new_data = null switch(type_to_use) if("string") - accepting_refs = 0 + accepting_refs = FALSE + copy_values = FALSE new_data = input(usr, "Now type in a string.","[src] string writing") as null|text new_data = sanitizeSafe(new_data, MAX_MESSAGE_LEN, 0, 0) if(istext(new_data) && CanInteract(user, GLOB.physical_state)) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to \"[new_data]\".") if("number") - accepting_refs = 0 + accepting_refs = FALSE + copy_values = FALSE new_data = input(usr, "Now type in a number.","[src] number writing") as null|num if(isnum(new_data) && CanInteract(user, GLOB.physical_state)) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to [new_data].") if("ref") - accepting_refs = 1 + accepting_refs = TRUE + copy_values = FALSE to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ an object for a ref of that object to save it in memory.") + if("copy") + accepting_refs = FALSE + copy_values = TRUE + to_chat(user, "You turn \the [src]'s value copier on. Use it on a pin \ + to save its current value in memory.") if("null") data_to_write = null + accepting_refs = FALSE + copy_values = FALSE to_chat(user, "You set \the [src]'s memory to absolutely nothing.") /obj/item/integrated_electronics/debugger/afterattack(atom/target, mob/living/user, proximity) @@ -153,23 +156,57 @@ visible_message("[user] slides \a [src]'s over \the [target].") to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ now off.") - accepting_refs = 0 + accepting_refs = FALSE /obj/item/integrated_electronics/debugger/proc/write_data(var/datum/integrated_io/io, mob/user) + //If the pin can take data: if(io.io_type == DATA_CHANNEL) + //If the debugger is set to copy, copy the data in the pin onto it + if(copy_values) + data_to_write = io.data + to_chat(user, "You let the debugger copy the data.") + copy_values = FALSE + return + + //Else, write the data to the pin io.write_data_to_pin(data_to_write) var/data_to_show = data_to_write + //This is only to convert a weakref into a name for better output if(isweakref(data_to_write)) var/datum/weakref/w = data_to_write var/atom/A = w.resolve() data_to_show = A.name to_chat(user, "You write '[data_to_write ? data_to_show : "NULL"]' to the '[io]' pin of \the [io.holder].") + + //If the pin can only be pulsed else if(io.io_type == PULSE_CHANNEL) - io.holder.check_then_do_work(ignore_power = TRUE) + io.holder.check_then_do_work(io.ord, 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/integrated_electronics/analyzer + name = "circuit analyzer" + desc = "This tool can scan an assembly and generate code necessary to recreate it in a circuit printer." + icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' + icon_state = "analyzer" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/integrated_electronics/analyzer/afterattack(var/atom/A, var/mob/living/user) + . = ..() + if(istype(A, /obj/item/electronic_assembly)) + var/obj/item/electronic_assembly/EA = A + if(!EA.opened) + to_chat(usr, SPAN_WARNING("You need to open the [A] to analyze the contents!")) + return + var/save = SScircuit.save_electronic_assembly(A) + var/saved = "[A.name] analyzed! On circuit printers with cloning enabled, you may use the code below to clone the circuit:

[save]" + if(save) + to_chat(usr, SPAN_WARNING("You scan [A].")) + user << browse(saved, "window=circuit_scan;size=500x600;border=1;can_resize=1;can_close=1;can_minimize=1") + else + to_chat(usr, SPAN_WARNING("[A] is not complete enough to be encoded!")) + @@ -278,6 +315,7 @@ /obj/item/integrated_electronics, /obj/item/tool/crowbar, /obj/item/tool/screwdriver, + /obj/item/tool/wrench, /obj/item/multitool ) cant_hold = list(/obj/item/tool/screwdriver/power) @@ -301,6 +339,7 @@ new /obj/item/multitool(src) new /obj/item/tool/screwdriver(src) new /obj/item/tool/crowbar(src) + new /obj/item/tool/wrench(src) make_exact_fit() /obj/item/storage/bag/circuits/all/PopulateContents() diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm index 4116cdfacc4..684d3879dfe 100644 --- a/code/modules/integrated_electronics/passive/power.dm +++ b/code/modules/integrated_electronics/passive/power.dm @@ -6,7 +6,7 @@ origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2, TECH_DATA = 2) category_text = "Power - Passive" -/obj/item/integrated_circuit/passive/power/proc/handle_passive_energy() +/obj/item/integrated_circuit/passive/power/proc/make_energy() return // For calculators. @@ -20,7 +20,7 @@ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH var/max_power = 1 -/obj/item/integrated_circuit/passive/power/solar_cell/handle_passive_energy() +/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) @@ -39,7 +39,7 @@ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH var/is_charge=0 -/obj/item/integrated_circuit/passive/power/starter/handle_passive_energy() +/obj/item/integrated_circuit/passive/power/starter/make_energy() if(assembly.battery) if(assembly.battery.charge) if(!is_charge) @@ -67,7 +67,7 @@ return FALSE // Robots and dead people don't have a metabolism. return TRUE -/obj/item/integrated_circuit/passive/power/metabolic_siphon/handle_passive_energy() +/obj/item/integrated_circuit/passive/power/metabolic_siphon/make_energy() var/mob/living/carbon/human/host = null if(assembly && istype(assembly, /obj/item/electronic_assembly/implant)) var/obj/item/electronic_assembly/implant/implant_assembly = assembly @@ -108,8 +108,8 @@ name = "fuel cell" desc = "Produces electricity from chemicals." icon_state = "chemical_cell" - extended_desc = "This is effectively an internal beaker. It will consume and produce power from phoron, slime jelly, welding fuel, carbon,\ - ethanol, and nutriments, in order of decreasing efficiency. It will consume fuel only if the battery can take more energy. Blood is also able to be used as \ + extended_desc = "This is effectively an internal beaker. It will consume and produce power from phoron, slime jelly, welding fuel, carbon,\ + ethanol, and nutriments, in order of decreasing efficiency. It will consume fuel only if the battery can take more energy. Blood is also able to be used as \ a source of organic fuel; blood from sapient creatures is more efficient." flags = OPENCONTAINER complexity = 4 @@ -134,7 +134,7 @@ set_pin_data(IC_OUTPUT, 1, reagents.total_volume) push_data() -/obj/item/integrated_circuit/passive/power/chemical_cell/handle_passive_energy() +/obj/item/integrated_circuit/passive/power/chemical_cell/make_energy() if(assembly) for(var/I in fuel) if(DYNAMIC_CELL_UNITS_TO_W(assembly.battery.maxcharge - assembly.battery.charge, 1) > fuel[I]) @@ -161,7 +161,7 @@ spawn_flags = IC_SPAWN_RESEARCH power_amount = 2000 -/obj/item/integrated_circuit/passive/power/relay/handle_passive_energy() +/obj/item/integrated_circuit/passive/power/relay/make_energy() if(!assembly) return var/area/A = get_area(src) @@ -178,8 +178,8 @@ desc = "Gives or takes power from a wire underneath the machine." icon_state = "powernet" extended_desc = "The assembly must be anchored, with a wrench, and a wire node must be avaiable directly underneath.
\ - The first pin determines if power is moved at all. The second pin, if true, will draw from the powernet to charge the assembly's \ - cell, otherwise it will give power from the cell to the powernet. All units are in kilowatts." + The first pin determines if power is moved at all. The second pin, if true, will draw from the powernet to charge the assembly's \ + cell, otherwise it will give power from the cell to the powernet. All units are in kilowatts." complexity = 20 inputs = list( "active" = IC_PINTYPE_BOOLEAN, @@ -210,7 +210,7 @@ /obj/item/integrated_circuit/passive/power/powernet/on_unanchored() IO.disconnect_from_network() -/obj/item/integrated_circuit/passive/power/powernet/handle_passive_energy() +/obj/item/integrated_circuit/passive/power/powernet/make_energy() if(assembly && assembly.anchored && assembly.battery) var/should_act = get_pin_data(IC_INPUT, 1) // Even if this is false, we still need to update the output pins with powernet information. var/drawing = get_pin_data(IC_INPUT, 2) diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm index 192d317baff..63a4aea4420 100644 --- a/code/modules/integrated_electronics/subtypes/arithmetic.dm +++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm @@ -29,10 +29,10 @@ /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 + for(var/k in 1 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(isnum(I)) + result += I set_pin_data(IC_OUTPUT, 1, result) push_data() @@ -55,12 +55,10 @@ 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 + for(var/k in 2 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(isnum(I)) + result -= I set_pin_data(IC_OUTPUT, 1, result) push_data() @@ -83,12 +81,10 @@ 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 + for(var/k in 2 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(isnum(I)) + result *= I set_pin_data(IC_OUTPUT, 1, result) push_data() @@ -98,7 +94,7 @@ /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!" + desc = "This circuit can divide numbers. Don't even 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." @@ -111,12 +107,10 @@ 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 + for(var/k in 2 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(isnum(I) && (I != 0)) + result /= I set_pin_data(IC_OUTPUT, 1, result) push_data() @@ -146,8 +140,8 @@ /obj/item/integrated_circuit/arithmetic/sign name = "sign circuit" - desc = "This will say if a number is positive, negative, or zero." - extended_desc = "Will output 1, -1, or 0, depending on if A is a postive number, a negative number, or zero, respectively." + desc = "This circuit will state if a number is positive, negative, or zero." + extended_desc = "Will output 1, -1, or 0, depending on if A is a positive number, a negative number, or zero, respectively." icon_state = "sign" inputs = list("A" = IC_PINTYPE_NUMBER) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -191,7 +185,6 @@ push_data() activate_pin(2) - // Absolute // /obj/item/integrated_circuit/arithmetic/absolute @@ -203,8 +196,8 @@ /obj/item/integrated_circuit/arithmetic/absolute/do_work() var/result = 0 - for(var/datum/integrated_io/I in inputs) - I.pull_data() + for(var/k in 1 to inputs.len) + var/datum/integrated_io/I = inputs[k] if(isnum(I.data)) result = abs(I.data) @@ -216,19 +209,19 @@ /obj/item/integrated_circuit/arithmetic/average name = "average circuit" - desc = "This circuit is of average quality, however it will compute the average for numbers you give it." - extended_desc = "Note that null pins are ignored, where as a pin containing 0 is included in the averaging calculation." + desc = "This circuit is of average quality. It will compute the average of the numbers you give it." + extended_desc = "Note that null pins are ignored, whereas a pin containing 0 is included in the averaging calculation." icon_state = "average" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH /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)) + for(var/k in 2 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(isnum(I)) inputs_used++ - result = result + I.data + result += I if(inputs_used) result = result / inputs_used @@ -244,9 +237,14 @@ icon_state = "pi" inputs = list() spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH +/* TBI +/obj/item/integrated_circuit/arithmetic/pi/Initialize(mapload) + . = ..() + desc = "Not recommended for cooking. Outputs '[PI]' when it receives a pulse." +*/ /obj/item/integrated_circuit/arithmetic/pi/do_work() - set_pin_data(IC_OUTPUT, 1, 3.14159) + set_pin_data(IC_OUTPUT, 1, 3.14159) //TBI Insert PI push_data() activate_pin(2) @@ -254,7 +252,7 @@ /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 \ + 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) @@ -276,17 +274,15 @@ /obj/item/integrated_circuit/arithmetic/square_root name = "square root circuit" - desc = "This outputs the square root of a number you put in." + desc = "This outputs the square root of the number input." 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) + var/I = get_pin_data(IC_INPUT, 1) + result = sqrt(I) set_pin_data(IC_OUTPUT, 1, result) push_data() diff --git a/code/modules/integrated_electronics/subtypes/built_in.dm b/code/modules/integrated_electronics/subtypes/built_in.dm index 8bfdda0d36e..53a1c2c030f 100644 --- a/code/modules/integrated_electronics/subtypes/built_in.dm +++ b/code/modules/integrated_electronics/subtypes/built_in.dm @@ -5,12 +5,12 @@ icon_state = "template" size = -1 w_class = ITEMSIZE_TINY - removable = FALSE // Determines if a circuit is removable from the assembly. + removable = FALSE /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'. + complexity = 0 activators = list("on pulsed" = IC_PINTYPE_PULSE_OUT) /obj/item/integrated_circuit/built_in/device_input/do_work() @@ -19,7 +19,7 @@ /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'. + complexity = 0 activators = list("pulse attached" = IC_PINTYPE_PULSE_IN) /obj/item/integrated_circuit/built_in/device_output/do_work() @@ -37,3 +37,18 @@ /obj/item/integrated_circuit/built_in/action_button/do_work() activate_pin(1) + +/obj/item/integrated_circuit/built_in/self_sensor + name = "self sensor" + desc = "This chip identifies the user." + extended_desc = "An integrated sensor that allows integrated circuitry to directly interfere with the wearer of the device." + complexity = 0 + outputs = list("user" = IC_PINTYPE_REF) + activators = list("set" = IC_PINTYPE_PULSE_IN, "on activation" = IC_PINTYPE_PULSE_OUT) + +/obj/item/integrated_circuit/built_in/self_sensor/do_work() + set_pin_data(IC_OUTPUT, 1, assembly.loc.loc) + push_data() + activate_pin(2) + . = ..() + diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm index 52bd8f2515f..a15db65f7a2 100644 --- a/code/modules/integrated_electronics/subtypes/converters.dm +++ b/code/modules/integrated_electronics/subtypes/converters.dm @@ -10,7 +10,7 @@ /obj/item/integrated_circuit/converter/num2text name = "number to string" desc = "This circuit can convert a number variable into a string." - extended_desc = "Because of game limitations null/false variables will output a '0' string." + extended_desc = "Because of circuit 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) @@ -18,7 +18,6 @@ /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) @@ -39,7 +38,7 @@ /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) @@ -58,7 +57,7 @@ /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 @@ -69,7 +68,7 @@ /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." + desc = "This circuit can encode a reference into a string, which can then be read by a reference decoder circuit." icon_state = "ref-string" inputs = list("input" = IC_PINTYPE_REF) outputs = list("output" = IC_PINTYPE_STRING) @@ -77,33 +76,32 @@ /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]" - + result = num2hex(4,5) + result = strtohex(XorEncrypt(REF(A), SScircuit.cipherkey)) set_pin_data(IC_OUTPUT, 1, result) push_data() activate_pin(2) /obj/item/integrated_circuit/converter/refdecode name = "reference decoder" - desc = "This circuit can convert an encoded reference to actual reference." + desc = "This circuit can convert an encoded reference to an actual reference." icon_state = "ref-string" inputs = list("input" = IC_PINTYPE_STRING) outputs = list("output" = IC_PINTYPE_REF) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - + var/dec /obj/item/integrated_circuit/converter/refdecode/do_work() - pull_data() - set_pin_data(IC_OUTPUT, 1, WEAKREF(locate(get_pin_data(IC_INPUT, 1)))) + dec = XorEncrypt(hextostr(get_pin_data(IC_INPUT, 1), TRUE), SScircuit.cipherkey) + set_pin_data(IC_OUTPUT, 1, WEAKREF(locate(dec))) push_data() activate_pin(2) /obj/item/integrated_circuit/converter/lowercase name = "lowercase string converter" - desc = "this will cause a string to come out in all lowercase." + desc = "This circuit 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) @@ -111,7 +109,7 @@ /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) @@ -130,7 +128,7 @@ /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) @@ -141,38 +139,66 @@ /obj/item/integrated_circuit/converter/concatenator name = "concatenator" - desc = "This joins many strings together to get one big string." + desc = "This circuit joins up to 8 strings together to get a string with a maximum of 2048 characters." 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 - ) + inputs = list() 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 + var/number_of_pins = 8 + var/max_string_length = 2048 + +/obj/item/integrated_circuit/converter/concatenator/Initialize(mapload) + for(var/i = 1 to number_of_pins) + inputs["input [i]"] = IC_PINTYPE_STRING + . = ..() /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 + var/spamprotection + for(var/k in 1 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(!isnull(I)) + if((result ? length(result) : 0) + length(I) > max_string_length) + spamprotection = (result ? length(result) : 0) + length(I) + break + result = result + I + + if(spamprotection >= max_string_length*2 && assembly) + if(assembly.fingerprintslast) + var/mob/M = get_mob_by_key(assembly.fingerprintslast) + var/more = "" + if(M) + more = "[ADMIN_LOOKUPFLW(M)] " + message_admins("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. Assembly last touched by [more ? more : assembly.fingerprintslast].") + investigate_log("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. Assembly last touched by [assembly.fingerprintslast].", INVESTIGATE_CIRCUIT) + else + message_admins("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. No associated key.") + investigate_log("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. No associated key.", INVESTIGATE_CIRCUIT) + result = "ERROR" set_pin_data(IC_OUTPUT, 1, result) push_data() activate_pin(2) +/obj/item/integrated_circuit/converter/concatenator/small + name = "small concatenator" + desc = "This circuit joins up to 4 strings together to get a string with a maximum of 2048 characters." + complexity = 2 + number_of_pins = 4 + max_string_length = 2048 + +/obj/item/integrated_circuit/converter/concatenator/large + name = "large concatenator" + desc = "This circuit joins up to 16 strings together to get a string with a maximum of 2048 characters." + complexity = 6 + number_of_pins = 16 + max_string_length = 2048 /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' \ + desc = "This splits a single string into two at the relative split point." + extended_desc = "This circuit 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( @@ -186,7 +212,6 @@ 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) @@ -204,9 +229,10 @@ /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." + desc = "This circuit outputs the position of the 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, inputting 'my wife has caught on fire' with 'has' as the sample will give you position 9. \ + This circuit isn't case sensitive, and it does not ignore spaces." complexity = 4 inputs = list( "string" = IC_PINTYPE_STRING, @@ -215,14 +241,41 @@ outputs = list( "position" = IC_PINTYPE_NUMBER ) - activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT) + activators = list( + "search" = IC_PINTYPE_PULSE_IN, + "after search" = IC_PINTYPE_PULSE_OUT, + "found" = IC_PINTYPE_PULSE_OUT, + "not found" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH /obj/item/integrated_circuit/converter/findstring/do_work() + var/position = findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) - set_pin_data(IC_OUTPUT, 1, findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) ) + set_pin_data(IC_OUTPUT, 1, position) + push_data() + activate_pin(2) + if(position) + activate_pin(3) + else + activate_pin(4) + +/obj/item/integrated_circuit/converter/stringlength + name = "get length" + desc = "This circuit will return the number of characters in a string." + complexity = 1 + inputs = list( + "string" = IC_PINTYPE_STRING + ) + outputs = list( + "length" = IC_PINTYPE_NUMBER + ) + activators = list("get length" = IC_PINTYPE_PULSE_IN, "on acquisition" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/stringlength/do_work() + set_pin_data(IC_OUTPUT, 1, length(get_pin_data(IC_INPUT, 1))) push_data() activate_pin(2) @@ -230,8 +283,9 @@ /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')." + 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'). Leave the delimiter null to get a list \ + of every individual character." complexity = 4 inputs = list( "string to split" = IC_PINTYPE_STRING, @@ -243,14 +297,14 @@ 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 )) + var/string = get_pin_data(IC_INPUT, 1) + var/delimiter = get_pin_data(IC_INPUT, 2) + if(delimiter == null) + set_pin_data(IC_OUTPUT, 1, text2charlist(string)) + else + set_pin_data(IC_OUTPUT, 1, splittext(string, delimiter)) push_data() - activate_pin(2) /obj/item/integrated_circuit/converter/radians2degrees @@ -262,7 +316,7 @@ /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) @@ -280,7 +334,7 @@ /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) @@ -293,7 +347,8 @@ /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 + extended_desc = "Keep in mind that both sets of input coordinates should be absolute." + complexity = 1 inputs = list( "X1" = IC_PINTYPE_NUMBER, "Y1" = IC_PINTYPE_NUMBER, @@ -321,23 +376,69 @@ push_data() activate_pin(2) -/obj/item/integrated_circuit/converter/stringlength - name = "len circuit" - desc = "This circuit will return the number of characters in a string." +/obj/item/integrated_circuit/converter/rel_to_abs_coords + name = "rel to abs coordinate converter" + desc = "Convert relative coordinates to absolute coordinates with this." + extended_desc = "Keep in mind that only one set of input coordinates should be absolute, and the other relative. \ + The output coordinates will be the absolute form of the input relative coordinates." complexity = 1 inputs = list( - "string" = IC_PINTYPE_STRING + "X1" = IC_PINTYPE_NUMBER, + "Y1" = IC_PINTYPE_NUMBER, + "X2" = IC_PINTYPE_NUMBER, + "Y2" = IC_PINTYPE_NUMBER ) outputs = list( - "length" = IC_PINTYPE_NUMBER + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER ) - activators = list("get length" = IC_PINTYPE_PULSE_IN, "on acquisition" = IC_PINTYPE_PULSE_OUT) + activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/converter/stringlength/do_work() - set_pin_data(IC_OUTPUT, 1, length(get_pin_data(IC_INPUT, 1))) - push_data() +/obj/item/integrated_circuit/converter/rel_to_abs_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) + +/obj/item/integrated_circuit/converter/adv_rel_to_abs_coords + name = "advanced rel to abs coordinate converter" + desc = "Easily convert relative coordinates to absolute coordinates with this." + extended_desc = "This circuit only requires a single set of relative inputs to output absolute coordinates." + complexity = 2 + inputs = list( + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER, + ) + outputs = list( + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER + ) + activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/adv_rel_to_abs_coords/do_work() + var/turf/T = get_turf(src) + + if(!T) + return + + var/x1 = get_pin_data(IC_INPUT, 1) + var/y1 = get_pin_data(IC_INPUT, 2) + + if(!isnull(x1) && !isnull(y1)) + set_pin_data(IC_OUTPUT, 1, T.x + x1) + set_pin_data(IC_OUTPUT, 2, T.y + y1) + + push_data() activate_pin(2) /obj/item/integrated_circuit/converter/hsv2hex @@ -356,7 +457,7 @@ /obj/item/integrated_circuit/converter/hsv2hex/do_work() var/result = null - pull_data() + var/hue = get_pin_data(IC_INPUT, 1) var/saturation = get_pin_data(IC_INPUT, 2) var/value = get_pin_data(IC_INPUT, 3) @@ -370,7 +471,7 @@ /obj/item/integrated_circuit/converter/rgb2hex name = "rgb to hexadecimal converter" desc = "This circuit can convert a RGB (Red, Green, Blue) color to a Hexadecimal RGB color." - extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. They all go from 0-255." + extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. They all go from 0-255." icon_state = "rgb-hex" inputs = list( "red" = IC_PINTYPE_NUMBER, @@ -382,7 +483,6 @@ /obj/item/integrated_circuit/converter/rgb2hex/do_work() var/result = null - pull_data() var/red = get_pin_data(IC_INPUT, 1) var/green = get_pin_data(IC_INPUT, 2) var/blue = get_pin_data(IC_INPUT, 3) diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm index 78507ffecbc..d3ca3e7c816 100644 --- a/code/modules/integrated_electronics/subtypes/data_transfer.dm +++ b/code/modules/integrated_electronics/subtypes/data_transfer.dm @@ -4,8 +4,8 @@ /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. \ + 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" @@ -52,8 +52,8 @@ /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. \ + 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" @@ -75,8 +75,10 @@ /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) + if(!isnull(output_index) && (output_index >= 1 && output_index <= outputs.len)) + var/datum/integrated_io/O = outputs[output_index] + O.data = get_pin_data(IC_INPUT, 2) + O.push_data() activate_pin(2) @@ -100,7 +102,7 @@ /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. \ + 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" @@ -142,3 +144,65 @@ icon_state = "dmux16" w_class = ITEMSIZE_SMALL number_of_outputs = 16 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer + name = "two pulse multiplexer" + desc = "Pulse in pins to choose the pin value to be sent." + extended_desc = "The input pulses are used to select which of the input pins has its data moved to the output." + complexity = 2 + icon_state = "dmux2" + inputs = list() + outputs = list("output" = IC_PINTYPE_ANY) + activators = list("on selected" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + var/number_of_pins = 2 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/Initialize(mapload) + for(var/i = 1 to number_of_pins) + inputs["input [i]"] = IC_PINTYPE_ANY + for(var/i = 1 to number_of_pins) + activators["input [i]"] = IC_PINTYPE_PULSE_IN + complexity = number_of_pins + + . = ..() + desc += " It has [number_of_pins] pulse in pins and [number_of_pins] output pins." + extended_desc += " This pulse multiplexer has a range from 1 to [activators.len - 1]." + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/do_work(ord) + var/input_index = ord - 2 + + if(!isnull(input_index) && (input_index >= 0 && input_index < inputs.len)) + set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1)) + push_data() + activate_pin(1) + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/medium + name = "four pulse multiplexer" + icon_state = "dmux4" + number_of_pins = 4 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/large + name = "eight pulse multiplexer" + icon_state = "dmux8" + w_class = WEIGHT_CLASS_SMALL + number_of_pins = 8 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/huge + name = "sixteen pulse multiplexer" + icon_state = "dmux16" + w_class = WEIGHT_CLASS_SMALL + number_of_pins = 16 + +/obj/item/integrated_circuit/transfer/wire_node + name = "wire node" + desc = "Just a wire node to make wiring easier. Transfers the pulse from in to out." + icon_state = "wire_node" + activators = list("pulse in" = IC_PINTYPE_PULSE_IN, "pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 0 + complexity = 0 + size = 0.1 + +/obj/item/integrated_circuit/transfer/wire_node/do_work() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/filters.dm b/code/modules/integrated_electronics/subtypes/filters.dm new file mode 100644 index 00000000000..0bb02a476ff --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/filters.dm @@ -0,0 +1,127 @@ +/obj/item/integrated_circuit/filter + category_text = "Filter" + power_draw_per_use = 5 + complexity = 2 + activators = list("compare" = IC_PINTYPE_PULSE_IN, "if valid" = IC_PINTYPE_PULSE_OUT, "if not valid" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + icon = 'icons/obj/electronic_assemblies.dmi' + +/obj/item/integrated_circuit/filter/proc/may_pass(var/input) + return FALSE + +/obj/item/integrated_circuit/filter/do_work() + push_data() + +/obj/item/integrated_circuit/filter/ref + extended_desc = "Uses heuristics and complex algorithms to match incoming data against its filtering parameters and occasionally produces both false positives and negatives." + var/filter_type + complexity = 4 + inputs = list( "input" = IC_PINTYPE_REF ) + outputs = list("result" = IC_PINTYPE_BOOLEAN) + +/obj/item/integrated_circuit/filter/ref/may_pass(var/weakref/data) + if(!(filter_type && isweakref(data))) + return FALSE + var/weakref/wref = data + return istype(wref.resolve(), filter_type) + +/obj/item/integrated_circuit/filter/ref/do_work() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = may_pass(A.data) ? TRUE : FALSE + + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + ..() + +/obj/item/integrated_circuit/filter/ref/mob + name = "life filter" + desc = "Only allow refs belonging to more complex, currently or formerly, living but not necessarily biological entities through." + icon_state = "filter_mob" + filter_type = /mob/living + +/obj/item/integrated_circuit/filter/ref/mob/humanoid + name = "humanoid filter" + desc = "Only allow refs belonging to humanoids (dead or alive) through." + icon_state = "filter_humanoid" + filter_type = /mob/living/carbon/human + +/obj/item/integrated_circuit/filter/ref/obj + name = "object filter" + desc = "Allows most kinds of refs to pass, as long as they are not considered (once) living entities." + icon_state = "filter_obj" + filter_type = /obj + +/obj/item/integrated_circuit/filter/ref/obj/item + name = "item filter" + desc = "Only allow refs belonging to minor items through, typically hand-held such." + icon_state = "filter_item" + filter_type = /obj/item + +/obj/item/integrated_circuit/filter/ref/obj/machinery + name = "machinery filter" + desc = "Only allow refs belonging machinery or complex objects through, such as computers and consoles." + icon_state = "filter_machinery" + filter_type = /obj/machinery + +/obj/item/integrated_circuit/filter/ref/object/structure + name = "structure filter" + desc = "Only allow refs belonging larger objects and structures through, such as closets and beds." + icon_state = "filter_structure" + filter_type = /obj/structure + +/obj/item/integrated_circuit/filter/ref/custom + name = "custom filter" + desc = "Allows custom filtering. It will match type against a stored reference." + icon_state = "filter_custom" + inputs = list( "input" = IC_PINTYPE_REF, "expected type" = IC_PINTYPE_REF ) + +/obj/item/integrated_circuit/filter/ref/custom/may_pass(var/weakref/data, var/weakref/typedata) + if(!isweakref(data) || !isweakref(typedata)) + return FALSE + var/weakref/wref = data + var/weakref/wref2 = typedata + var/atom/A = wref.resolve() + var/atom/B = wref2.resolve() + return (A && B && (istype(A, B.type))) + +/obj/item/integrated_circuit/filter/ref/custom/do_work() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/T = inputs[2] + var/datum/integrated_io/O = outputs[1] + O.data = may_pass(A.data, T.data) ? TRUE : FALSE + + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + push_data() + +/obj/item/integrated_circuit/filter/string + name = "string filter" + desc = "Allows string filtering. It will match a string against a stored string." + extended_desc = "Matches incoming data against its filtering parameters and occasionally produces both false positives and negatives." + icon_state = "filter_string" + complexity = 2 + inputs = list( + "input" = IC_PINTYPE_STRING, + "expected string" = IC_PINTYPE_STRING + ) + outputs = list("result" = IC_PINTYPE_BOOLEAN) + +/obj/item/integrated_circuit/filter/string/may_pass(var/datum/integrated_io/A, var/datum/integrated_io/B) + return A.data == B.data + +/obj/item/integrated_circuit/filter/string/do_work() + var/datum/integrated_io/A = inputs[1] + var/datum/integrated_io/B = inputs[2] + var/datum/integrated_io/O = outputs[1] + O.data = may_pass(A, B) ? TRUE : FALSE + + if(get_pin_data(IC_OUTPUT, 1)) + activate_pin(2) + else + activate_pin(3) + push_data() diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index cfe512c8fff..0a3d3b60048 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -1,5 +1,4 @@ /obj/item/integrated_circuit/input - var/can_be_asked_input = 0 category_text = "Input" power_draw_per_use = 5 @@ -20,7 +19,7 @@ /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.displayed_name]'.") + to_chat(user, SPAN_NOTICE("You press the button labeled '[src.displayed_name]'.")) activate_pin(1) /obj/item/integrated_circuit/input/toggle_button @@ -53,8 +52,8 @@ 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", get_pin_data(IC_OUTPUT, 1)) as null|num - if(isnum(new_input) && CanInteract(user, GLOB.physical_state)) + var/new_input = input(user, "Enter a number, please.","Number pad", null) as null|num + if(isnum(new_input)) set_pin_data(IC_OUTPUT, 1, new_input) push_data() activate_pin(1) @@ -72,8 +71,8 @@ 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", get_pin_data(IC_OUTPUT, 1)) as null|text - if(istext(new_input) && CanInteract(user, GLOB.physical_state)) + var/new_input = input(user, "Enter some words, please.","Number pad", null) as null|text + if(istext(new_input)) set_pin_data(IC_OUTPUT, 1, new_input) push_data() activate_pin(1) @@ -92,11 +91,48 @@ /obj/item/integrated_circuit/input/colorpad/ask_for_input(mob/user) var/new_color = input(user, "Enter a color, please.", "Color pad", get_pin_data(IC_OUTPUT, 1)) as color|null - if(new_color && CanInteract(user, GLOB.physical_state)) + if(new_color) set_pin_data(IC_OUTPUT, 1, new_color) push_data() activate_pin(1) +/obj/item/integrated_circuit/input/card_reader + name = "ID card reader" //To differentiate it from the data card reader + desc = "A circuit that can read the registred name, assignment, and PassKey string from an ID card." + icon_state = "card_reader" + + complexity = 4 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + outputs = list( + "registered name" = IC_PINTYPE_STRING, + "assignment" = IC_PINTYPE_STRING, + ) + activators = list( + "on read" = IC_PINTYPE_PULSE_OUT + ) + +/obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent) + var/obj/item/card/id/card = I.GetID() + var/list/access = I.GetAccess() + + if(assembly) + assembly.access_card.access |= access + + if(card) // An ID card. + set_pin_data(IC_OUTPUT, 1, card.registered_name) + set_pin_data(IC_OUTPUT, 2, card.assignment) + + else if(length(access)) // A non-card object that has access levels. + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + + else + return FALSE + + push_data() + activate_pin(1) + return TRUE + /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." @@ -104,27 +140,30 @@ complexity = 4 inputs = list("target" = IC_PINTYPE_REF) outputs = list( - "total health %" = IC_PINTYPE_NUMBER, - "total missing health" = IC_PINTYPE_NUMBER + "total health %" = IC_PINTYPE_NUMBER, + "total missing health" = IC_PINTYPE_NUMBER, + "pulse" = 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 +/obj/item/integrated_circuit/input/med_scanner/do_work(ord) + if(ord == 1) + 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) + set_pin_data(IC_OUTPUT, 1, total_health) + set_pin_data(IC_OUTPUT, 2, missing_health) + set_pin_data(IC_OUTPUT, 3, H.pulse) - push_data() - activate_pin(2) + push_data() + activate_pin(2) @@ -138,156 +177,387 @@ complexity = 12 inputs = list("target" = IC_PINTYPE_REF) outputs = list( + "brain activity" = IC_PINTYPE_BOOLEAN, + "is conscious" = IC_PINTYPE_BOOLEAN, "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 + "clone damage" = IC_PINTYPE_NUMBER, + "blood loss" = IC_PINTYPE_NUMBER, + "pain level" = IC_PINTYPE_NUMBER, + "radiation" = 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 +/obj/item/integrated_circuit/input/adv_med_scanner/do_work(ord) + if(ord == 1) + 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 medibot's analyzer it can be used in range.. + if(H in view(get_turf(H))) // Like medibot'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 + 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()) + set_pin_data(IC_OUTPUT, 1, (H.has_brain() && H.stat != DEAD)) + set_pin_data(IC_OUTPUT, 2, (H.stat == 0)) + set_pin_data(IC_OUTPUT, 3, total_health) + set_pin_data(IC_OUTPUT, 4, missing_health) + set_pin_data(IC_OUTPUT, 5, H.getBruteLoss()) + set_pin_data(IC_OUTPUT, 6, H.getFireLoss()) + set_pin_data(IC_OUTPUT, 7, H.getToxLoss()) + set_pin_data(IC_OUTPUT, 8, H.getOxyLoss()) + set_pin_data(IC_OUTPUT, 9, H.getCloneLoss()) + set_pin_data(IC_OUTPUT, 10, round((H.vessel.get_reagent_amount("blood") / H.species.blood_volume)*100)) + set_pin_data(IC_OUTPUT, 11, H.traumatic_shock) + set_pin_data(IC_OUTPUT, 12, H.radiation) - push_data() - activate_pin(2) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/slime_scanner + name = "slime scanner" + desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non-associative." + icon_state = "medscan_adv" + complexity = 12 + inputs = list("target" = IC_PINTYPE_REF) + outputs = list( + "colour" = IC_PINTYPE_STRING, + "adult" = IC_PINTYPE_BOOLEAN, + "nutrition" = IC_PINTYPE_NUMBER, + "charge" = IC_PINTYPE_NUMBER, + "health" = IC_PINTYPE_NUMBER, + "possible mutation" = IC_PINTYPE_LIST, + "genetic destability" = IC_PINTYPE_NUMBER, + "slime core amount" = IC_PINTYPE_NUMBER, + "Growth progress" = IC_PINTYPE_NUMBER, + ) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 80 + +/obj/item/integrated_circuit/input/slime_scanner/do_work(ord) + if(ord == 1) + var/mob/living/simple_mob/slime/xenobio/T = get_pin_data_as_type(IC_INPUT, 1, /mob/living/simple_mob/slime) + if(!isslime(T)) //Invalid input + return + if(T in view(get_turf(src))) // Like medbot's analyzer it can be used in range.. + + set_pin_data(IC_OUTPUT, 1, T.slime_color) + set_pin_data(IC_OUTPUT, 2, T.is_adult) + set_pin_data(IC_OUTPUT, 3, T.nutrition/T.get_max_nutrition()) + set_pin_data(IC_OUTPUT, 4, T.power_charge) + set_pin_data(IC_OUTPUT, 5, round(T.health/T.maxHealth,0.01)*100) + set_pin_data(IC_OUTPUT, 6, uniqueList(T.slime_mutation)) + set_pin_data(IC_OUTPUT, 7, T.mutation_chance) + set_pin_data(IC_OUTPUT, 8, T.cores) + set_pin_data(IC_OUTPUT, 9, T.amount_grown/1000) + + + 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,\ + 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, + "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, + "amount of reagents" = IC_PINTYPE_NUMBER, + "density" = IC_PINTYPE_BOOLEAN, + "opacity" = IC_PINTYPE_BOOLEAN, + "occupied turf" = IC_PINTYPE_REF ) 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 +/obj/item/integrated_circuit/input/examiner/do_work(ord) + if(ord == 1) + var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/turf/T = get_turf(src) - if(H in view(T)) // This is a camera. It can't examine thngs,that it can't see. + if(!istype(H) || !(H in view(T))) + activate_pin(3) + else + set_pin_data(IC_OUTPUT, 1, H.name) + set_pin_data(IC_OUTPUT, 2, H.desc) + + if(istype(H, /mob/living)) + var/msg = H.examine(H) + if(msg) + set_pin_data(IC_OUTPUT, 2, msg) + + 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) + set_pin_data(IC_OUTPUT, 8, H.density) + set_pin_data(IC_OUTPUT, 9, H.opacity) + set_pin_data(IC_OUTPUT, 10, get_turf(H)) + push_data() + activate_pin(2) - 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) + +/obj/item/integrated_circuit/input/turfpoint + name = "Tile pointer" + desc = "This circuit will get a tile ref with the provided absolute coordinates." + extended_desc = "If the machine cannot see the target, it will not be able to calculate the correct direction.\ + This circuit only works while inside an assembly." + icon_state = "numberpad" + complexity = 5 + inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER) + outputs = list("tile" = IC_PINTYPE_REF) + activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 40 + +/obj/item/integrated_circuit/input/turfpoint/do_work(ord) + if(ord == 1) + if(!assembly) + activate_pin(3) + return + var/turf/T = get_turf(assembly) + var/target_x = clamp(get_pin_data(IC_INPUT, 1), 0, world.maxx) + var/target_y = clamp(get_pin_data(IC_INPUT, 2), 0, world.maxy) + var/turf/A = locate(target_x, target_y, T.z) + set_pin_data(IC_OUTPUT, 1, null) + if(!A || !(A in view(T))) + activate_pin(3) + return + else + set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) push_data() activate_pin(2) - else - activate_pin(3) + +/obj/item/integrated_circuit/input/turfscan + name = "tile analyzer" + desc = "This circuit can analyze the contents of the scanned turf, and can read letters on the turf." + icon_state = "video_camera" + complexity = 5 + inputs = list( + "target" = IC_PINTYPE_REF + ) + outputs = list( + "located ref" = IC_PINTYPE_LIST, + "Written letters" = IC_PINTYPE_STRING, + "area" = IC_PINTYPE_STRING + ) + activators = list( + "scan" = IC_PINTYPE_PULSE_IN, + "on scanned" = IC_PINTYPE_PULSE_OUT, + "not scanned" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 40 + cooldown_per_use = 10 + +/obj/item/integrated_circuit/input/turfscan/do_work(ord) + if(ord == 1) + var/turf/scanned_turf = get_pin_data_as_type(IC_INPUT, 1, /turf) + var/turf/circuit_turf = get_turf(src) + var/area_name = get_area_name(scanned_turf) + if(!istype(scanned_turf)) //Invalid input + activate_pin(3) + return + + if(scanned_turf in view(circuit_turf)) // This is a camera. It can't examine things that it can't see. + var/list/turf_contents = new() + for(var/obj/U in scanned_turf) + turf_contents += WEAKREF(U) + for(var/mob/U in scanned_turf) + turf_contents += WEAKREF(U) + set_pin_data(IC_OUTPUT, 1, turf_contents) + set_pin_data(IC_OUTPUT, 3, area_name) + var/list/St = new() + for(var/obj/effect/decal/cleanable/crayon/I in scanned_turf) + St.Add(I.icon_state) + if(St.len) + set_pin_data(IC_OUTPUT, 2, jointext(St, ",", 1, 0)) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/turfpoint + name = "tile pointer" + desc = "This circuit will get tile ref with given absolute coorinates." + extended_desc = "If the machine cannot see the target, it will not be able to scan it.\ + This circuit will only work in an assembly." + icon_state = "numberpad" + complexity = 5 + inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER) + outputs = list("tile" = IC_PINTYPE_REF) + activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT,"not scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 40 + +/obj/item/integrated_circuit/input/turfpoint/do_work(ord) + if(ord == 1) + if(!assembly) + activate_pin(3) + return + var/turf/T = get_turf(assembly) + var/target_x = clamp(get_pin_data(IC_INPUT, 1), 0, world.maxx) + var/target_y = clamp(get_pin_data(IC_INPUT, 2), 0, world.maxy) + var/turf/A = locate(target_x, target_y, T.z) + set_pin_data(IC_OUTPUT, 1, null) + if(!A || !(A in view(T))) + activate_pin(3) + return + else + set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/turfscan + name = "tile analyzer" + desc = "This machine vision system can analyze contents of desired tile and read letters on the floor." + icon_state = "video_camera" + complexity = 5 + inputs = list( + "target" = IC_PINTYPE_REF + ) + outputs = list( + "located contents" = IC_PINTYPE_LIST, + "Written letters" = IC_PINTYPE_STRING + ) + activators = list( + "scan" = IC_PINTYPE_PULSE_IN, + "on scanned" = IC_PINTYPE_PULSE_OUT, + "not scanned" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 40 + cooldown_per_use = 10 + +/obj/item/integrated_circuit/input/turfscan/do_work(ord) + if(ord == 1) + var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom) + var/turf/T = get_turf(src) + var/turf/E = get_turf(H) + 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. + var/list/cont = new() + if(E.contents.len) + for(var/i = 1 to E.contents.len) + var/atom/U = E.contents[i] + cont += WEAKREF(U) + set_pin_data(IC_OUTPUT, 1, cont) + var/list/St = new() + for(var/obj/effect/decal/cleanable/crayon/I in E.contents) + St.Add(I.icon_state) + if(St.len) + set_pin_data(IC_OUTPUT, 2, jointext(St, ",", 1, 0)) + 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) + inputs = list("creatures only" = IC_PINTYPE_BOOLEAN) + outputs = list( + "located ref" = IC_PINTYPE_REF, + "is ground" = IC_PINTYPE_BOOLEAN, + "is creature" = IC_PINTYPE_BOOLEAN) + activators = list( + "locate" = IC_PINTYPE_PULSE_IN, + "on scanned" = IC_PINTYPE_PULSE_OUT) 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. +/obj/item/integrated_circuit/input/local_locator/do_work(ord) + if(ord == 1) + var/datum/integrated_io/O = outputs[1] + O.data = null + if(!get_pin_data(IC_INPUT, 1)) // Check toggle. We can just grab ref if false. O.data = WEAKREF(assembly.loc) + else if(get_pin_data(IC_INPUT, 1) && istype(assembly.loc, /mob/living)) // Now check if someone's holding us. + O.data = WEAKREF(assembly.loc) + istype(O.data, /obj/item/electronic_assembly/clothing) ? (O.data = WEAKREF(O.data)) : null + set_pin_data(IC_OUTPUT, 2, isturf(assembly.loc)) + set_pin_data(IC_OUTPUT, 3, ismob(assembly.loc)) + push_data() + activate_pin(2) - 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 \ + extended_desc = "The first pin requires a ref to the 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") + inputs = list("desired type ref" = IC_PINTYPE_REF) + outputs = list("located ref" = IC_PINTYPE_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 +/obj/item/integrated_circuit/input/adjacent_locator/do_work(ord) + if(ord == 1) + 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 + 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() + 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 7 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." + 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") + outputs = list("located ref" = IC_PINTYPE_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 @@ -296,39 +566,103 @@ /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, 7) + 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() - if(A) +/obj/item/integrated_circuit/input/advanced_locator/do_work(ord) + if(ord == 1) + var/datum/integrated_io/I = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = null + var/turf/T = get_turf(src) + var/list/nearby_things = view(radius,T) + var/list/valid_things = list() + if(isweakref(I.data)) + var/atom/A = I.data.resolve() var/desired_type = A.type if(desired_type) - for(var/atom/thing in nearby_things) + for(var/i in nearby_things) + var/atom/thing = i 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) + else if(istext(I.data)) + var/DT = I.data + for(var/i in nearby_things) + var/atom/thing = i + 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/advanced_locator_list + complexity = 6 + name = "list advanced locator" + desc = "This is needed for certain devices that demand list of names for a target to act upon. This type locates something \ + that is standing in given radius of up to 8 meters. Output is non-associative. Input will only consider keys if associative." + extended_desc = "The first pin requires a list of the kinds of objects that you want the locator to acquire. It will locate nearby objects by name and description, \ + and will then provide a list of all found objects which are similar. \ + The second pin is a radius." + inputs = list("desired type ref" = IC_PINTYPE_LIST, "radius" = IC_PINTYPE_NUMBER) + outputs = list("located ref" = IC_PINTYPE_LIST) + activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 30 + var/radius = 1 + cooldown_per_use = 10 +/obj/item/integrated_circuit/input/advanced_locator_list/on_data_written() + var/rad = get_pin_data(IC_INPUT, 2) + + if(isnum(rad)) + rad = clamp(rad, 0, 8) + radius = rad + +/obj/item/integrated_circuit/input/advanced_locator_list/do_work(ord) + if(ord == 1) + var/datum/integrated_io/I = inputs[1] + var/datum/integrated_io/O = outputs[1] + O.data = null + var/list/input_list = list() + input_list = I.data + if(length(input_list)) //if there is no input don't do anything. + var/turf/T = get_turf(src) + var/list/nearby_things = view(radius,T) + var/list/valid_things = list() + for(var/item in input_list) + if(!isnull(item) && !isnum(item)) + if(istext(item)) + for(var/i in nearby_things) + var/atom/thing = i + if(ismob(thing) && !isliving(thing)) + continue + if(findtext(addtext(thing.name," ",thing.desc), item, 1, 0) ) + valid_things.Add(WEAKREF(thing)) + else + var/atom/A = item + var/desired_type = A.type + for(var/i in nearby_things) + var/atom/thing = i + if(thing.type != desired_type) + continue + if(ismob(thing) && !isliving(thing)) + continue + valid_things.Add(WEAKREF(thing)) + if(valid_things.len) + O.data = valid_things + O.push_data() + activate_pin(2) + else + O.push_data() + activate_pin(3) + else + O.push_data() + activate_pin(3) /obj/item/integrated_circuit/input/signaler @@ -346,19 +680,22 @@ "on signal sent" = IC_PINTYPE_PULSE_OUT, "on signal received" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + action_flags = IC_ACTION_LONG_RANGE origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_MAGNET = 2) power_draw_idle = 5 power_draw_per_use = 40 - + cooldown_per_use = 5 var/frequency = 1457 var/code = 30 var/datum/radio_frequency/radio_connection /obj/item/integrated_circuit/input/signaler/Initialize(mapload) . = ..() - set_pin_data(IC_INPUT, 1, frequency) - set_pin_data(IC_INPUT, 2, code) - addtimer(CALLBACK(src, .proc/set_frequency, frequency), 40) + spawn(40) + set_frequency(frequency) + set_pin_data(IC_INPUT, 1, frequency) + set_pin_data(IC_INPUT, 2, code) + addtimer(CALLBACK(src, .proc/set_frequency, frequency), 40) /obj/item/integrated_circuit/input/signaler/Destroy() if(radio_controller) @@ -375,16 +712,17 @@ code = new_code -/obj/item/integrated_circuit/input/signaler/do_work() // Sends a signal. - if(!radio_connection) - return +/obj/item/integrated_circuit/input/signaler/do_work(ord) // Sends a signal. + if(ord == 1) + 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) + 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) @@ -453,13 +791,14 @@ 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) +/obj/item/integrated_circuit/input/EPv2/do_work(ord) + if(ord == 1) + 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) + 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) @@ -469,6 +808,109 @@ push_data() activate_pin(2) +/* TBI NTNet +/obj/item/integrated_circuit/input/ntnet_packet + name = "NTNet networking circuit" + desc = "Enables the sending and receiving of messages over NTNet via packet data protocol." + extended_desc = "Data can be sent or received using the second pin on each side, \ + with additonal data reserved for the third pin. When a message is received, the second activation pin \ + will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \ + can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." + icon_state = "signal" + complexity = 2 + cooldown_per_use = 1 + inputs = list( + "target NTNet addresses"= 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, + "passkey" = IC_PINTYPE_STRING, + "is_broadcast" = IC_PINTYPE_BOOLEAN + ) + activators = list("send data" = IC_PINTYPE_PULSE_IN, "on data received" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + action_flags = IC_ACTION_LONG_RANGE + power_draw_per_use = 50 + var/address + +/obj/item/integrated_circuit/input/ntnet_packet/Initialize(mapload) + . = ..() + var/datum/component/ntnet_interface/net = LoadComponent(/datum/component/ntnet_interface) + address = net.hardware_id + net.differentiate_broadcast = FALSE + desc += "
This circuit's NTNet hardware address is: [address]" + +/obj/item/integrated_circuit/input/ntnet_packet/do_work(ord) + if(ord == 1) + 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) + + var/datum/netdata/data = new + data.recipient_ids = splittext(target_address, ";") + data.standard_format_data(message, text, assembly ? strtohex(XorEncrypt(json_encode(assembly.access_card.access), SScircuit.cipherkey)) : null) + ntnet_send(data) + +/obj/item/integrated_circuit/input/ntnet_receive(datum/netdata/data) + set_pin_data(IC_OUTPUT, 1, data.sender_id) + set_pin_data(IC_OUTPUT, 2, data.data["data"]) + set_pin_data(IC_OUTPUT, 3, data.data["data_secondary"]) + set_pin_data(IC_OUTPUT, 4, data.data["encrypted_passkey"]) + set_pin_data(IC_OUTPUT, 5, data.broadcast) + + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/input/ntnet_advanced + name = "Low level NTNet transreceiver" + desc = "Enables the sending and receiving of messages over NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity." + extended_desc = "Data can be sent or received using the second pin on each side, \ + When a message is received, the second activation pin will pulse whatever is connected to it. \ + Pulsing the first activation pin will send a message. Messages can be sent to multiple recepients. \ + Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." + icon_state = "signal" + complexity = 4 + cooldown_per_use = 10 + inputs = list( + "target NTNet addresses"= IC_PINTYPE_STRING, + "data" = IC_PINTYPE_LIST, + ) + outputs = list("received data" = IC_PINTYPE_LIST, "is_broadcast" = IC_PINTYPE_BOOLEAN) + activators = list("send data" = IC_PINTYPE_PULSE_IN, "on data received" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + action_flags = IC_ACTION_LONG_RANGE + power_draw_per_use = 50 + var/address + +/obj/item/integrated_circuit/input/ntnet_advanced/Initialize(mapload) + . = ..() + var/datum/component/ntnet_interface/net = LoadComponent(/datum/component/ntnet_interface) + address = net.hardware_id + net.differentiate_broadcast = FALSE + desc += "
This circuit's NTNet hardware address is: [address]" + +/obj/item/integrated_circuit/input/ntnet_advanced/do_work(ord) + if(ord == 1) + var/target_address = get_pin_data(IC_INPUT, 1) + var/list/message = get_pin_data(IC_INPUT, 2) + if(!islist(message)) + message = list() + var/datum/netdata/data = new + data.recipient_ids = splittext(target_address, ";") + data.data = message + data.passkey = assembly.access_card.access + ntnet_send(data) + +/obj/item/integrated_circuit/input/ntnet_advanced/ntnet_receive(datum/netdata/data) + set_pin_data(IC_OUTPUT, 1, data.data) + set_pin_data(IC_OUTPUT, 2, data.broadcast) + push_data() + activate_pin(2) +*/ //This circuit gives information on where the machine is. /obj/item/integrated_circuit/input/gps name = "global positioning system" @@ -477,44 +919,46 @@ icon_state = "gps" complexity = 4 inputs = list() - outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER) + outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER, "full coords" = IC_PINTYPE_STRING) 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) +/obj/item/integrated_circuit/input/gps/do_work(ord) + if(ord == 1) + var/turf/T = get_turf(src) - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - if(!T) + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, null) + set_pin_data(IC_OUTPUT, 4, null) + if(!T) + return + + set_pin_data(IC_OUTPUT, 1, T.x) + set_pin_data(IC_OUTPUT, 2, T.y) + set_pin_data(IC_OUTPUT, 3, T.z) + set_pin_data(IC_OUTPUT, 4, "[T.x],[T.y],[T.z]") push_data() - return - - set_pin_data(IC_OUTPUT, 1, T.x) - set_pin_data(IC_OUTPUT, 2, T.y) - - push_data() - activate_pin(2) - + activate_pin(2) /obj/item/integrated_circuit/input/microphone name = "microphone" - desc = "Useful for spying on people or for voice activated machines." + desc = "Useful for spying on people, or for voice-activated machines." extended_desc = "This will automatically translate most languages it hears to Galactic Common. \ The first activation pin is always pulsed when the circuit hears someone talk, while the second one \ is only triggered if it hears someone speaking a language other than Galactic Common." icon_state = "recorder" complexity = 8 inputs = list() + flags = HEAR outputs = list( "speaker" = IC_PINTYPE_STRING, "message" = IC_PINTYPE_STRING ) activators = list("on message received" = IC_PINTYPE_PULSE_OUT, "on translation" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 15 - flags = HEAR + power_draw_per_use = 5 /obj/item/integrated_circuit/input/microphone/Initialize(mapload) . = ..() @@ -594,10 +1038,32 @@ hear_talk(M, text, verb, speaking) return + +/obj/item/integrated_circuit/output/move_detector + name = "movement detection" + desc = "It's have a complicit gyroscope system, that activates pin if assembly moved" + category_text = "Output" + complexity = 4 + inputs = list("toggle" = IC_PINTYPE_BOOLEAN) + outputs = list() + activators = list("moved" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_idle = 0 + var/turf/last_location + +/obj/item/integrated_circuit/output/light/advanced/on_data_written() + get_pin_data(IC_INPUT, 1) ? (power_draw_idle = 10) : (power_draw_idle = 0) + +/obj/item/integrated_circuit/output/move_detector/ext_moved() + var/turf/T = get_turf(get_object()) + if(last_location != T && get_pin_data(IC_INPUT, 1)) + activate_pin(1) + last_location = T + /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." + extended_desc = "If the '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) @@ -606,14 +1072,82 @@ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH power_draw_per_use = 120 -/obj/item/integrated_circuit/input/sensor/proc/scan(var/atom/A) +/obj/item/integrated_circuit/input/sensor/sense(atom/A, mob/user, prox) + if(!prox || !A || (ismob(A) && !isliving(A))) + return FALSE + if(!check_then_do_work()) + return FALSE + var/ignore_bags = get_pin_data(IC_INPUT, 1) + //var/datum/component/storage/STR_A = A.GetComponent(/datum/component/storage) + if(ignore_bags /*&& STR_A*/) + return FALSE + set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) + push_data() + to_chat(user, "You scan [A] with [assembly].") + activate_pin(1) + return TRUE + +/obj/item/integrated_circuit/input/sensor/ranged + name = "ranged sensor" + desc = "Scans and obtains a reference for any objects or persons in range. All you need to do is point the machine towards the target." + extended_desc = "If the 'ignore storage' pin is set to true, the sensor will disregard scanning various storage containers such as backpacks." + icon_state = "recorder" + complexity = 36 + inputs = list("ignore storage" = IC_PINTYPE_BOOLEAN) + outputs = list("scanned" = IC_PINTYPE_REF) + activators = list("on scanned" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 120 + +/obj/item/integrated_circuit/input/sensor/ranged/sense(atom/A, mob/user) + if(!user || !A || (ismob(A) && !isliving(A))) + return FALSE + if(user.client) + if(!(A in view(user.client))) + return FALSE + else + if(!(A in view(user))) + return FALSE + if(!check_then_do_work()) + return FALSE var/ignore_bags = get_pin_data(IC_INPUT, 1) if(ignore_bags) if(istype(A, /obj/item/storage)) return FALSE - set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) push_data() + to_chat(user, "You scan [A] with [assembly].") + activate_pin(1) + return TRUE + +/obj/item/integrated_circuit/input/obj_scanner + name = "scanner" + desc = "Scans and obtains a reference for any objects you use on the assembly." + extended_desc = "If the 'put down' pin is set to true, the assembly will take the scanned object from your hands to its location. \ + Useful for interaction with the grabber. The scanner only works using the help intent." + icon_state = "recorder" + can_be_asked_input = 1 + complexity = 4 + inputs = list("put down" = 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 = 20 + +/obj/item/integrated_circuit/input/obj_scanner/ask_for_input(obj/item/I, mob/living/user, a_intent) + if(!isobj(I)) + return FALSE + attackby_react(I, user, a_intent) + +/obj/item/integrated_circuit/input/obj_scanner/attackby_react(obj/item/I, mob/living/user, a_intent) + if(!isobj(I) || a_intent!=INTENT_HELP || !check_then_do_work()) + return FALSE + var/pu = get_pin_data(IC_INPUT, 1) + if(pu) + user.transferItemToLoc(I,drop_location()) + set_pin_data(IC_OUTPUT, 1, WEAKREF(I)) + push_data() + to_chat(user, "You let [assembly] scan [I].") activate_pin(1) return TRUE @@ -621,7 +1155,7 @@ 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." + extended_desc = "This circuit will give you the values of charge, max charge and the current percentage of the internal battery on demand." w_class = ITEMSIZE_TINY complexity = 1 inputs = list() @@ -629,32 +1163,36 @@ "cell charge" = IC_PINTYPE_NUMBER, "max charge" = IC_PINTYPE_NUMBER, "percentage" = IC_PINTYPE_NUMBER, - "refference to assembly" = IC_PINTYPE_REF + "refference to assembly" = IC_PINTYPE_REF, + "refference to cell" = 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/internalbm/do_work(ord) + if(ord == 1) + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, null) + set_pin_data(IC_OUTPUT, 4, null) + set_pin_data(IC_OUTPUT, 5, null) + if(assembly) + set_pin_data(IC_OUTPUT, 4, WEAKREF(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) + set_pin_data(IC_OUTPUT, 5, WEAKREF(assembly.battery)) + 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." + desc = "This can read the battery state of any device in view." icon_state = "externalbm" - extended_desc = "This circuit will give you values of charge, max charge and percentage of any device or battery in view" + extended_desc = "This circuit will give you the charge, max charge and the current percentage values of any device or battery in view." w_class = ITEMSIZE_TINY complexity = 2 inputs = list("target" = IC_PINTYPE_REF) @@ -668,40 +1206,232 @@ 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() +/obj/item/integrated_circuit/input/externalbm/do_work(ord) + if(ord == 1) - 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/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/cell/cell = null - if(istype(AM, /obj/item/cell)) // Is this already a cell? - cell = AM - else // If not, maybe there's a cell inside it? - for(var/obj/item/cell/C in AM.contents) - if(C) // Find one cell to charge. - cell = C - break - if(cell) + var/obj/item/cell/cell = null + if(istype(AM, /obj/item/cell)) // Is this already a cell? + cell = AM + else // If not, maybe there's a cell inside it? + for(var/obj/item/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) + 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) +/*TBI NTNet +/obj/item/integrated_circuit/input/ntnetsc + name = "NTNet scanner" + desc = "This can return the NTNet IDs of a component inside the given object, if there are any." + icon_state = "signalsc" + w_class = WEIGHT_CLASS_TINY + complexity = 2 + inputs = list("target" = IC_PINTYPE_REF) + outputs = list( + "id" = IC_PINTYPE_STRING + ) + activators = list("read" = 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 = 1 + +/obj/item/integrated_circuit/input/ntnetsc/do_work(ord) + if(ord == 1) + var/atom/AM = get_pin_data_as_type(IC_INPUT, 1, /atom) + var/datum/component/ntnet_interface/net + + if(AM) + var/list/processing_list = list(AM) + while(processing_list.len && !net) + var/atom/A = processing_list[1] + processing_list.Cut(1, 2) + //Byond does not allow things to be in multiple contents, or double parent-child hierarchies, so only += is needed + //This is also why we don't need to check against assembled as we go along + processing_list += A.contents + net = A.GetComponent(/datum/component/ntnet_interface) + + if(net) + set_pin_data(IC_OUTPUT, 1, net.hardware_id) + push_data() + activate_pin(2) + else + set_pin_data(IC_OUTPUT, 1, null) + push_data() + activate_pin(3) +*/ +/* TBI ouija +/obj/item/integrated_circuit/input/ouija + name = "superstring resonator" + desc = "A highly dubious piece of hardware. It may do nothing or it may ruin your day." + extended_desc = "This chip contains an esoteric mix of sensors with spurious claims. Proponents claim it facilitates communication \ + with beings from other dimensions. A larger majority believe it to be a sophisticated hacking device. The designers simply state\ + that \"more testing is required\"." + w_class = ITEMSIZE_TINY + complexity = 4 + inputs = list("toggle on" = IC_PINTYPE_BOOLEAN) +// outputs = list("" = ) + activators = list("read" = IC_PINTYPE_PULSE_IN, "on activity" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + origin_tech = list(TECH_ARCANE = 1, TECH_ILLEGAL = 1) + power_draw_idle = 10 + power_draw_per_use = 50 + +/obj/item/integrated_circuit/input/ouija/do_work(ord) +// if(ord == 1) + +*/ + +/obj/item/integrated_circuit/input/data_card_reader + name = "data card reader" + desc = "A circuit that can read from and write to data cards." + extended_desc = "Setting the \"write mode\" boolean to true will cause any data cards that are used on the assembly to replace\ + their existing function and data strings with the given strings, if it is set to false then using a data card on the assembly will cause\ + the function and data strings stored on the card to be written to the output pins." + icon_state = "card_reader" + complexity = 4 + can_be_asked_input = TRUE + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "function" = IC_PINTYPE_STRING, + "data to store" = IC_PINTYPE_STRING, + "write mode" = IC_PINTYPE_BOOLEAN + ) + outputs = list( + "function" = IC_PINTYPE_STRING, + "stored data" = IC_PINTYPE_STRING + ) + activators = list( + "on write" = IC_PINTYPE_PULSE_OUT, + "on read" = IC_PINTYPE_PULSE_OUT + ) + +/obj/item/integrated_circuit/input/data_card_reader/ask_for_input(obj/item/I, mob/living/user, a_intent) + if(!isobj(I)) + return FALSE + attackby_react(I, user, a_intent) + +/obj/item/integrated_circuit/input/data_card_reader/attackby_react(obj/item/I, mob/living/user, intent) + var/obj/item/card/data/card = I + var/write_mode = get_pin_data(IC_INPUT, 3) + if(card) + if(write_mode == TRUE) + card.function = get_pin_data(IC_INPUT, 1) + card.data = get_pin_data(IC_INPUT, 2) + push_data() + activate_pin(1) + else + set_pin_data(IC_OUTPUT, 1, card.function) + set_pin_data(IC_OUTPUT, 2, card.data) + push_data() + activate_pin(2) + else + return FALSE + return TRUE + +/*Adding some color to cards aswell, because why not // Because it's not implemented? +/obj/item/card/data/attackby_react(obj/item/I, mob/living/user) + if(istype(I, /obj/item/integrated_electronics/detailer)) + var/obj/item/integrated_electronics/detailer/D = I + detail_color = D.detail_color + update_icon() + return ..() +*/ +/obj/item/integrated_circuit/input/storage_examiner + name = "storage examiner circuit" + desc = "This circuit lets you scan a storage's content. (backpacks, toolboxes etc.)" + extended_desc = "The items are put out as reference, which makes it possible to interact with them. Additionally also gives the amount of items." + icon_state = "grabber" + can_be_asked_input = 1 + complexity = 6 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "storage" = IC_PINTYPE_REF + ) + activators = list( + "examine" = IC_PINTYPE_PULSE_IN, + "on examined" = IC_PINTYPE_PULSE_OUT + ) + outputs = list( + "item amount" = IC_PINTYPE_NUMBER, + "item list" = IC_PINTYPE_LIST + ) + power_draw_per_use = 85 + +/obj/item/integrated_circuit/input/storage_examiner/do_work(ord) + if(ord == 1) + var/obj/item/storage = get_pin_data_as_type(IC_INPUT, 1, /obj/item) + if(!istype(storage,/obj/item/storage)) + return + + set_pin_data(IC_OUTPUT, 1, storage.contents.len) + + var/list/regurgitated_contents = list() + for(var/obj/o in storage.contents) + regurgitated_contents.Add(WEAKREF(o)) + + + set_pin_data(IC_OUTPUT, 2, regurgitated_contents) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/mining/ore_analyzer + name = "ore analyzer" + desc = "Analyzes a rock for its ore type." + extended_desc = "Takes a reference for an object and checks if it is a rock first. If that is the case, it outputs the mineral \ + inside the rock." + category_text = "Input" + ext_cooldown = 1 + complexity = 20 + cooldown_per_use = 1 SECONDS + inputs = list( + "rock" = IC_PINTYPE_REF + ) + outputs = list("ore type" = IC_PINTYPE_LIST) + activators = list( + "analyze" = IC_PINTYPE_PULSE_IN, + "on failed" = IC_PINTYPE_PULSE_OUT, + "on found" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 20 + +/obj/item/integrated_circuit/mining/ore_analyzer/do_work(ord) + if(ord == 1) + var/turf/simulated/mineral/T + var/mineral + T = get_pin_data(IC_INPUT, 1) + if(!ismineralturf(T) && !T.has_resources) + mineral = null + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + push_data() + activate_pin(2) + return + mineral = T.resources + set_pin_data(IC_OUTPUT, 1, mineral) + push_data() + activate_pin(3) /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 + complexity = 2 inputs = list() outputs = list( "pressure" = IC_PINTYPE_NUMBER, @@ -717,38 +1447,39 @@ 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() +/obj/item/integrated_circuit/input/atmo_scanner/do_work(ord) + if(ord == 1) + 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 + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles - if (total_moles) - var/o2_level = environment.gas[/datum/gas/oxygen]/total_moles - var/n2_level = environment.gas[/datum/gas/nitrogen]/total_moles - var/co2_level = environment.gas[/datum/gas/carbon_dioxide]/total_moles - var/phoron_level = environment.gas[/datum/gas/phoron]/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_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(phoron_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) + if (total_moles) + var/o2_level = environment.gas[/datum/gas/oxygen]/total_moles + var/n2_level = environment.gas[/datum/gas/nitrogen]/total_moles + var/co2_level = environment.gas[/datum/gas/carbon_dioxide]/total_moles + var/phoron_level = environment.gas[/datum/gas/phoron]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_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(phoron_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" @@ -764,21 +1495,22 @@ 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() +/obj/item/integrated_circuit/input/pressure_sensor/do_work(ord) + if(ord == 1) + 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 + 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) + 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" @@ -794,20 +1526,21 @@ 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() +/obj/item/integrated_circuit/input/temperature_sensor/do_work(ord) + if(ord == 1) + 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 + 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) + 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" @@ -823,21 +1556,22 @@ 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() +/obj/item/integrated_circuit/input/oxygen_sensor/do_work(ord) + if(ord == 1) + 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 + var/total_moles = environment.total_moles - if (total_moles) - var/o2_level = environment.gas[/datum/gas/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) + if (total_moles) + var/o2_level = environment.gas[/datum/gas/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" @@ -853,21 +1587,22 @@ 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() +/obj/item/integrated_circuit/input/co2_sensor/do_work(ord) + if(ord == 1) + 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 + var/total_moles = environment.total_moles - if (total_moles) - var/co2_level = environment.gas[/datum/gas/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) + if (total_moles) + var/co2_level = environment.gas[/datum/gas/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" @@ -883,21 +1618,22 @@ 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() +/obj/item/integrated_circuit/input/nitrogen_sensor/do_work(ord) + if(ord == 1) + 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 + var/total_moles = environment.total_moles - if (total_moles) - var/n2_level = environment.gas[/datum/gas/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) + if (total_moles) + var/n2_level = environment.gas[/datum/gas/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/phoron_sensor name = "integrated phoron sensor" @@ -913,18 +1649,19 @@ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3) power_draw_per_use = 20 -/obj/item/integrated_circuit/input/phoron_sensor/do_work() - var/turf/T = get_turf(src) - if(!istype(T)) //Invalid input - return - var/datum/gas_mixture/environment = T.return_air() +/obj/item/integrated_circuit/input/phoron_sensor/do_work(ord) + if(ord == 1) + 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 + var/total_moles = environment.total_moles - if (total_moles) - var/phoron_level = environment.gas[/datum/gas/phoron]/total_moles - set_pin_data(IC_OUTPUT, 1, round(phoron_level*100,0.1)) - else - set_pin_data(IC_OUTPUT, 1, 0) - push_data() - activate_pin(2) + if (total_moles) + var/phoron_level = environment.gas[/datum/gas/phoron]/total_moles + set_pin_data(IC_OUTPUT, 1, round(phoron_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 index f4c0119af66..a717486050f 100644 --- a/code/modules/integrated_electronics/subtypes/lists.dm +++ b/code/modules/integrated_electronics/subtypes/lists.dm @@ -1,47 +1,61 @@ //These circuits do things with lists, and use special list pins for stability. -/obj/item/integrated_circuit/list +/obj/item/integrated_circuit/lists complexity = 1 inputs = list( - "input" = IC_PINTYPE_LIST - ) - outputs = list("result" = IC_PINTYPE_STRING) - activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT) + "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 + cooldown_per_use = 10 -/obj/item/integrated_circuit/list/pick +/obj/item/integrated_circuit/lists/pick name = "pick circuit" - desc = "This circuit will randomly 'pick' an element from a list that is inputted." - extended_desc = "Will output null if the list is empty. Input list is unmodified." + desc = "This circuit will pick a random element from the input list, and output that element." + extended_desc = "The input list is not modified." icon_state = "addition" + outputs = list( + "result" = IC_PINTYPE_ANY + ) + activators = list( + "compute" = IC_PINTYPE_PULSE_IN, + "on success" = IC_PINTYPE_PULSE_OUT, + "on failure" = IC_PINTYPE_PULSE_OUT, + ) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + cooldown_per_use = 1 -/obj/item/integrated_circuit/list/pick/do_work() - var/result = null +/obj/item/integrated_circuit/lists/pick/do_work() 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) + set_pin_data(IC_OUTPUT, 1, pick(input_list)) + push_data() + activate_pin(2) + else + activate_pin(3) -/obj/item/integrated_circuit/list/append +/obj/item/integrated_circuit/lists/append name = "append circuit" desc = "This circuit will add an element to a list." extended_desc = "The new element will always be at the bottom of the list." inputs = list( "list to append" = IC_PINTYPE_LIST, "input" = IC_PINTYPE_ANY - ) + ) outputs = list( "appended list" = IC_PINTYPE_LIST - ) + ) icon_state = "addition" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/list/append/do_work() +/obj/item/integrated_circuit/lists/append/do_work() var/list/input_list = get_pin_data(IC_INPUT, 1) var/list/output_list = list() var/new_entry = get_pin_data(IC_INPUT, 2) @@ -52,139 +66,274 @@ push_data() activate_pin(2) -/obj/item/integrated_circuit/list/search + +/obj/item/integrated_circuit/lists/search name = "search circuit" - desc = "This circuit will give index of desired element in the list." - extended_desc = "Search will start at 1 position and will return first matching position." + desc = "This circuit will get the index location of the desired element in a list." + extended_desc = "Search will start at position 1 and will return the first matching position." inputs = list( "list" = IC_PINTYPE_LIST, "item" = IC_PINTYPE_ANY - ) + ) outputs = list( "index" = IC_PINTYPE_NUMBER - ) + ) + activators = list( + "compute" = IC_PINTYPE_PULSE_IN, + "on success" = IC_PINTYPE_PULSE_OUT, + "on failure" = IC_PINTYPE_PULSE_OUT, + ) icon_state = "addition" + complexity = 2 spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + cooldown_per_use = 1 -/obj/item/integrated_circuit/list/search/do_work() +/obj/item/integrated_circuit/lists/search/do_work() var/list/input_list = get_pin_data(IC_INPUT, 1) - var/item = get_pin_data(IC_INPUT, 2) - set_pin_data(IC_OUTPUT, 1, input_list.Find(item)) + var/output = input_list.Find(get_pin_data(IC_INPUT, 2)) + + set_pin_data(IC_OUTPUT, 1, output) 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." + if(output) + activate_pin(2) + else + activate_pin(3) + + +/obj/item/integrated_circuit/lists/filter + name = "filter circuit" + desc = "This circuit will search through a list for anything matching the desired element(s) and outputs two lists: \ + one containing only the matching elements, and one with the matching elements filtered out." + extended_desc = "Sample accepts lists. If no match is found, the original list is sent to output 1." 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 - ) + "input list" = IC_PINTYPE_LIST, + "sample" = IC_PINTYPE_ANY + ) outputs = list( - "item" = IC_PINTYPE_LIST - ) + "list filtered" = IC_PINTYPE_LIST, + "list matched" = IC_PINTYPE_LIST + ) + activators = list( + "compute" = IC_PINTYPE_PULSE_IN, + "on match" = IC_PINTYPE_PULSE_OUT, + "on no match" = IC_PINTYPE_PULSE_OUT + ) + complexity = 6 icon_state = "addition" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/list/delete/do_work() +/obj/item/integrated_circuit/lists/filter/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) + var/sample = get_pin_data(IC_INPUT, 2) + var/list/sample_list = islist(sample) ? uniqueList(sample) : null + var/list/output_list1 = input_list.Copy() + var/list/output_list2 = list() + var/list/output = list() + + for(var/input_item in input_list) + if(sample_list) + for(var/sample_item in sample_list) + if(!isnull(sample_item)) + if(istext(input_item) && istext(sample_item) && findtext(input_item, sample_item)) + output += input_item + if(istype(input_item, /atom) && istext(sample_item)) + var/atom/input_item_atom = input_item + if(istext(sample_item) && findtext(input_item_atom.name, sample_item)) + output += input_item + if(!istext(input_item)) + if(input_item == sample_item) + output += input_item + else + if(!isnull(sample)) + if(istext(input_item) && istext(sample) && findtext(input_item, sample)) + output += input_item + continue + if(istype(input_item, /atom) && istext(sample)) + var/atom/input_itema = input_item + if(findtext(input_itema.name, sample)) + output += input_item + if(!istext(input_item)) + if(input_item == sample) + output += input_item + + output_list1.Remove(output) + output_list2.Add(output) + set_pin_data(IC_OUTPUT, 1, output_list1) + set_pin_data(IC_OUTPUT, 2, output_list2) 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." + output_list1 ~! input_list ? activate_pin(2) : activate_pin(3) + +/obj/item/integrated_circuit/lists/listset + name = "list set circuit" + desc = "This circuit will remove any duplicate entries from a list." + extended_desc = "If there are no duplicate entries, the output list will be unchanged." inputs = list( - "list" = IC_PINTYPE_LIST, - "index" = IC_PINTYPE_NUMBER, - "item" = IC_PINTYPE_ANY - ) + "list" = IC_PINTYPE_LIST + ) outputs = list( - "redacted list" = IC_PINTYPE_LIST - ) + "list filtered" = IC_PINTYPE_LIST + ) icon_state = "addition" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/list/write/do_work() +/obj/item/integrated_circuit/lists/listset/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 + input_list = uniqueList(input_list) + set_pin_data(IC_OUTPUT, 1, input_list) push_data() activate_pin(2) -obj/item/integrated_circuit/list/len +/obj/item/integrated_circuit/lists/at + name = "at circuit" + desc = "This circuit will pick an element from a list by the input index." + extended_desc = "If there is no element at the given index, the result will be null." + inputs = list( + "list" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_INDEX + ) + outputs = list( + "item" = IC_PINTYPE_ANY + ) + activators = list( + "compute" = IC_PINTYPE_PULSE_IN, + "on success" = IC_PINTYPE_PULSE_OUT, + "on failure" = IC_PINTYPE_PULSE_OUT + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + cooldown_per_use = 1 + +/obj/item/integrated_circuit/lists/at/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/index = get_pin_data(IC_INPUT, 2) + + // Check if index is valid + if(index > input_list.len) + set_pin_data(IC_OUTPUT, 1, null) + push_data() + activate_pin(3) + return + + set_pin_data(IC_OUTPUT, 1, input_list[index]) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/lists/delete + name = "delete circuit" + desc = "This circuit will remove an element from a list by the index." + extended_desc = "If there is no element at the given index, the result list will be unchanged." + inputs = list( + "list" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_INDEX + ) + outputs = list( + "item" = IC_PINTYPE_LIST + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/delete/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/list/red_list = list() + var/index = get_pin_data(IC_INPUT, 2) + + if(length(input_list)) + for(var/j in 1 to input_list.len) + var/I = input_list[j] + if(j != index) + red_list.Add(I) + set_pin_data(IC_OUTPUT, 1, red_list) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/lists/write + name = "write circuit" + desc = "This circuit will write an element to a list at the given index location." + extended_desc = "If there is no element at the given index, it will output the same list as before." + inputs = list( + "list" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_INDEX, + "item" = IC_PINTYPE_ANY + ) + outputs = list( + "redacted list" = IC_PINTYPE_LIST + ) + activators = list( + "compute" = IC_PINTYPE_PULSE_IN, + "on success" = IC_PINTYPE_PULSE_OUT, + "on failure" = IC_PINTYPE_PULSE_OUT, + ) + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/lists/write/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/index = get_pin_data(IC_INPUT, 2) + var/item = get_pin_data(IC_INPUT, 3) + + // Check if index is valid + if(index > input_list.len) + set_pin_data(IC_OUTPUT, 1, input_list) + push_data() + activate_pin(3) + return + + if(!islist(item)) + var/list/red_list = input_list.Copy() //crash proof + red_list[index] = item + set_pin_data(IC_OUTPUT, 1, red_list) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/lists/len name = "len circuit" - desc = "This circuit will give length of the list." + desc = "This circuit will return the 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() +/obj/item/integrated_circuit/lists/len/do_work() var/list/input_list = get_pin_data(IC_INPUT, 1) set_pin_data(IC_OUTPUT, 1, input_list.len) push_data() activate_pin(2) + cooldown_per_use = 1 -/obj/item/integrated_circuit/list/jointext +/obj/item/integrated_circuit/lists/jointext name = "join text circuit" - desc = "This circuit will add all elements of a list into one string, seperated by a character." + desc = "This circuit will combine two lists into one, and output it as a string." extended_desc = "Default settings will encode the entire list into a string." + icon_state = "join" inputs = list( - "list to join" = IC_PINTYPE_LIST,// - "delimiter" = IC_PINTYPE_CHAR, - "start" = IC_PINTYPE_NUMBER, + "list to join" = IC_PINTYPE_LIST, + "delimiter" = IC_PINTYPE_STRING, + "start" = IC_PINTYPE_INDEX, "end" = IC_PINTYPE_NUMBER - ) + ) inputs_default = list( - "2" = ",", - "3" = 1, + "2" = ", ", "4" = 0 - ) + ) outputs = list( "joined text" = IC_PINTYPE_STRING - ) + ) icon_state = "addition" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + cooldown_per_use = 1 -/obj/item/integrated_circuit/list/jointext/do_work() +/obj/item/integrated_circuit/lists/jointext/do_work() var/list/input_list = get_pin_data(IC_INPUT, 1) var/delimiter = get_pin_data(IC_INPUT, 2) var/start = get_pin_data(IC_INPUT, 3) @@ -198,3 +347,92 @@ obj/item/integrated_circuit/list/len set_pin_data(IC_OUTPUT, 1, result) push_data() activate_pin(2) + + +/obj/item/integrated_circuit/lists/constructor + name = "large list constructor" + desc = "This circuit will build a list out of up to sixteen input values." + icon_state = "constr8" + inputs = list() + outputs = list( + "result" = IC_PINTYPE_LIST + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/number_of_pins = 16 + +/obj/item/integrated_circuit/lists/constructor/Initialize(mapload) + 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. + complexity = number_of_pins / 2 + . = ..() + +/obj/item/integrated_circuit/lists/constructor/do_work() + var/list/output_list = list() + for(var/i = 1 to number_of_pins) + var/data = get_pin_data(IC_INPUT, i) + + // No nested lists + if(!islist(data)) + output_list += data + else + output_list += null + + set_pin_data(IC_OUTPUT, 1, output_list) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/lists/constructor/small + name = "list constructor" + desc = "This circuit will build a list out of up to four input values." + icon_state = "constr" + number_of_pins = 4 + +/obj/item/integrated_circuit/lists/constructor/medium + name = "medium list constructor" + desc = "This circuit will build a list out of up to eight input values." + icon_state = "constr8" + number_of_pins = 8 + + +/obj/item/integrated_circuit/lists/deconstructor + name = "large list deconstructor" + desc = "This circuit will write the first sixteen entries of its input list, starting with the index, into the output values." + icon_state = "deconstr8" + inputs = list( + "input" = IC_PINTYPE_LIST, + "index" = IC_PINTYPE_INDEX + ) + outputs = list() + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/number_of_pins = 16 + +/obj/item/integrated_circuit/lists/deconstructor/Initialize(mapload) + for(var/i = 1 to number_of_pins) + outputs["output [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. + complexity = number_of_pins / 2 + . = ..() + +/obj/item/integrated_circuit/lists/deconstructor/do_work() + var/list/input_list = get_pin_data(IC_INPUT, 1) + var/start_index = get_pin_data(IC_INPUT, 2) + + for(var/i = 1 to number_of_pins) + var/list_index = i + start_index - 1 + if(list_index > input_list.len) + set_pin_data(IC_OUTPUT, i, null) + else + set_pin_data(IC_OUTPUT, i, input_list[list_index]) + + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/lists/deconstructor/small + name = "list deconstructor" + desc = "This circuit will write the first four entries of its input list, starting with the index, into the output values." + icon_state = "deconstr" + number_of_pins = 4 + +/obj/item/integrated_circuit/lists/deconstructor/medium + name = "medium list deconstructor" + desc = "This circuit will write the first eight entries of its input list, starting with the index, into the output values." + number_of_pins = 8 diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm index 82667634a1c..aee8c2f4505 100644 --- a/code/modules/integrated_electronics/subtypes/logic.dm +++ b/code/modules/integrated_electronics/subtypes/logic.dm @@ -2,8 +2,8 @@ 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") + complexity = 1 + outputs = list("result" = IC_PINTYPE_BOOLEAN) activators = list("compare" = IC_PINTYPE_PULSE_IN) category_text = "Logic" power_draw_per_use = 1 @@ -12,11 +12,10 @@ push_data() /obj/item/integrated_circuit/logic/binary - inputs = list("A","B") + inputs = list("A" = IC_PINTYPE_ANY,"B" = IC_PINTYPE_ANY) 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] @@ -31,12 +30,14 @@ /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/binary/proc/comparable(var/datum/integrated_io/A, var/datum/integrated_io/B) + return (isnum(A.data) && isnum(B.data)) || (istext(A.data) && istext(B.data)) + /obj/item/integrated_circuit/logic/unary - inputs = list("A") + inputs = list("A" = IC_PINTYPE_ANY) 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 @@ -48,7 +49,7 @@ /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." + desc = "This gate compares two values, and outputs TRUE if both are the same." icon_state = "equal" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -57,16 +58,15 @@ /obj/item/integrated_circuit/logic/binary/jklatch name = "JK latch" - desc = "This gate is a synchronysed JK latch." + desc = "This gate is a synchronized JK latch." icon_state = "jklatch" - inputs = list("J","K") - outputs = list("Q","!Q") + inputs = list("J" = IC_PINTYPE_ANY,"K" = IC_PINTYPE_ANY) + outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) 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] @@ -89,16 +89,15 @@ /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." + desc = "This gate is a synchronized RS latch. If both R and S are true its state will not change." icon_state = "sr_nor" - inputs = list("S","R") - outputs = list("Q","!Q") + inputs = list("S" = IC_PINTYPE_ANY,"R" = IC_PINTYPE_ANY) + outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) 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] @@ -119,16 +118,15 @@ /obj/item/integrated_circuit/logic/binary/gdlatch name = "gated D latch" - desc = "This gate is a synchronysed gated D latch." + desc = "This gate is a synchronized gated D latch." icon_state = "gated_d" - inputs = list("D","E") - outputs = list("Q","!Q") + inputs = list("D" = IC_PINTYPE_ANY,"E" = IC_PINTYPE_ANY) + outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) 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] @@ -149,7 +147,7 @@ /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." + desc = "This gate compares two values, and outputs TRUE if both are different." icon_state = "not_equal" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -158,7 +156,7 @@ /obj/item/integrated_circuit/logic/binary/and name = "and gate" - desc = "This gate will output 'one' if both inputs evaluate to true." + desc = "This gate will output TRUE if both inputs evaluate to true." icon_state = "and" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -167,7 +165,7 @@ /obj/item/integrated_circuit/logic/binary/or name = "or gate" - desc = "This gate will output 'one' if one of the inputs evaluate to true." + desc = "This gate will output TRUE if one of the inputs evaluate to true." icon_state = "or" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -176,39 +174,43 @@ /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." + desc = "This will output TRUE 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 + if(comparable(A, 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." + desc = "This will output TRUE 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 + if(comparable(A, 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." + desc = "This will output TRUE 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 + if(comparable(A, 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." + name = "greater than or equal gate" + desc = "This will output TRUE 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 + if(comparable(A, B)) + return A.data >= B.data /obj/item/integrated_circuit/logic/unary/not name = "not gate" diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm index 894052b08a5..06e0852a69f 100644 --- a/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -1,6 +1,819 @@ /obj/item/integrated_circuit/manipulation category_text = "Manipulation" +/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 one step 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; only drone assemblies can move." + w_class = ITEMSIZE_NORMAL + complexity = 10 + cooldown_per_use = 1 + ext_cooldown = 4 + inputs = list("direction" = IC_PINTYPE_DIR) + outputs = list("obstacle" = IC_PINTYPE_REF) + activators = list("step towards dir" = IC_PINTYPE_PULSE_IN,"on step"=IC_PINTYPE_PULSE_OUT,"blocked"=IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + action_flags = IC_ACTION_MOVEMENT + power_draw_per_use = 100 + +/obj/item/integrated_circuit/manipulation/locomotion/do_work(ord) + if(ord == 1) + ..() + var/turf/T = get_turf(src) + if(T && assembly) + if(assembly.anchored || !assembly.can_move()) + return + if(assembly.loc == T) // Check if we're held by someone. If the loc is the floor, we're not held. + var/datum/integrated_io/wanted_dir = inputs[1] + if(isnum(wanted_dir.data)) + if(step(assembly, wanted_dir.data)) + activate_pin(2) + return + else + set_pin_data(IC_OUTPUT, 1, WEAKREF(assembly.collw)) + push_data() + activate_pin(3) + return FALSE + return FALSE + +/obj/item/integrated_circuit/manipulation/anchoring + name = "anchoring bolts" + desc = "Pop-out anchoring bolts which can secure an assembly to the floor." + + outputs = list( + "enabled" = IC_PINTYPE_BOOLEAN + ) + activators = list( + "toggle" = IC_PINTYPE_PULSE_IN, + "on toggle" = IC_PINTYPE_PULSE_OUT + ) + + complexity = 8 + cooldown_per_use = 2 SECOND + power_draw_per_use = 50 + spawn_flags = IC_SPAWN_DEFAULT + origin_tech = list(TECH_ENGINEERING = 2) + +/obj/item/integrated_circuit/manipulation/anchoring/remove(mob/user, silent, index) + if(assembly.anchored_by == src) + silent ? null : to_chat(SPAN_WARNING("With the bolts deployed you can't remove the circuit.")) + return + . = ..() + +/obj/item/integrated_circuit/manipulation/anchoring/do_work(ord) + //var/obj/item/electronic_assembly/ass = assembly + if(!isturf(assembly.loc)) + return + + if(ord == 1) + if(assembly.anchored && assembly.anchored_by != src) + audible_message(SPAN_WARNING("The [assembly] whirrs and clanks!"), SPAN_NOTICE("You hear a soft whirr then a clank.")) + return + assembly.anchored = !assembly.anchored + assembly.anchored ? (assembly.anchored_by = src) : (assembly.anchored_by = null) + + visible_message( + assembly.anchored ? \ + "\The [get_object()] deploys a set of anchoring bolts!" \ + : \ + "\The [get_object()] retracts its anchoring bolts" + ) + + set_pin_data(IC_OUTPUT, 1, assembly.anchored) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/hatchlock + name = "maintenance hatch lock" + desc = "An electronically controlled lock for the assembly's maintenance hatch." + extended_desc = "WARNING: If you lock the hatch with no circuitry to reopen it, there is no way to open the hatch again!" + icon_state = "hatch_lock" + + outputs = list( + "enabled" = IC_PINTYPE_BOOLEAN + ) + activators = list( + "toggle" = IC_PINTYPE_PULSE_IN, + "on toggle" = IC_PINTYPE_PULSE_OUT + ) + + complexity = 4 + cooldown_per_use = 2 SECOND + power_draw_per_use = 50 + spawn_flags = IC_SPAWN_DEFAULT + origin_tech = list(TECH_ENGINEERING = 2) + var/lock_enabled = -1 + +/obj/item/integrated_circuit/manipulation/hatchlock/do_work(ord) + if(ord == 1) + if(!assembly) + return + visible_message(SPAN_NOTICE("\The [assembly] whirrs.[assembly.panel_locked > 1 ? null : lock_enabled ? " The screws are now covered." : " The screws are now exposed!"]")) + lock_enabled *= -1 + assembly.panel_locked += lock_enabled + set_pin_data(IC_OUTPUT, 1, lock_enabled) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/hatchlock/remove(mob/user, silent, index) + lock_enabled ? (assembly.panel_locked -= 1) : null + . = ..() + +/obj/item/integrated_circuit/manipulation/plant_module + name = "plant manipulation module" + desc = "Used to uproot weeds and harvest/plant trays." + icon_state = "plant_m" + extended_desc = "The circuit accepts a reference to a hydroponic tray or an item on an adjacent tile. \ + Mode input (0-harvest, 1-uproot weeds, 2-uproot plant, 3-plant seed) determines action. \ + Harvesting outputs a list of the harvested plants." + w_class = WEIGHT_CLASS_TINY + complexity = 10 + inputs = list("tray" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER,"item" = IC_PINTYPE_REF) + outputs = list("result" = IC_PINTYPE_LIST) + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 50 + +/obj/item/integrated_circuit/manipulation/plant_module/do_work(ord) + if(ord == 1) + ..() + var/obj/acting_object = get_object() + var/obj/OM = get_pin_data_as_type(IC_INPUT, 1, /obj) + var/obj/O = get_pin_data_as_type(IC_INPUT, 3, /obj/item) + + if(!check_target(OM)) + push_data() + activate_pin(2) + return + /* TBI May be pointless. Testing needed on vines. + if(istype(OM,/obj/structure/spacevine) && check_target(OM) && get_pin_data(IC_INPUT, 2) == 2) + qdel(OM) + push_data() + activate_pin(2) + return + */ + var/obj/machinery/portable_atmospherics/hydroponics/TR = OM + if(istype(TR)) + switch(get_pin_data(IC_INPUT, 2)) + if(0) + var/list/harvest_output = TR.attack_hand() + for(var/i in 1 to length(harvest_output)) + harvest_output[i] = WEAKREF(harvest_output[i]) + + if(length(harvest_output)) + set_pin_data(IC_OUTPUT, 1, harvest_output) + push_data() + if(1) + TR.weedlevel = 0 + TR.update_icon() + if(2) + if(TR.seed) //Could be that they're just using it as a de-weeder + TR.age = 0 + TR.health = 0 + if(TR.harvest) + TR.harvest = FALSE //To make sure they can't just put in another seed and insta-harvest it + qdel(TR.seed) + TR.seed = null + TR.weedlevel = 0 //Has a side effect of cleaning up those nasty weeds + TR.dead = 0 + TR.update_icon() + if(3) + if(!check_target(O)) + activate_pin(2) + return FALSE + + else if(istype(O, /obj/item/seeds)/* && !istype(O, /obj/item/seeds/sample)*/) + if(!TR.seed) + + if(istype(O, /obj/item/seeds/kudzuseed)) + investigate_log("had Kudzu planted in it by [acting_object] at [AREACOORD(src)]","kudzu") + acting_object.visible_message("[acting_object] plants [O].") + TR.plant_seeds(O) + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/seed_extractor + name = "seed extractor module" + desc = "Used to extract seeds from grown produce." + icon_state = "plant_m" + extended_desc = "The circuit accepts a reference to a plant item and extracts seeds from it, outputting the results to a list." + complexity = 8 + inputs = list("target" = IC_PINTYPE_REF) + outputs = list("result" = IC_PINTYPE_LIST) + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 50 +/* TBI: Move seed extrac proc to root +/obj/item/integrated_circuit/manipulation/seed_extractor/do_work() + ..() + var/obj/O = get_pin_data_as_type(IC_INPUT, 1, /obj/item) + if(!check_target(O)) + push_data() + activate_pin(2) + return + + var/list/seed_output = seedify(O, -1) + for(var/i in 1 to length(seed_output)) + seed_output[i] = WEAKREF(seed_output[i]) + + if(seed_output.len) + set_pin_data(IC_OUTPUT, 1, seed_output) + push_data() + activate_pin(2) +*/ +/obj/item/integrated_circuit/manipulation/grabber + name = "grabber" + desc = "A circuit with its own inventory for items. Used to grab and store things." + icon_state = "grabber" + extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, -1 to eject all objects, and -2 to eject the target. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly." + w_class = WEIGHT_CLASS_SMALL + size = 3 + cooldown_per_use = 5 + complexity = 10 + inputs = list("target" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER) + outputs = list("first" = IC_PINTYPE_REF, "last" = IC_PINTYPE_REF, "amount" = IC_PINTYPE_NUMBER,"contents" = IC_PINTYPE_LIST) + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + action_flags = IC_ACTION_COMBAT + power_draw_per_use = 50 + var/max_items = 10 + +/obj/item/integrated_circuit/manipulation/grabber/do_work(ord) + if(ord == 1) + var/obj/item/AM = get_pin_data_as_type(IC_INPUT, 1, /obj/item) + if(!QDELETED(AM) && !istype(AM, /obj/item/electronic_assembly) && !istype(AM, /obj/item/transfer_valve) && !istype(assembly.loc, /obj/item/implant/compressed)) + var/mode = get_pin_data(IC_INPUT, 2) + switch(mode) + if(1) + grab(AM) + if(0) + if(contents.len) + drop(contents[1]) + if(-1) + drop_all() + if(-2) + drop(AM) + update_outputs() + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/grabber/proc/grab(obj/item/AM) + var/max_w_class = assembly.w_class + if(check_target(AM)) + if(contents.len < max_items && AM.w_class <= max_w_class) + var/atom/A = get_object() + A.investigate_log("picked up ([AM]) with [src].", INVESTIGATE_CIRCUIT) + AM.forceMove(src) + +/obj/item/integrated_circuit/manipulation/grabber/proc/drop(obj/item/AM, turf/T = drop_location()) + if(!check_target(AM, FALSE, TRUE, TRUE, TRUE)) + return + var/atom/A = get_object() + A.investigate_log("dropped ([AM]) from [src].", INVESTIGATE_CIRCUIT) + AM.forceMove(T) + +/obj/item/integrated_circuit/manipulation/grabber/proc/drop_all() + if(contents.len) + var/turf/T = drop_location() + var/obj/item/U + for(U in src) + drop(U, T) + +/obj/item/integrated_circuit/manipulation/grabber/proc/update_outputs() + if(contents.len) + set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1])) + set_pin_data(IC_OUTPUT, 2, WEAKREF(contents[contents.len])) + else + set_pin_data(IC_OUTPUT, 1, null) + set_pin_data(IC_OUTPUT, 2, null) + set_pin_data(IC_OUTPUT, 3, contents.len) + set_pin_data(IC_OUTPUT, 4, contents) + push_data() + +/obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user) + drop_all() + update_outputs() + push_data() + +/obj/item/integrated_circuit/manipulation/claw + name = "pulling claw" + desc = "Circuit which can pull things.." + icon_state = "pull_claw" + extended_desc = "This circuit accepts a reference to a thing to be pulled. Modes: 0 for release. 1 for pull." + w_class = WEIGHT_CLASS_SMALL + size = 3 + cooldown_per_use = 5 + complexity = 10 + inputs = list("target" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_INDEX,"dir" = IC_PINTYPE_DIR) + outputs = list("is pulling" = IC_PINTYPE_BOOLEAN) + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT,"release" = IC_PINTYPE_PULSE_IN,"pull to dir" = IC_PINTYPE_PULSE_IN,"released" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 50 + ext_cooldown = 1 + var/max_grab = GRAB_PASSIVE + +/obj/item/integrated_circuit/manipulation/claw/do_work(ord) + var/obj/acting_object = get_object() + var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/mode = get_pin_data(IC_INPUT, 2) + switch(ord) + if(1) + mode = clamp(mode, GRAB_PASSIVE, max_grab) + if(AM) + if(check_target(AM, exclude_contents = TRUE)) + acting_object.investigate_log("grabbed ([AM]) using [src].", INVESTIGATE_CIRCUIT) + acting_object.start_pulling(AM,mode) + if(acting_object.pulling) + set_pin_data(IC_OUTPUT, 1, TRUE) + else + set_pin_data(IC_OUTPUT, 1, FALSE) + push_data() + + if(3) + stop_pulling() + + if(4) + if(acting_object.pulling) + var/dir = get_pin_data(IC_INPUT, 3) + var/turf/G =get_step(get_turf(acting_object),dir) + var/atom/movable/pullee = acting_object.pulling + var/turf/Pl = get_turf(pullee) + var/turf/F = get_step_towards(Pl,G) + if(acting_object.Adjacent(F)) + if(!step_towards(pullee, F)) + F = get_step_towards2(Pl,G) + if(acting_object.Adjacent(F)) + step_towards(pullee, F) + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/claw/stop_pulling() + set_pin_data(IC_OUTPUT, 1, FALSE) + activate_pin(5) + push_data() + ..() + +/obj/item/integrated_circuit/manipulation/thrower + name = "thrower" + desc = "A compact launcher to throw things from inside or nearby tiles at a low enough velocity not to harm someone." + extended_desc = "The first and second inputs need to be numbers which correspond to the coordinates to throw objects at relative to the machine itself. \ + The 'fire' activator will cause the mechanism to attempt to throw objects at the coordinates, if possible. Note that the \ + projectile needs to be inside the machine, or on an adjacent tile, and must be medium sized or smaller. The assembly \ + must also be a gun if you wish to throw something while the assembly is in hand." + complexity = 25 + w_class = WEIGHT_CLASS_SMALL + size = 2 + cooldown_per_use = 10 + ext_cooldown = 1 + inputs = list( + "target X rel" = IC_PINTYPE_NUMBER, + "target Y rel" = IC_PINTYPE_NUMBER, + "projectile" = IC_PINTYPE_REF + ) + outputs = list() + activators = list( + "fire" = IC_PINTYPE_PULSE_IN, + "on success"= IC_PINTYPE_PULSE_OUT, + "on fail" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + action_flags = IC_ACTION_COMBAT + power_draw_per_use = 50 + +/obj/item/integrated_circuit/manipulation/thrower/do_work(ord) + if(ord == 1) + set waitfor = FALSE // Don't sleep in a proc that is called by a processor without this set, otherwise it'll delay the entire thing + var/max_w_class = assembly.w_class + var/target_x_rel = round(get_pin_data(IC_INPUT, 1)) + var/target_y_rel = round(get_pin_data(IC_INPUT, 2)) + var/obj/item/A = get_pin_data_as_type(IC_INPUT, 3, /obj/item) + + if(!A || A.anchored || A.throwing || A == assembly || istype(A, /obj/item/transfer_valve)/* || A.GetComponent(/datum/component/two_handed)*/) + activate_pin(3) + return + + if (istype(assembly.loc, /obj/item/implant)) //Prevents the more abusive form of chestgun. + activate_pin(3) + return + + if(max_w_class && (A.w_class > max_w_class)) + activate_pin(3) + return + + if(!assembly.can_fire_equipped && ishuman(assembly.loc)) + activate_pin(3) + return + + // Is the target inside the assembly or close to it? + if(!check_target(A, exclude_components = TRUE)) + activate_pin(3) + return + + var/turf/T = get_turf(get_object()) + if(!T) + activate_pin(3) + return + + // If the item is in mob's inventory, try to remove it from there. + if(ismob(A.loc)) + var/mob/living/M = A.loc + if(A.current_equipped_slot && M.canUnEquip(A)) + visible_message(SPAN_DANGER("\The [src] is trying to remove \the [M]'s [A.name]!")) + do_atom(src, M, HUMAN_STRIP_DELAY) + if(!check_target(A, exclude_components = TRUE)) + activate_pin(3) + return + add_attack_logs("IC Assembly",src,"Removed equipment from slot [A]") + M.unEquip(A) + activate_pin(2) + return TRUE + if(!M.unEquip(A)) + activate_pin(3) + return + + // If the item is in a grabber circuit we'll update the grabber's outputs after we've thrown it. + var/obj/item/integrated_circuit/manipulation/grabber/G = A.loc + + var/x_abs = clamp(T.x + target_x_rel, 0, world.maxx) + var/y_abs = clamp(T.y + target_y_rel, 0, world.maxy) + var/range = round(clamp(sqrt(target_x_rel*target_x_rel+target_y_rel*target_y_rel),0,8),1) + + //throw it + assembly.visible_message("[assembly] has thrown [A]!") + log_attack("[assembly] [REF(assembly)] has thrown [A] with non-lethal force.") + A.forceMove(drop_location()) + A.throw_at(locate(x_abs, y_abs, T.z), range, 3, src) + + // If the item came from a grabber now we can update the outputs since we've thrown it. + if(istype(G)) + G.update_outputs() + activate_pin(2) + +/*! TBI: Material update +/obj/item/integrated_circuit/manipulation/matman + name = "material manager" + desc = "This circuit is designed for automatic storage and distribution of materials." + extended_desc = "The first input takes a ref of a machine with a material container. \ + Second input is used for inserting material stacks into the internal material storage. \ + Inputs 3-13 are used to transfer materials between target machine and circuit storage. \ + Positive values will take that number of materials from another machine. \ + Negative values will fill another machine from internal storage. Outputs show current stored amounts of mats." + icon_state = "grabber" + complexity = 16 + inputs = list( + "target" = IC_PINTYPE_REF, + "sheets to insert" = IC_PINTYPE_NUMBER, + "Metal" = IC_PINTYPE_NUMBER, + "Glass" = IC_PINTYPE_NUMBER, + "Silver" = IC_PINTYPE_NUMBER, + "Gold" = IC_PINTYPE_NUMBER, + "Diamond" = IC_PINTYPE_NUMBER, + "Uranium" = IC_PINTYPE_NUMBER, + "Solid Plasma" = IC_PINTYPE_NUMBER, + "Bluespace Mesh" = IC_PINTYPE_NUMBER, + "Bananium" = IC_PINTYPE_NUMBER, + "Titanium" = IC_PINTYPE_NUMBER, + "Plastic" = IC_PINTYPE_NUMBER + ) + outputs = list( + "self ref" = IC_PINTYPE_REF, + "Total amount" = IC_PINTYPE_NUMBER, + "Metal" = IC_PINTYPE_NUMBER, + "Glass" = IC_PINTYPE_NUMBER, + "Silver" = IC_PINTYPE_NUMBER, + "Gold" = IC_PINTYPE_NUMBER, + "Diamond" = IC_PINTYPE_NUMBER, + "Uranium" = IC_PINTYPE_NUMBER, + "Solid Plasma" = IC_PINTYPE_NUMBER, + "Bluespace Mesh" = IC_PINTYPE_NUMBER, + "Bananium" = IC_PINTYPE_NUMBER, + "Titanium" = IC_PINTYPE_NUMBER, + "Plastic" = IC_PINTYPE_NUMBER + ) + activators = list( + "insert sheet" = IC_PINTYPE_PULSE_IN, + "transfer mats" = IC_PINTYPE_PULSE_IN, + "on success" = IC_PINTYPE_PULSE_OUT, + "on failure" = IC_PINTYPE_PULSE_OUT, + "push ref" = IC_PINTYPE_PULSE_IN, + "on push ref" = IC_PINTYPE_PULSE_IN + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 40 + ext_cooldown = 1 + cooldown_per_use = 10 + var/static/list/mtypes = list( + /datum/material/iron, + /datum/material/glass, + /datum/material/silver, + /datum/material/gold, + /datum/material/diamond, + /datum/material/uranium, + /datum/material/plasma, + /datum/material/bluespace, + /datum/material/bananium, + /datum/material/titanium, + /datum/material/plastic + ) + +/obj/item/integrated_circuit/manipulation/matman/ComponentInitialize() + var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, mtypes, 100000, FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + materials.precise_insertion = TRUE + .=..() + +/obj/item/integrated_circuit/manipulation/matman/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + set_pin_data(IC_OUTPUT, 2, materials.total_amount) + for(var/I in 1 to mtypes.len) + var/datum/material/M = materials.materials[SSmaterials.GetMaterialRef(I)] + var/amount = materials[M] + if(M) + set_pin_data(IC_OUTPUT, I+2, amount) + push_data() + +/obj/item/integrated_circuit/manipulation/matman/proc/is_insertion_ready(mob/user) + return TRUE + +/obj/item/integrated_circuit/manipulation/matman/do_work(ord) + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + if(!check_target(H)) + activate_pin(4) + return + var/turf/T = get_turf(H) + switch(ord) + if(1) + var/obj/item/stack/sheet/S = H + if(!S) + activate_pin(4) + return + if(materials.insert_item(S, clamp(get_pin_data(IC_INPUT, 2),0,100), multiplier = 1) ) + AfterMaterialInsert() + activate_pin(3) + else + activate_pin(4) + if(2) + var/datum/component/material_container/mt = H.GetComponent(/datum/component/material_container) + var/suc + for(var/I in 1 to mtypes.len) + var/datum/material/M = materials.materials[mtypes[I]] + if(M) + var/U = clamp(get_pin_data(IC_INPUT, I+2),-100000,100000) + if(!U) + continue + if(!mt) //Invalid input + if(U>0) + if(materials.retrieve_sheets(U, SSmaterials.GetMaterialRef(mtypes[I]), T)) + suc = TRUE + else + if(mt.transer_amt_to(materials, U, mtypes[I])) + suc = TRUE + if(suc) + AfterMaterialInsert() + activate_pin(3) + else + activate_pin(4) + if(5) + set_pin_data(IC_OUTPUT, 1, WEAKREF(src)) + AfterMaterialInsert() + activate_pin(6) + +/obj/item/integrated_circuit/manipulation/matman/Destroy() + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + materials.retrieve_all() + .=..() +*/ + +//Hippie Ported Code-------------------------------------------------------------------------------------------------------- + + +// - inserter circuit - // +/obj/item/integrated_circuit/manipulation/inserter + name = "inserter" + desc = "A nimble circuit that puts stuff inside a storage like a backpack and can take it out aswell." + icon_state = "grabber" + extended_desc = "This circuit accepts a reference to an object to be inserted or extracted depending on mode. If a storage is given for extraction, the extracted item will be put in the new storage. Modes: 1 insert, 0 to extract." + w_class = WEIGHT_CLASS_SMALL + size = 3 + cooldown_per_use = 5 + complexity = 10 + can_be_asked_input = TRUE + inputs = list("target object" = IC_PINTYPE_REF, "target container" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_BOOLEAN) + activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"on success" = IC_PINTYPE_PULSE_OUT,"on fail" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + action_flags = IC_ACTION_COMBAT + power_draw_per_use = 20 + var/max_items = 10 + +/obj/item/integrated_circuit/manipulation/inserter/do_work(ord) + if(ord == 1) + //There shouldn't be any target required to eject all contents + var/obj/item/target_obj = get_pin_data_as_type(IC_INPUT, 1, /obj/item) + if(!target_obj) + activate_pin(3) + return + + var/distance = get_dist(get_turf(src),get_turf(target_obj)) + if(distance > 1) + activate_pin(3) + return + + var/atom/movable/A = get_pin_data(IC_INPUT, 2) + var/mode = get_pin_data(IC_INPUT, 3) + switch(mode) + if(1) + if(A && !isstorage(A) && Adjacent(A)) + A.attackby(src, target_obj) + activate_pin(2) + return TRUE + if(istype(A,/obj/item/integrated_circuit) && Adjacent(A)) + A.attackby(src, target_obj) + activate_pin(2) + return TRUE + if(0) + if(A && isstorage(A) && Adjacent(A)) + var/obj/item/storage/S = A + S.remove_from_storage(target_obj,drop_location()) + activate_pin(2) + return TRUE + return + +/obj/item/integrated_circuit/mining/mining_drill + name = "mining drill" + desc = "A mining drill that can drill through rocks." + extended_desc = "A mining drill to strike the earth. It takes some time to get the job done and \ + must remain stationary until complete." + category_text = "Manipulation" + ext_cooldown = 1 + complexity = 40 + cooldown_per_use = 3 SECONDS + ext_cooldown = 6 SECONDS + inputs = list( + "target" = IC_PINTYPE_REF + ) + outputs = list() + activators = list( + "drill" = IC_PINTYPE_PULSE_IN, + "on success" = IC_PINTYPE_PULSE_OUT, + "on failure" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 1000 + + var/busy = FALSE + var/targetlock + var/usedx + var/usedy + var/targx + var/targy + var/drill_force = 15 + var/turf/simulated/mineral + +/obj/item/integrated_circuit/mining/mining_drill/do_work(ord) + if(ord == 1) + var/atom/target = get_pin_data(IC_INPUT, 1) + var/drill_delay = null + if(!target || busy) + activate_pin(3) + return + src.assembly.visible_message(SPAN_DANGER("[assembly] starts to drill [target]!"), null, SPAN_WARNING("You hear a drill.")) + drill_delay = isturf(target)? 6 SECONDS : isliving(target) ? issimple(target) ? 2 SECONDS : 3 SECONDS : 4 SECONDS + busy = TRUE + targetlock = target + usedx = assembly.loc.x + usedy = assembly.loc.y + targx = target.loc.x + targy = target.loc.y + playsound(src, 'sound/items/drill_use.ogg',50,1) + addtimer(CALLBACK(src, .proc/drill), drill_delay) + + +/obj/item/integrated_circuit/mining/mining_drill/proc/drill() + var/atom/target = get_pin_data(IC_INPUT, 1) + busy = FALSE + // The assembly was moved, hence stopping the mining OR the rock was mined before + if(usedx != assembly.loc.x || usedy != assembly.loc.y || targx != target.loc.x || targy != target.loc.y || target != targetlock) + activate_pin(3) + return FALSE + if(isliving(target)) + if(ishuman(target)) + var/mob/living/carbon/human/S = target + S.apply_damage(drill_force, BRUTE) + return + else if(issimple(target)) + var/mob/living/simple_mob/S = target + if(S.stat == DEAD) + if(S.meat_amount > 0) + S.harvest(assembly) + return + else + S.gib() + return + else + var/mob/living/S = target + S.apply_damage(drill_force) + return + if(istype(target, /turf/simulated/mineral)) + var/turf/simulated/mineral/S = target + if(S.finds && S.finds.len) + visible_message(SPAN_WARNING("[pick("There is a crunching noise","[assembly]'s [src] collides with some different rock","Part of the rock face crumbles away","Something breaks under [assembly]'s [src]")]")) + if(istype(S, /turf/simulated/mineral)) + S.GetDrilled() + investigate_log("Drilled through [target]") + else if(istype(S, /turf/simulated/wall)) + investigate_log("Drilled through [target]") + S.ex_act(2) + else + investigate_log("Drilled through [target]") + target.ex_act(2) + activate_pin(2) + return(TRUE) + + + +// Renamer circuit. Renames the assembly it is in. Useful in cooperation with telecomms-based circuits. +/obj/item/integrated_circuit/manipulation/renamer + name = "renamer" + desc = "A small circuit that renames the assembly it is in. Useful paired with speech-based circuits." + icon_state = "internalbm" + extended_desc = "This circuit accepts a string as input, and can be pulsed to rewrite the current assembly's name with said string. On success, it pulses the default pulse-out wire." + inputs = list("name" = IC_PINTYPE_STRING) + outputs = list("current name" = IC_PINTYPE_STRING) + activators = list("rename" = IC_PINTYPE_PULSE_IN,"get name" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + power_draw_per_use = 1 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/manipulation/renamer/do_work(ord) + if(!assembly) + return + switch(ord) + if(1) + var/new_name = sanitizeSafe(get_pin_data(IC_INPUT, 1),MAX_NAME_LEN) + if(new_name) + assembly.name = new_name + + else + set_pin_data(IC_OUTPUT, 1, assembly.name) + push_data() + + activate_pin(3) + + + +// - redescribing circuit - // +/obj/item/integrated_circuit/manipulation/redescribe + name = "redescriber" + desc = "Takes any string as an input and will set it as the assembly's description." + extended_desc = "Strings should can be of any length." + icon_state = "speaker" + cooldown_per_use = 10 + complexity = 3 + inputs = list("text" = IC_PINTYPE_STRING) + outputs = list("description" = IC_PINTYPE_STRING) + activators = list("redescribe" = IC_PINTYPE_PULSE_IN,"get description" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/manipulation/redescribe/do_work(ord) + if(!assembly) + return + + switch(ord) + if(1) + assembly.desc = get_pin_data(IC_INPUT, 1) + + else + set_pin_data(IC_OUTPUT, 1, assembly.desc) + push_data() + + activate_pin(3) + +// - repainting circuit - // +/obj/item/integrated_circuit/manipulation/repaint + name = "auto-repainter" + desc = "There's an oddly high amount of spraying cans fitted right inside this circuit." + extended_desc = "Takes a value in hexadecimal and uses it to repaint the assembly it is in." + cooldown_per_use = 10 + complexity = 3 + inputs = list("color" = IC_PINTYPE_COLOR) + outputs = list("current color" = IC_PINTYPE_COLOR) + activators = list("repaint" = IC_PINTYPE_PULSE_IN,"get color" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/manipulation/repaint/do_work(ord) + if(!assembly) + return + + switch(ord) + if(1) + assembly.detail_color = get_pin_data(IC_INPUT, 1) + assembly.update_icon() + + else + set_pin_data(IC_OUTPUT, 1, assembly.detail_color) + push_data() + + activate_pin(3) + /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." @@ -11,6 +824,7 @@ complexity = 20 w_class = ITEMSIZE_NORMAL size = 3 + can_be_asked_input = TRUE inputs = list( "target X rel" = IC_PINTYPE_NUMBER, "target Y rel" = IC_PINTYPE_NUMBER @@ -28,7 +842,12 @@ installed_gun = null // It will be qdel'd by ..() if still in our contents return ..() -/obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user) +/obj/item/integrated_circuit/manipulation/weapon_firing/proc/ask_for_input(obj/item/I, mob/living/user, a_intent) + if(!isobj(I)) + return FALSE + attackby_react(I, user, a_intent) + +/obj/item/integrated_circuit/manipulation/weapon_firing/attackby_react(var/obj/O, var/mob/user) if(istype(O, /obj/item/gun)) var/obj/item/gun/gun = O if(installed_gun) @@ -100,43 +919,17 @@ 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 = ITEMSIZE_NORMAL - 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(T && assembly) - if(assembly.anchored || !assembly.can_move()) - return - if(assembly.loc == T) // Check if we're held by someone. If the loc is the floor, we're not held. - var/datum/integrated_io/wanted_dir = inputs[1] - if(isnum(wanted_dir.data)) - step(assembly, 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. \ + 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 + can_be_asked_input = TRUE inputs = list("detonation time" = IC_PINTYPE_NUMBER) outputs = list() activators = list("prime grenade" = IC_PINTYPE_PULSE_IN) @@ -157,7 +950,12 @@ detach_grenade() . =..() -/obj/item/integrated_circuit/manipulation/grenade/attackby(var/obj/item/grenade/G, var/mob/user) +/obj/item/integrated_circuit/manipulation/grenade/proc/ask_for_input(obj/item/I, mob/living/user, a_intent) + if(!isobj(I)) + return FALSE + attackby_react(I, user, a_intent) + +/obj/item/integrated_circuit/manipulation/grenade/attackby_react(var/obj/item/grenade/G, var/mob/user) if(istype(G)) if(attached_grenade) to_chat(user, "There is already a grenade attached!") @@ -183,9 +981,9 @@ attached_grenade.det_time = between(1, detonation_time.data, 12) SECONDS attached_grenade.activate() var/atom/holder = loc - log_and_message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]") + 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 +/// These procs do not relocate the grenade, that's the callers responsibility /obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/grenade/G) attached_grenade = G RegisterSignal(attached_grenade, COMSIG_PARENT_QDELETING, .proc/detach_grenade) diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index b23971065c2..49813fc57b9 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -120,7 +120,7 @@ /obj/item/integrated_circuit/output/text_to_speech name = "text-to-speech circuit" - desc = "A miniature speaker is attached to this component. It is able to transpose any valid text to speech." + desc = "A miniature speaker is attached to this component. It is able to transpose any valid text to speech." extended_desc = "This will emit an audible message to anyone who can hear the assembly." icon_state = "speaker" complexity = 12 @@ -139,7 +139,7 @@ /obj/item/integrated_circuit/output/text_to_speech/advanced name = "advanced text-to-speech circuit" - desc = "A miniature speaker is attached to this component. It is able to transpose any valid text to speech, matching a scanned target's voice." + desc = "A miniature speaker is attached to this component. It is able to transpose any valid text to speech, matching a scanned target's voice." complexity = 15 cooldown_per_use = 6 SECONDS inputs = list("text" = IC_PINTYPE_STRING, "mimic target" = IC_PINTYPE_REF) @@ -183,9 +183,9 @@ /obj/item/integrated_circuit/output/sound/Initialize(mapload) . = ..() extended_desc = list() - extended_desc += "The first input pin determines which sound is used. The choices are; " + 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 += ". 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) @@ -382,7 +382,7 @@ The assembly must be able to see the object to make a holographic copy of it.
\ Scaling is capped between -2 and 2.
\ The rotation pin uses degrees.
\ - Imitated object cannot be changed while projecting. Position, \ + Imitated object cannot be changed while projecting. Position, \ scale, and rotation can be updated without restarting by pulsing the update hologram pin." complexity = 40 icon_state = "holo_projector" diff --git a/code/modules/integrated_electronics/subtypes/power.dm b/code/modules/integrated_electronics/subtypes/power.dm index 294c8b76d0f..18efb5900b4 100644 --- a/code/modules/integrated_electronics/subtypes/power.dm +++ b/code/modules/integrated_electronics/subtypes/power.dm @@ -5,7 +5,7 @@ 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 \ + 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." @@ -25,11 +25,12 @@ /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 \ + 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." + some power is lost due to ineffiency. Warning! Don't stack more than 1 power transmitter, as it becomes less efficient for every other \ + transmission circuit in its own assembly and other nearby ones." w_class = ITEMSIZE_LARGE complexity = 32 origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 6, TECH_MAGNET = 5) @@ -39,53 +40,56 @@ /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/cell/cell = null - if(istype(AM, /obj/item/cell)) // Is this already a cell? - cell = AM - else // If not, maybe there's a cell inside it? + if(!AM) + return FALSE + if(istype(AM, /obj/item/gun/energy)) + return FALSE + if(!assembly) + return FALSE // Pointless to do everything else if there's no battery to draw from. + var/obj/item/cell/cell = AM.get_cell() + /*if(cell) + if(istype(AM, /obj/item/cell)) // Is this already a cell? + cell = AM + else // If not, maybe there's a cell inside it? for(var/obj/item/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. + 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.fully_charged()) - return FALSE + if(cell.fully_charged()) + return FALSE - if(transfer_amount && assembly.draw_power(amount_to_move)) // CELLRATE is already handled in draw_power() - cell.give(DYNAMIC_W_TO_CELL_UNITS(transfer_amount, 1)) - AM.update_icon() + if(transfer_amount && assembly.draw_power(amount_to_move)) // CELLRATE is already handled in draw_power() + cell.give(DYNAMIC_W_TO_CELL_UNITS(transfer_amount, 1)) + AM.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) + 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 FALSE - return FALSE + 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 /obj/item/integrated_circuit/power/transmitter/large/do_work() if(..()) // If the above code succeeds, do this below. - if(prob(2)) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread() - sparks.set_up(3, 0, get_turf(src)) - sparks.start() - visible_message("\The [assembly] makes some sparks!") - qdel(sparks) + var/atom/movable/acting_object = get_object() + if(prob(20)) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(12, 1, src) + s.start() + acting_object.visible_message("\The [acting_object] makes some sparks!") + return TRUE diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm index 935dc184d3a..d61c845567c 100644 --- a/code/modules/integrated_electronics/subtypes/reagents.dm +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -1,5 +1,6 @@ /obj/item/integrated_circuit/reagent category_text = "Reagent" + cooldown_per_use = 10 var/volume = 0 unacidable = 1 origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) @@ -8,6 +9,11 @@ . = ..() if(volume) create_reagents(volume) + push_vol() + +/obj/item/integrated_circuit/reagent/proc/push_vol() + set_pin_data(IC_OUTPUT, 1, reagents.total_volume) + push_data() /obj/item/integrated_circuit/reagent/smoke name = "smoke generator" @@ -19,7 +25,7 @@ complexity = 20 cooldown_per_use = 30 SECONDS inputs = list() - outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF) + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_SELFREF) 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) @@ -47,137 +53,145 @@ /obj/item/integrated_circuit/reagent/injector name = "integrated hypo-injector" - desc = "This scary looking thing is able to pump liquids into whatever it's pointed at." + desc = "This scary looking thing is able to pump liquids into, or suck liquids out of, 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. A negative amount makes the injector draw out reagents." + extended_desc = "This autoinjector can push up to 30 units of 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. A negative quantity inverts the injector, sucking out reagents instead." flags = OPENCONTAINER + volume = 30 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) + outputs = list( + "volume used" = IC_PINTYPE_NUMBER, + "self reference" = IC_PINTYPE_SELFREF + ) + activators = list( + "push ref" = IC_PINTYPE_PULSE_IN, + "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/direction_mode = SYRINGE_INJECT var/transfer_amount = 10 + var/busy = FALSE -/obj/item/integrated_circuit/reagent/injector/interact(mob/user) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - ..() +/obj/item/integrated_circuit/reagent/injector/Initialize(mapload) + . = ..() + reagents.reagents_holder_flags |= OPENCONTAINER - -/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_reagent_change(changetype) + push_vol() /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 + direction_mode = SYRINGE_DRAW else - direc = 1 + direction_mode = SYRINGE_INJECT 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 +/obj/item/integrated_circuit/reagent/injector/do_work(ord) + switch(ord) + if(1) + inject() + if(4) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + +/obj/item/integrated_circuit/reagent/injector/proc/inject() + set waitfor = FALSE // Don't sleep in a proc that is called by a processor without this set, otherwise it'll delay the entire thing var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/atom/movable/acting_object = get_object() + + if(busy || !check_target(AM)) + activate_pin(3) + return + if(!istype(AM)) //Invalid input activate_pin(3) return - if(direc == 1) + if(!AM.reagents) + activate_pin(3) + return - if(!istype(AM)) //Invalid input + if(direction_mode == SYRINGE_INJECT) + if(!reagents.total_volume || !AM.can_be_injected_by(src) || AM.reagents.holder_full()) activate_pin(3) return - if(!reagents.total_volume) // Empty + if(isliving(AM)) + var/mob/living/L = AM + if(!L.can_inject(null, 0)) + activate_pin(3) + return + +//Always log attemped injections for admins + var/contained = reagents.log_list() + log_attack(src, L, "attempted to inject [L] which had [contained]") + L.visible_message("[acting_object] is trying to inject [L]!", \ + "[acting_object] is trying to inject you!") + busy = TRUE + if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0))) + reagents.trans_to(L, transfer_amount) + log_attack(src, L, "attempted to inject [L] which had [contained]") + L.visible_message("[acting_object] injects [L] with its needle!", \ + "[acting_object] injects you with its needle!") + else + busy = FALSE + activate_pin(3) + return + busy = FALSE + else + reagents.trans_to(AM, transfer_amount) + if(direction_mode == SYRINGE_DRAW) + if(reagents.total_volume >= reagents.maximum_volume) + acting_object.visible_message("[acting_object] tries to draw from [AM], but the injector is full.") 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)) + + var/tramount = abs(transfer_amount) + + if(isliving(AM)) + var/mob/living/L = AM + L.visible_message("[acting_object] is trying to take a blood sample from [L]!", \ + "[acting_object] is trying to take a blood sample from you!") + busy = TRUE + if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0))) + var/mob/living/carbon/LB = L + if(LB.take_blood(src, tramount)) + L.visible_message("[acting_object] takes a blood sample from [L]!", \ + "[acting_object] takes a blood sample from you!") + else + L.visible_message("[acting_object] fails to take a blood sample from [L].", \ + "[acting_object] fails to take a blood sample from you!") + busy = FALSE activate_pin(3) return - var/contained = reagents.get_reagents() - var/trans = reagents.trans_to_mob(L, transfer_amount, CHEM_BLOOD) - 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 + busy = FALSE - 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_obj(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_obj(src, tramount)) - activate_pin(2) - else - activate_pin(3) - return - var/datum/reagent/B - if(istype(T, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = T - if(H.species && !H.should_have_organ(O_HEART)) - H.reagents.trans_to_obj(src, tramount) - else - B = T.take_blood(src, tramount) - else - 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 + else + if(!AM.reagents.total_volume) + acting_object.visible_message("[acting_object] tries to draw from [AM], but it is empty!") activate_pin(3) return - else //if not mob - if(!target.reagents.total_volume) - visible_message( "[target] is empty.") + if(!AM.can_be_injected_by()) activate_pin(3) return - target.reagents.trans_to_obj(src, tramount) + tramount = min(tramount, AM.reagents.total_volume) + AM.reagents.trans_to(src, tramount) activate_pin(2) - /obj/item/integrated_circuit/reagent/pump name = "reagent pump" desc = "Moves liquids safely inside a machine, or even nearby it." @@ -234,6 +248,98 @@ target.reagents.trans_to(source, transfer_amount) activate_pin(2) +/obj/item/integrated_circuit/input/beaker_connector + category_text = "Reagent" + cooldown_per_use = 1 + name = "beaker slot" + desc = "Lets you add a beaker to your assembly and remove it even when the assembly is closed." + icon_state = "reagent_storage" + extended_desc = "It can help you extract reagents easier." + complexity = 4 + can_be_asked_input = TRUE + inputs = list() + outputs = list( + "volume used" = IC_PINTYPE_NUMBER, + "current beaker" = IC_PINTYPE_REF + ) + activators = list( + "on insert" = IC_PINTYPE_PULSE_OUT, + "on remove" = IC_PINTYPE_PULSE_OUT, + "push ref" = IC_PINTYPE_PULSE_OUT + ) + + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + can_be_asked_input = TRUE + + var/obj/item/reagent_containers/glass/beaker/current_beaker + +/obj/item/integrated_circuit/input/beaker_connector/ask_for_input(obj/item/I, mob/living/user, a_intent) + if(!isobj(I)) + return FALSE + attackby_react(I, user, a_intent) + +/obj/item/integrated_circuit/input/beaker_connector/attackby_react(var/obj/item/reagent_containers/I, var/mob/living/user) + //Check if it truly is a reagent container + if(!istype(I,/obj/item/reagent_containers/glass/beaker)) + to_chat(user,"The [I.name] doesn't seem to fit in here.") + return + + //Check if there is no other beaker already inside + if(current_beaker) + to_chat(user,"There is already a reagent container inside.") + return + + //The current beaker is the one we just attached, its location is inside the circuit + current_beaker = I + user.transferItemToLoc(I,src) + + to_chat(user,"You put the [I.name] inside the beaker connector.") + + //Set the pin to a weak reference of the current beaker + push_vol() + set_pin_data(IC_OUTPUT, 2, WEAKREF(current_beaker)) + push_data() + activate_pin(1) + activate_pin(3) + + +/obj/item/integrated_circuit/input/beaker_connector/ask_for_input(mob/user) + attack_self(user) + + +/obj/item/integrated_circuit/input/beaker_connector/attack_self(mob/user) + //Check if no beaker attached + if(!current_beaker) + to_chat(user, "There is currently no beaker attached.") + return + + //Remove beaker and put in user's hands/location + to_chat(user, "You take [current_beaker] out of the beaker connector.") + user.put_in_hands(current_beaker) + current_beaker = null + //Remove beaker reference + push_vol() + set_pin_data(IC_OUTPUT, 2, null) + push_data() + activate_pin(2) + activate_pin(3) + + +/obj/item/integrated_circuit/input/beaker_connector/proc/push_vol() + var/beakerVolume = 0 + if(current_beaker) + beakerVolume = current_beaker.reagents.total_volume + + set_pin_data(IC_OUTPUT, 1, beakerVolume) + push_data() + + +/obj/item/reagent_containers/glass/beaker/on_reagent_change() + ..() + if(istype(loc,/obj/item/integrated_circuit/input/beaker_connector)) + var/obj/item/integrated_circuit/input/beaker_connector/current_circuit = loc + current_circuit.push_vol() + /obj/item/integrated_circuit/reagent/storage name = "reagent storage" desc = "Stores liquid inside, and away from electrical components. Can store up to 60u." @@ -268,6 +374,35 @@ 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/heater + name = "chemical heater" + desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. It will heat or cool the reagents \ + to the target temperature when turned on." + icon_state = "heater" + complexity = 8 + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + inputs_default = list("1" = 300) + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_SELFREF,"temperature" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_RESEARCH + var/heater_coefficient = 0.1 + +/obj/item/integrated_circuit/reagent/storage/heater/on_data_written() + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/reagent/storage/heater/Initialize() + .=..() + START_PROCESSING(SScircuit, src) + +/obj/item/integrated_circuit/reagent/storage/heater/Destroy() + STOP_PROCESSING(SScircuit, src) + return ..() + /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." @@ -279,6 +414,129 @@ 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/funnel + category_text = "Reagent" + name = "reagent funnel" + desc = "A funnel with a small pump that lets you refill an internal reagent storage." + icon_state = "reagent_funnel" + can_be_asked_input = TRUE + inputs = list( + "target" = IC_PINTYPE_REF + ) + activators = list( + "on transfer" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + complexity = 4 + power_draw_per_use = 5 + +/obj/item/integrated_circuit/reagent/funnel/proc/ask_for_input(obj/item/I, mob/living/user, a_intent) + if(!isobj(I)) + return FALSE + attackby_react(I, user, a_intent) + +/obj/item/integrated_circuit/reagent/funnel/attackby_react(obj/item/I, mob/living/user, intent) + var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) + var/obj/item/reagent_containers/container = I + + if(!check_target(target) || !I.reagents) + return FALSE + + if(container.standard_pour_into(user, target)) + activate_pin(1) + return TRUE + + return FALSE + +/obj/item/integrated_circuit/reagent/tubing + name = "tubing" + desc = "A length of flexible piping that can be used to connect one container to another." + extended_desc = "Use these to supply your fuel cell with never-ending phoron! Beware of leaks." + icon_state = "reagent_funnel" + flags = OPENCONTAINER + inputs = list( + "toggle cap" = IC_PINTYPE_BOOLEAN, + "source vol" = IC_PINTYPE_NUMBER, + "destination vol" = IC_PINTYPE_NUMBER + ) + outputs = list( + "volume" = IC_PINTYPE_NUMBER + ) + power_draw_per_use = 0 + complexity = 1 + volume = 5 + next_use = null + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/reagent/tubing/on_data_written() + if(world.time < next_use) + return + // Vars for final comparison. + var/s_vol = get_pin_data(IC_INPUT, 2) + var/d_vol = get_pin_data(IC_INPUT, 3) + var/obj/item/integrated_circuit/s = get_pin_linked_src(IC_INPUT, 2) + var/obj/item/integrated_circuit/d = get_pin_linked_src(IC_INPUT, 3) + // Try to transfer any pipe contents to an attached container. If no container, spill. + src.reagents.total_volume ? (d && d.reagents) ? reagents.trans_to(d, 5) : reagents.splash_area(assembly.loc, 0) : null + if(s && s.reagents.total_volume) + s.reagents.trans_to(src, 5) + if(s_vol != get_pin_data(IC_INPUT, 2) || d_vol != get_pin_data(IC_INPUT, 3)) + spawn(5) on_data_written() + next_use = world.time + 5 + push_vol() + +/obj/item/integrated_circuit/reagent/storage/grinder + name = "reagent grinder" + desc = "This is a reagent grinder. It accepts a ref to something, and refines it into reagents. It can store up to 100u." + icon_state = "blender" + extended_desc = "" + inputs = list( + "target" = IC_PINTYPE_REF + ) + outputs = list( + "volume used" = IC_PINTYPE_NUMBER, + "self reference" = IC_PINTYPE_SELFREF + ) + activators = list( + "grind" = IC_PINTYPE_PULSE_IN, + "on grind" = IC_PINTYPE_PULSE_OUT, + "on fail" = IC_PINTYPE_PULSE_OUT, + "push ref" = IC_PINTYPE_PULSE_IN + ) + flags = OPENCONTAINER + volume = 100 + power_draw_per_use = 150 + complexity = 16 + spawn_flags = IC_SPAWN_RESEARCH + + +/obj/item/integrated_circuit/reagent/storage/grinder/do_work(ord) + switch(ord) + if(1) + grind() + if(4) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + +/obj/item/integrated_circuit/reagent/storage/grinder/proc/grind() + if(reagents.total_volume >= reagents.maximum_volume) + activate_pin(3) + return FALSE + var/obj/item/I = get_pin_data_as_type(IC_INPUT, 1, /obj/item) + if(istype(I) && (I.reagents.total_volume) && check_target(I)) + var/list/reagent_names_list = list() + for(var/datum/reagent/R in reagents?.reagent_list) + reagent_names_list.Add(R.name) + var/atom/AM = get_object() + AM.investigate_log("ground reagents: [jointext(reagent_names_list, ", ")] with [src].", INVESTIGATE_CIRCUIT) + playsound(src, 'sound/machines/blender.ogg', 50, 1) + I.reagents.trans_to(src, I.reagents.total_volume) + qdel(I) + activate_pin(2) + return TRUE + activate_pin(3) + return FALSE + /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." @@ -303,10 +561,10 @@ 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. \ + 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." + 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) @@ -356,5 +614,57 @@ activate_pin(2) push_data() +/obj/item/integrated_circuit/reagent/extinguisher + name = "integrated extinguisher" + desc = "This circuit sprays any of its contents out like an extinguisher." + icon_state = "injector" + extended_desc = "This circuit can hold up to 30 units of any given chemicals. On each use, it sprays these reagents like a fire extinguisher." + + volume = 30 + flags = OPENCONTAINER + + complexity = 20 + cooldown_per_use = 6 SECONDS + inputs = list( + "target" = IC_PINTYPE_REF, + ) + outputs = list( + "volume" = IC_PINTYPE_NUMBER, + "self reference" = IC_PINTYPE_SELFREF + ) + activators = list( + "spray" = IC_PINTYPE_PULSE_IN, + "on sprayed" = IC_PINTYPE_PULSE_OUT, + "on fail" = IC_PINTYPE_PULSE_OUT, + "push ref" = IC_PINTYPE_PULSE_IN + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 15 + var/busy = FALSE + +/obj/item/integrated_circuit/reagent/extinguisher/Initialize() + .=..() + set_pin_data(IC_OUTPUT,2, src) + +/obj/item/integrated_circuit/reagent/extinguisher/on_reagent_change(changetype) + push_vol() + +/obj/item/integrated_circuit/reagent/extinguisher/do_work() + //Check if enough volume + if(!reagents || reagents.total_volume < 10) + push_data() + activate_pin(3) + return + + var/mob/living/user = null + if(ismob(src.loc)) + user = src.loc + else if(ismob(src.loc.loc)) + user = src.loc.loc + var/atom/movable/A = get_pin_data(IC_INPUT, 1) + if(!extinguish_spray(A, user, 10, spray_size = 3, delay = 10, spray_particles = 3)) + activate_pin(3) + push_vol() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index 6d5a86455e6..abb1dbdb9c7 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -4,13 +4,13 @@ /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 \ + 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) + complexity = 5 + inputs = list("target" = IC_PINTYPE_REF,"ignore obstacles" = IC_PINTYPE_BOOLEAN) outputs = list("dir" = IC_PINTYPE_DIR) - activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT) + activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_RESEARCH origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 5) power_draw_per_use = 40 @@ -18,17 +18,355 @@ /obj/item/integrated_circuit/smart/basic_pathfinder/do_work() var/datum/integrated_io/I = inputs[1] set_pin_data(IC_OUTPUT, 1, null) - if(!isweakref(I.data)) + activate_pin(3) return var/atom/A = I.data.resolve() if(!A) + activate_pin(3) return if(!(A in view(get_turf(src)))) push_data() + activate_pin(3) 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) + if(get_pin_data(IC_INPUT, 2)) + set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A))) + else + set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_step_towards2(get_turf(src),A))) push_data() activate_pin(2) + +/obj/item/integrated_circuit/smart/coord_basic_pathfinder + name = "coordinate pathfinder" + desc = "This complex circuit is able to determine what direction a given target is." + extended_desc = "This circuit uses absolute coordinates to determine where the target is. If the machine \ + cannot see the target, it will not be able to calculate the correct direction. \ + This circuit will only work while inside an assembly." + icon_state = "numberpad" + complexity = 5 + inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER,"ignore obstacles" = IC_PINTYPE_BOOLEAN) + outputs = list( "dir" = IC_PINTYPE_DIR, + "distance" = IC_PINTYPE_NUMBER + ) + activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 40 + +/obj/item/integrated_circuit/smart/coord_basic_pathfinder/do_work() + if(!assembly) + activate_pin(3) + return + var/turf/T = get_turf(assembly) + var/target_x = clamp(get_pin_data(IC_INPUT, 1), 0, world.maxx) + var/target_y = clamp(get_pin_data(IC_INPUT, 2), 0, world.maxy) + var/turf/A = locate(target_x, target_y, T.z) + set_pin_data(IC_OUTPUT, 1, null) + if(!A||A==T) + activate_pin(3) + return + if(get_pin_data(IC_INPUT, 2)) + set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A))) + else + set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_step_towards2(get_turf(src),A))) + set_pin_data(IC_OUTPUT, 2, sqrt((A.x-T.x)*(A.x-T.x)+ (A.y-T.y)*(A.y-T.y))) + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/smart/advanced_pathfinder + name = "advanced pathfinder" + desc = "This circuit uses a complex processor for long-range pathfinding." + extended_desc = "This circuit uses absolute coordinates to find its target. A path will be generated to the target, taking obstacles into account, \ + and pathing around any instances of said input. The passkey provided from a card reader is used to calculate a valid path through airlocks." + icon_state = "numberpad" + complexity = 40 + cooldown_per_use = 50 + inputs = list("X target" = IC_PINTYPE_NUMBER,"Y target" = IC_PINTYPE_NUMBER,"obstacle" = IC_PINTYPE_REF,"access" = IC_PINTYPE_STRING) + outputs = list("X" = IC_PINTYPE_LIST,"Y" = IC_PINTYPE_LIST) + activators = list("calculate path" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 80 + var/obj/item/card/id/idc + +/obj/item/integrated_circuit/smart/advanced_pathfinder/Initialize(mapload) + .=..() + idc = new(src) + +/obj/item/integrated_circuit/smart/advanced_pathfinder/do_work() + if(!assembly) + activate_pin(3) + return + idc.access = assembly.access_card.access + var/turf/a_loc = get_turf(assembly) + var/list/P = get_path_to(assembly, locate(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2),a_loc.z), 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT,3, /atom)), simulated_only = 0) + + if(!P) + activate_pin(3) + return + else + var/list/Xn = new/list(P.len) + var/list/Yn = new/list(P.len) + var/turf/T + for(var/i =1 to P.len) + T=P[i] + Xn[i] = T.x + Yn[i] = T.y + set_pin_data(IC_OUTPUT, 1, Xn) + set_pin_data(IC_OUTPUT, 2, Yn) + push_data() + activate_pin(2) + + +//Hippie Ported Code-------------------------------------------------------------------------------------------------------- + + + +// - MMI Tank - // +/* TBI MMI tank +/obj/item/integrated_circuit/input/mmi_tank + name = "man-machine interface tank" + desc = "This circuit is just a jar filled with an artificial liquid mimicking the cerebrospinal fluid." + extended_desc = "This jar can hold 1 man-machine interface and let it take control of some basic functions of the assembly." + complexity = 60 + can_be_asked_input = TRUE + inputs = list("laws" = IC_PINTYPE_LIST) + outputs = list( + "man-machine interface" = IC_PINTYPE_REF, + "direction" = IC_PINTYPE_DIR, + "click target" = IC_PINTYPE_REF + ) + activators = list( + "move" = IC_PINTYPE_PULSE_OUT, + "left" = IC_PINTYPE_PULSE_OUT, + "right" = IC_PINTYPE_PULSE_OUT, + "up" = IC_PINTYPE_PULSE_OUT, + "down" = IC_PINTYPE_PULSE_OUT, + "leftclick" = IC_PINTYPE_PULSE_OUT, + "shiftclick" = IC_PINTYPE_PULSE_OUT, + "altclick" = IC_PINTYPE_PULSE_OUT, + "ctrlclick" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 150 + can_be_asked_input = TRUE + demands_object_input = TRUE + + var/obj/item/mmi/installed_brain + +/obj/item/integrated_circuit/input/mmi_tank/attackby_react(var/obj/item/mmi/O, var/mob/user) + if(!istype(O,/obj/item/mmi)) + to_chat(user,"You can't put that inside.") + return + if(installed_brain) + to_chat(user,"There's already a brain inside.") + return + user.transferItemToLoc(O,src) + installed_brain = O + can_be_asked_input = FALSE + to_chat(user, "You gently place \the man-machine interface inside the tank.") + to_chat(O, "You are slowly being placed inside the man-machine-interface tank.") + O.brainmob.remote_control=src + set_pin_data(IC_OUTPUT, 1, O) + +/obj/item/integrated_circuit/input/mmi_tank/attack_self(var/mob/user) + if(installed_brain) + RemoveBrain() + to_chat(user, "You slowly lift [installed_brain] out of the MMI tank.") + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + installed_brain = null + push_data() + else + to_chat(user, "You don't see any brain swimming in the tank.") + +/obj/item/integrated_circuit/input/mmi_tank/Destroy() + RemoveBrain() + ..() + +/obj/item/integrated_circuit/input/mmi_tank/relaymove(var/n,var/dir) + set_pin_data(IC_OUTPUT, 2, dir) + do_work(1) + switch(dir) + if(8) activate_pin(2) + if(4) activate_pin(3) + if(1) activate_pin(4) + if(2) activate_pin(5) + +/obj/item/integrated_circuit/input/mmi_tank/do_work(var/n) + push_data() + activate_pin(n) + +/obj/item/integrated_circuit/input/mmi_tank/proc/RemoveBrain() + if(installed_brain) + can_be_asked_input = TRUE + installed_brain.forceMove(drop_location()) + set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) + if(installed_brain.brainmob) + installed_brain.brainmob.remote_control = null + + +//Brain changes +/mob/living/brain/var/check_bot_self = FALSE + +/mob/living/brain/ClickOn(atom/A, params) + ..() + if(!istype(remote_control,/obj/item/integrated_circuit/input/mmi_tank)) + return + var/obj/item/integrated_circuit/input/mmi_tank/brainholder=remote_control + brainholder.set_pin_data(IC_OUTPUT, 3, A) + var/list/modifiers = params2list(params) + + if(modifiers["shift"]) + brainholder.do_work(7) + return + if(modifiers["alt"]) + brainholder.do_work(8) + return + if(modifiers["ctrl"]) + brainholder.do_work(9) + return + + if(istype(A,/obj/item/electronic_assembly)) + var/obj/item/electronic_assembly/CheckedAssembly = A + + if(brainholder in CheckedAssembly.assembly_components) + var/obj/item/electronic_assembly/holdingassembly=A + check_bot_self=TRUE + + if(holdingassembly.opened) + holdingassembly.ui_interact(src) + holdingassembly.attack_self(src) + check_bot_self=FALSE + return + + brainholder.do_work(6) + +/mob/living/brain/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) + return check_bot_self +*/ + +/* TBI pAI connector +// - pAI connector circuit - // +/obj/item/integrated_circuit/input/pAI_connector + name = "pAI connector circuit" + desc = "This circuit lets you fit in a personal artificial intelligence to give it some form of control over the bot." + extended_desc = "You can wire various functions to it." + complexity = 60 + can_be_asked_input = TRUE + inputs = list("laws" = IC_PINTYPE_LIST) + outputs = list( + "personal artificial intelligence" = IC_PINTYPE_REF, + "direction" = IC_PINTYPE_DIR, + "click target" = IC_PINTYPE_REF + ) + activators = list( + "move" = IC_PINTYPE_PULSE_OUT, + "left" = IC_PINTYPE_PULSE_OUT, + "right" = IC_PINTYPE_PULSE_OUT, + "up" = IC_PINTYPE_PULSE_OUT, + "down" = IC_PINTYPE_PULSE_OUT, + "leftclick" = IC_PINTYPE_PULSE_OUT, + "shiftclick" = IC_PINTYPE_PULSE_OUT, + "altclick" = IC_PINTYPE_PULSE_OUT, + "ctrlclick" = IC_PINTYPE_PULSE_OUT, + "shiftctrlclick" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 150 + can_be_asked_input = TRUE + demands_object_input = TRUE + + var/obj/item/paicard/installed_pai + +/obj/item/integrated_circuit/input/pAI_connector/attackby_react(var/obj/item/paicard/O, var/mob/user) + if(!istype(O,/obj/item/paicard)) + to_chat(user,"You can't put that inside.") + return + if(installed_pai) + to_chat(user,"There's already a pAI connected to this.") + return + user.transferItemToLoc(O,src) + installed_pai = O + can_be_asked_input = FALSE + to_chat(user, "You slowly connect the circuit's pins to the [installed_pai].") + to_chat(O, "You are slowly being connected to the pAI connector.") + O.pai.remote_control=src + set_pin_data(IC_OUTPUT, 1, O) + +/obj/item/integrated_circuit/input/pAI_connector/attack_self(var/mob/user) + if(installed_pai) + RemovepAI() + to_chat(user, "You slowly disconnect the circuit's pins from the [installed_pai].") + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + installed_pai = null + push_data() + else + to_chat(user, "The connection port is empty.") + +/obj/item/integrated_circuit/input/pAI_connector/relaymove(var/n,var/dir) + set_pin_data(IC_OUTPUT, 2, dir) + do_work(1) + switch(dir) + if(8) activate_pin(2) + if(4) activate_pin(3) + if(1) activate_pin(4) + if(2) activate_pin(5) + +/obj/item/integrated_circuit/input/pAI_connector/do_work(var/n) + push_data() + activate_pin(n) + + +/obj/item/integrated_circuit/input/pAI_connector/Destroy() + RemovepAI() + ..() + +/obj/item/integrated_circuit/input/pAI_connector/proc/RemovepAI() + if(installed_pai) + can_be_asked_input = TRUE + installed_pai.forceMove(drop_location()) + set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) + installed_pai.pai.remote_control = null + + +//pAI changes +/mob/living/silicon/pai/var/check_bot_self = FALSE + +/mob/living/silicon/pai/ClickOn(atom/A, params) + ..() + if(!istype(remote_control,/obj/item/integrated_circuit/input/pAI_connector)) + return + var/obj/item/integrated_circuit/input/pAI_connector/paiholder=remote_control + paiholder.set_pin_data(IC_OUTPUT, 3, A) + var/list/modifiers = params2list(params) + + if(modifiers["shift"] && modifiers["ctrl"]) + paiholder.do_work(10) + return + if(modifiers["shift"]) + paiholder.do_work(7) + return + if(modifiers["alt"]) + paiholder.do_work(8) + return + if(modifiers["ctrl"]) + paiholder.do_work(9) + return + + if(istype(A,/obj/item/electronic_assembly)) + var/obj/item/electronic_assembly/CheckedAssembly = A + + if(paiholder in CheckedAssembly.assembly_components) + var/obj/item/electronic_assembly/holdingassembly=A + check_bot_self=TRUE + + if(holdingassembly.opened) + holdingassembly.ui_interact(src) + holdingassembly.attack_self(src) + check_bot_self=FALSE + return + + paiholder.do_work(6) + +/mob/living/silicon/pai/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) + return check_bot_self +*/ diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm index 5c8b916f2ab..45134d0db86 100644 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -1,60 +1,94 @@ /obj/item/integrated_circuit/time name = "time circuit" desc = "Now you can build your own clock!" - complexity = 2 + complexity = 1 inputs = list() outputs = list() category_text = "Time" /obj/item/integrated_circuit/time/delay - name = "delay circuit" - desc = "This sends a pulse signal out after a delay defined in tenths of a second, 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. \ + name = "two-sec delay circuit" + desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ + This circuit is set to send a pulse after a delay of two seconds." + icon_state = "delay-20" + var/delay = 20 + activators = list("incoming"= IC_PINTYPE_PULSE_IN,"outgoing" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 2 + +/obj/item/integrated_circuit/time/delay/do_work() + addtimer(CALLBACK(src, .proc/activate_pin, 2), delay) + +/obj/item/integrated_circuit/time/delay/five_sec + name = "five-sec delay circuit" + desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ + This circuit is set to send a pulse after a delay of five seconds." + icon_state = "delay-50" + delay = 50 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/time/delay/one_sec + name = "one-sec delay circuit" + desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ + This circuit is set to send a pulse after a delay of one second." + icon_state = "delay-10" + delay = 10 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/time/delay/half_sec + name = "half-sec delay circuit" + desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ + This circuit is set to send a pulse after a delay of half a second." + icon_state = "delay-5" + delay = 5 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/time/delay/tenth_sec + name = "tenth-sec delay circuit" + desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ + This circuit is set to send a pulse after a delay of 1/10th of a second." + icon_state = "delay-1" + delay = 1 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/time/delay/custom + name = "custom delay circuit" + desc = "This sends a pulse signal out after a delay defined in tenths of a second, 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." extended_desc = "The delay is defined in tenths of a second. For instance, 4 will be a delay of 0.4 seconds, or 15 for 1.5 seconds." icon_state = "delay" inputs = list("delay time" = IC_PINTYPE_NUMBER) - 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 - var/delay = 20 + spawn_flags = IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/time/delay/do_work() +/obj/item/integrated_circuit/time/delay/custom/do_work() var/delay_input = get_pin_data(IC_INPUT, 1) if(delay_input && isnum(delay_input) ) var/new_delay = clamp(delay_input, 1, 1 HOUR) delay = new_delay - addtimer(CALLBACK(src, .proc/activate_pin, 2), delay) + ..() + /obj/item/integrated_circuit/time/ticker name = "ticker circuit" - desc = "This circuit sends an automatic pulse every given interval, defined in tenths of a second." - extended_desc ="This circuit sends an automatic pulse every given interval, defined in tenths of a second. \ - For example, setting the time pin to 4 will send a pulse every 0.4 seconds, or 15 for every 1.5 seconds.
\ - The power consumption will scale based on how fast this ticks. Also, note that most components have a short \ - internal cooldown when activated." - icon_state = "tick-f" - complexity = 10 - inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN, "delay time" = IC_PINTYPE_NUMBER) - activators = list("outgoing pulse" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 5 - var/delay = 2 SECONDS + desc = "This circuit sends an automatic pulse every four seconds." + icon_state = "tick-m" + complexity = 4 + var/delay = 4 SECONDS var/next_fire = 0 var/is_running = FALSE - // Power consumption scales based on how fast it ticks. - // This, plus the fact it ticks more often will increase consumption non-linearly, - // and the circuit cooldown and will hopefully discourage stupidly fast ticking machines. - var/max_power_draw = 500 + inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN) + activators = list("outgoing pulse" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 4 + +/obj/item/integrated_circuit/time/ticker/Destroy() + if(is_running) + STOP_PROCESSING(SSfastprocess, src) + return ..() /obj/item/integrated_circuit/time/ticker/on_data_written() - var/delay_input = get_pin_data(IC_INPUT, 2) - if(delay_input && isnum(delay_input) ) - var/new_delay = clamp(delay_input, 1, 1 HOUR) - delay = new_delay - power_draw_per_use = CEILING((max_power_draw / delay) / delay, 1) - var/do_tick = get_pin_data(IC_INPUT, 1) if(do_tick && !is_running) is_running = TRUE @@ -64,32 +98,80 @@ /obj/item/integrated_circuit/time/ticker/proc/tick() - if(is_running && check_power()) + if(is_running) addtimer(CALLBACK(src, .proc/tick), delay) if(world.time > next_fire) next_fire = world.time + delay activate_pin(1) + +/obj/item/integrated_circuit/time/ticker/custom + name = "custom ticker" + desc = "This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second." + extended_desc ="This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second. \ + For example, setting the time pin to 4 will send a pulse every 0.4 seconds, or 15 for every 1.5 seconds." + icon_state = "tick-f" + complexity = 8 + delay = 2 SECONDS + inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN,"delay time" = IC_PINTYPE_NUMBER) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 8 + +/obj/item/integrated_circuit/time/ticker/custom/on_data_written() + var/delay_input = get_pin_data(IC_INPUT, 2) + if(delay_input && isnum(delay_input) ) + var/new_delay = clamp(delay_input ,1 ,1 HOURS) + delay = new_delay + ..() + +/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 = 6 + delay = 2 SECONDS + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 8 + +/obj/item/integrated_circuit/time/ticker/slow + name = "slow ticker" + desc = "This simple circuit sends an automatic pulse every six seconds." + icon_state = "tick-s" + complexity = 2 + delay = 6 SECONDS + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 2 + /obj/item/integrated_circuit/time/clock name = "integrated clock" - desc = "Tells you what the local time is, specific to your station or planet." + desc = "Tells you what the time is, in Nanotrasen Common Time. It has a toggle \ + for calculating time relative to the start of your shift" icon_state = "clock" - inputs = list() + inputs = list("shift toggle" = IC_PINTYPE_BOOLEAN) outputs = list( "time" = IC_PINTYPE_STRING, "hours" = IC_PINTYPE_NUMBER, "minutes" = IC_PINTYPE_NUMBER, - "seconds" = IC_PINTYPE_NUMBER + "seconds" = IC_PINTYPE_NUMBER, + "date" = 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 + power_draw_per_use = 2 /obj/item/integrated_circuit/time/clock/do_work() - set_pin_data(IC_OUTPUT, 1, time2text(station_time_in_ds, "hh:mm:ss") ) - set_pin_data(IC_OUTPUT, 2, text2num(time2text(station_time_in_ds, "hh") ) ) - set_pin_data(IC_OUTPUT, 3, text2num(time2text(station_time_in_ds, "mm") ) ) - set_pin_data(IC_OUTPUT, 4, text2num(time2text(station_time_in_ds, "ss") ) ) + if(get_pin_data(IC_INPUT, 1 == 0)) + set_pin_data(IC_OUTPUT, 1, time2text(station_time_in_ds, "hh:mm:ss") ) + set_pin_data(IC_OUTPUT, 2, text2num(time2text(station_time_in_ds, "hh") ) ) + set_pin_data(IC_OUTPUT, 3, text2num(time2text(station_time_in_ds, "mm") ) ) + set_pin_data(IC_OUTPUT, 4, text2num(time2text(station_time_in_ds, "ss") ) ) + set_pin_data(IC_OUTPUT, 5, stationdate2text()) + else + set_pin_data(IC_OUTPUT, 1, time2text(round_duration_in_ds, "hh:mm:ss") ) + set_pin_data(IC_OUTPUT, 2, text2num(time2text(round_duration_in_ds, "hh") ) ) + set_pin_data(IC_OUTPUT, 3, text2num(time2text(round_duration_in_ds, "mm") ) ) + set_pin_data(IC_OUTPUT, 4, text2num(time2text(round_duration_in_ds, "ss") ) ) + set_pin_data(IC_OUTPUT, 5, stationdate2text()) push_data() activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/trig.dm b/code/modules/integrated_electronics/subtypes/trig.dm index 236f46759d2..7ed9070408d 100644 --- a/code/modules/integrated_electronics/subtypes/trig.dm +++ b/code/modules/integrated_electronics/subtypes/trig.dm @@ -21,13 +21,12 @@ /obj/item/integrated_circuit/trig/sine name = "sin circuit" - desc = "Has nothing to do with evil, unless you consider trigonometry to be evil. Outputs the sine of A." + desc = "Only evil if you're allergic to math. Takes a degree and outputs the sine of said degree." 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)) @@ -41,13 +40,12 @@ /obj/item/integrated_circuit/trig/cosine name = "cos circuit" - desc = "Outputs the cosine of A." + desc = "Takes a degree and outputs the cosine of said degree." 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)) @@ -61,13 +59,12 @@ /obj/item/integrated_circuit/trig/tangent name = "tan circuit" - desc = "Outputs the tangent of A. Guaranteed to not go on a tangent about its existance." + desc = "Takes a degree and outputs the tangent of said degree." 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)) @@ -80,14 +77,13 @@ // Cosecant // /obj/item/integrated_circuit/trig/cosecant - name = "csc circuit" - desc = "Outputs the cosecant of A." + name = "cosecant (CSC) circuit" + desc = "Takes a degree and outputs the cosecant of said degree." 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)) @@ -97,18 +93,16 @@ 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." + name = "secant (SEC) circuit" + desc = "Takes a degree and outputs the secant of said degree." 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)) @@ -118,18 +112,16 @@ push_data() activate_pin(2) - // Cotangent // /obj/item/integrated_circuit/trig/cotangent - name = "cot circuit" - desc = "Outputs the cotangent of A." + name = "cotangent (COT) circuit" + desc = "Takes a degree and outputs the cotangent of said degree." 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)) diff --git a/code/modules/integrated_electronics/~defines/~defines.dm b/code/modules/integrated_electronics/~defines/~defines.dm index fba32d0b3eb..013758f0561 100644 --- a/code/modules/integrated_electronics/~defines/~defines.dm +++ b/code/modules/integrated_electronics/~defines/~defines.dm @@ -14,8 +14,12 @@ #undef IC_FORMAT_BOOLEAN #undef IC_FORMAT_REF #undef IC_FORMAT_LIST +#undef IC_FORMAT_INDEX +#undef IC_FORMAT_SELFREF #undef IC_FORMAT_PULSE +#undef IC_FORMAT_INPUT +#undef IC_FORMAT_OUTPUT #undef IC_PINTYPE_ANY #undef IC_PINTYPE_STRING diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 8a7ba198278..52a4e3fbd7e 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -34,6 +34,18 @@ my_atom.reagents = null return ..() +// Used in attack logs for reagents in pills and such +/datum/reagents/proc/log_list() + if(!length(reagent_list)) + return "no reagents" + + var/list/data = list() + for(var/r in reagent_list) //no reagents will be left behind + var/datum/reagent/R = r + data += "[R.type] [R.volume]u)" + //Using IDs because SOME chemicals (I'm looking at you, chlorhydrate-beer) have the same names as other chemicals. + return english_list(data) + /* Internal procs */ /datum/reagents/proc/get_free_space() // Returns free space. @@ -107,6 +119,11 @@ C.post_reaction(src) update_total() +/datum/reagents/proc/holder_full() + if(total_volume >= maximum_volume) + return TRUE + return FALSE + /* Holder-to-chemical */ /datum/reagents/proc/add_reagent(var/id, var/amount, var/data = null, var/safety = 0) diff --git a/code/modules/reagents/exposedprocs.dm b/code/modules/reagents/exposedprocs.dm new file mode 100644 index 00000000000..ffee71dc0e8 --- /dev/null +++ b/code/modules/reagents/exposedprocs.dm @@ -0,0 +1,93 @@ +/// Spray bottle style, intended for use with afterattack. See '/obj/effect/water/proc/set_up' for more details. +/obj/item/proc/spray_at(atom/A as mob|obj, proximity_flag, amount_per_transfer_from_this = 10, spray_size = 3, delay = 10) + playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6) + if (A.density && proximity_flag) + A.visible_message("[src.loc] sprays [A] with [src].") + reagents.splash(A, amount_per_transfer_from_this) + else + spawn(0) + var/obj/effect/water/chempuff/D = new/obj/effect/water/chempuff(get_turf(src)) + var/turf/my_target = get_turf(A) + D.create_reagents(amount_per_transfer_from_this) + if(!src) + return + reagents.trans_to_obj(D, amount_per_transfer_from_this) + D.set_color() + D.set_up(my_target, spray_size, delay) + return + +/// Chem thrower style spray. If spray_size = null a random value from 6-8 will be chosen. +/obj/item/proc/spray_at_wide(atom/A as mob|obj, var/amount_per_transfer_from_this = 10, var/spray_size = null, var/delay = 2) + var/direction = get_dir(src, A) + var/turf/T = get_turf(A) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + var/list/the_targets = list(T, T1, T2) + + for(var/a = 1 to 3) + spawn(0) + if(reagents.total_volume < 1) break + var/obj/effect/water/chempuff/D = new/obj/effect/water/chempuff(get_turf(src)) + var/turf/my_target = the_targets[a] + D.create_reagents(amount_per_transfer_from_this) + if(!src) + return + playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6) + reagents.trans_to_obj(D, amount_per_transfer_from_this) + D.set_color() + spray_size ? null : (spray_size = rand(6, 8)) + D.set_up(my_target, spray_size, delay) + return + +/// Extinguisher spray, intended for use with afterattack. See '/obj/effect/water/proc/set_up' for more details. +/obj/item/proc/extinguish_spray(atom/A as mob|obj|turf, var/mob/living/user, var/amount_per_transfer_from_this = 10, var/spray_size = 3, var/delay = 10, var/spray_particles = 3) + var/direction = get_dir(src, A) + if(user && user.buckled && isobj(user.buckled)) + spawn(0) + ex_propel_object(usr.buckled, user, turn(direction,180)) + + var/turf/T = get_turf(A) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + + var/list/the_targets = list(T,T1,T2) + + for(var/a = 1 to spray_particles) + spawn(0) + if(!src || !reagents.total_volume) return + + var/obj/effect/water/W = new /obj/effect/water(get_turf(src)) + var/turf/my_target + if(a <= the_targets.len) + my_target = the_targets[a] + else + my_target = pick(the_targets) + W.create_reagents(amount_per_transfer_from_this) + if(!src) + return + playsound(src.loc, 'sound/effects/extinguish.ogg', 75, 1, -3) + reagents.trans_to_obj(W, amount_per_transfer_from_this) + W.set_color() + W.set_up(my_target) + + if((istype(usr.loc, /turf/space)) || (usr.lastarea.has_gravity == 0)) + user.newtonian_move(get_dir(A, user)) + +// Propel an object backwards in the same form as a fire extinguisher. +/obj/item/proc/ex_propel_object(var/obj/O, mob/user, movementdirection) + if(O.anchored) return + + var/obj/structure/bed/chair/C + if(istype(O, /obj/structure/bed/chair)) + C = O + + var/list/move_speed = list(1, 1, 1, 2, 2, 3) + for(var/i in 1 to 6) + if(C) C.propelled = (6-i) + O.Move(get_step(user,movementdirection), movementdirection) + sleep(move_speed[i]) + + //additional movement + for(var/i in 1 to 3) + O.Move(get_step(user,movementdirection), movementdirection) + sleep(3) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index d38f2f0f3cc..07c66db7cbe 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -185,6 +185,7 @@ D.create_reagents(amount_per_transfer_from_this) if(!src) return + playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6) reagents.trans_to_obj(D, amount_per_transfer_from_this) D.set_color() D.set_up(my_target, rand(6, 8), 2) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 515e2a6fa49..f823e25976b 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -1,9 +1,6 @@ //////////////////////////////////////////////////////////////////////////////// /// Syringes. //////////////////////////////////////////////////////////////////////////////// -#define SYRINGE_DRAW 0 -#define SYRINGE_INJECT 1 -#define SYRINGE_BROKEN 2 /obj/item/reagent_containers/syringe name = "syringe" diff --git a/code/modules/tables/rack.dm b/code/modules/tables/rack.dm index c3a6455f038..6963b807245 100644 --- a/code/modules/tables/rack.dm +++ b/code/modules/tables/rack.dm @@ -27,3 +27,8 @@ /obj/structure/table/rack/holorack/dismantle(obj/item/tool/wrench/W, mob/user) to_chat(user, "You cannot dismantle \the [src].") return + +/obj/structure/rack/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + . = !density + if(istype(caller)) + . = . || (caller.pass_flags & PASSTABLE) diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm index 2229ca5e19e..63d1f78f2ea 100644 --- a/code/modules/tables/tables.dm +++ b/code/modules/tables/tables.dm @@ -211,6 +211,11 @@ var/list/table_icon_cache = list() else return ..() +/obj/structure/table/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + . = !density + if(istype(caller)) + . = . || (caller.pass_flags & PASSTABLE) + /obj/structure/table/proc/reinforce_table(obj/item/stack/material/S, mob/user) if(reinforced) to_chat(user, "\The [src] is already reinforced!") diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm index cd797476444..0d1971e082e 100644 --- a/code/modules/tgui/states/physical.dm +++ b/code/modules/tgui/states/physical.dm @@ -28,6 +28,9 @@ GLOBAL_DATUM_INIT(physical_state, /datum/ui_state/physical, new) /mob/living/silicon/ai/physical_can_use_topic(src_object) return UI_UPDATE // AIs are not physical. +/mob/observer/dead/physical_can_use_topic(src_object) + return UI_UPDATE + /** * tgui state: physical_obscured_state @@ -53,3 +56,6 @@ GLOBAL_DATUM_INIT(physical_obscured_state, /datum/ui_state/physical_obscured_sta /mob/living/silicon/ai/physical_obscured_can_use_topic(src_object) return UI_UPDATE // AIs are not physical. + +/mob/observer/dead/physical_can_use_topic(src_object) + return UI_UPDATE diff --git a/icons/obj/integrated_electronics/electronic_components.dmi b/icons/obj/integrated_electronics/electronic_components.dmi index b9b82061caf..275fc10534b 100644 Binary files a/icons/obj/integrated_electronics/electronic_components.dmi and b/icons/obj/integrated_electronics/electronic_components.dmi differ diff --git a/icons/obj/integrated_electronics/electronic_setups.dmi b/icons/obj/integrated_electronics/electronic_setups.dmi index 7730744e4df..6f7da1311a0 100644 Binary files a/icons/obj/integrated_electronics/electronic_setups.dmi and b/icons/obj/integrated_electronics/electronic_setups.dmi differ diff --git a/tgui/packages/tgui/interfaces/ICAssembly.js b/tgui/packages/tgui/interfaces/ICAssembly.js index bafdec15b37..f57e1bac81d 100644 --- a/tgui/packages/tgui/interfaces/ICAssembly.js +++ b/tgui/packages/tgui/interfaces/ICAssembly.js @@ -1,6 +1,6 @@ import { round } from 'common/math'; import { useBackend } from "../backend"; -import { Box, Button, LabeledList, ProgressBar, Section, AnimatedNumber } from "../components"; +import { Box, Button, LabeledList, ProgressBar, Section, AnimatedNumber, Flex } from "../components"; import { Window } from "../layouts"; import { formatPower } from "../format"; @@ -12,73 +12,78 @@ export const ICAssembly = (props, context) => { max_components, total_complexity, max_complexity, + opened, battery_charge, battery_max, net_power, - unremovable_circuits, - removable_circuits, + circuit_props, } = data; return ( -
- - - - {total_parts} / {max_components} - ({round((total_parts / max_components) * 100, 1)}%) - - - - - {total_complexity} / {max_complexity} - ({round((total_complexity / max_complexity) * 100, 1)}%) - - - - {battery_charge && ( + + {opened ? ( +
+ + - {battery_charge} / {battery_max} - ({round((battery_charge / battery_max) * 100, 1)}%) + {total_parts} / {max_components} + ({round((total_parts / max_components) * 100, 1)}%) - ) || No cell detected.} - - - {net_power === 0 && "0 W/s" || ( - "-" + formatPower(Math.abs(val)) + "/s"} /> - )} - - -
- {unremovable_circuits.length && ( - - ) || null} - {removable_circuits.length && ( - +
+ + + {total_complexity} / {max_complexity} + ({round((total_complexity / max_complexity) * 100, 1)}%) + + + + {battery_charge && ( + + {battery_charge} / {battery_max} + ({round((battery_charge / battery_max) * 100, 1)}%) + + ) || No cell detected.} + + + + + {net_power === 0 && "0 W/s" || ( + "-" + formatPower(Math.abs(val)) + "/s"} />)} + + + + +
+
+ ) + : The assembly is closed.} + {opened && ( + ) || null}
@@ -96,16 +101,39 @@ const ICAssemblyCircuits = (props, context) => { return (
- {circuits.map(circuit => ( - + {circuits.map((circuit, i) => ( + - - - - ))} + + + ) + )}
); }; + +export const ICTerminal = (props, context) => { + const { act, data } = useBackend(context); + + const { + UI_settings, + circuits, + } = props; + + return ( +
+ + {circuits.map(circuit => + (circuit.input && ( + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ICCircuit.js b/tgui/packages/tgui/interfaces/ICCircuit.js index 9c52fa94125..aa7b4f9c762 100644 --- a/tgui/packages/tgui/interfaces/ICCircuit.js +++ b/tgui/packages/tgui/interfaces/ICCircuit.js @@ -14,6 +14,7 @@ export const ICCircuit = (props, context) => { displayed_name, removable, complexity, + cooldown_per_use, power_draw_idle, power_draw_per_use, extended_desc, @@ -36,6 +37,9 @@ export const ICCircuit = (props, context) => { {complexity} + + {cooldown_per_use / 10} secs + {power_draw_idle && ( {formatPower(power_draw_idle)} diff --git a/tgui/packages/tgui/interfaces/ICPrinter.js b/tgui/packages/tgui/interfaces/ICPrinter.js index f159e947918..236371fefff 100644 --- a/tgui/packages/tgui/interfaces/ICPrinter.js +++ b/tgui/packages/tgui/interfaces/ICPrinter.js @@ -1,5 +1,5 @@ import { useBackend, useSharedState } from "../backend"; -import { Button, LabeledList, ProgressBar, Section, Tabs, Stack } from "../components"; +import { Button, Flex, LabeledList, ProgressBar, Section, Tabs, Stack } from "../components"; import { Window } from "../layouts"; import { sortBy, filter } from 'common/collections'; @@ -13,12 +13,12 @@ export const ICPrinter = (props, context) => { debug, upgraded, can_clone, - assembly_to_clone, + program, categories, } = data; return ( - +
@@ -37,6 +37,7 @@ export const ICPrinter = (props, context) => {
+
@@ -87,7 +88,7 @@ const ICPrinterCategories = (props, context) => { {selectedCategory && (
- {sortBy(item => item.name)(selectedCategory.items).map(item => ( + {(selectedCategory.items).map(item => ( { }> @@ -111,3 +112,40 @@ const ICPrinterCategories = (props, context) => {
); }; +const ICCloningSection = (props, context) => { + const { act, data } = useBackend(context); + const { + can_clone, + program, + } = data; + + if (!can_clone) { + return false; + } + return ( +
+ + +
+ ); +};