diff --git a/code/modules/integrated_electronics/_defines.dm b/code/modules/integrated_electronics/_defines.dm new file mode 100644 index 0000000000..a2f4aa190a --- /dev/null +++ b/code/modules/integrated_electronics/_defines.dm @@ -0,0 +1,5 @@ +#define IC_TOPIC_UNHANDLED 0 +#define IC_TOPIC_HANDLED 1 +#define IC_TOPIC_REFRESH 2 +#define IC_FLAG_ANCHORABLE 1 +#define IC_FLAG_CAN_FIRE 2 \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/atmospherics.dm b/code/modules/integrated_electronics/subtypes/atmospherics.dm new file mode 100644 index 0000000000..1715223fa2 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/atmospherics.dm @@ -0,0 +1,762 @@ +#define SOURCE_TO_TARGET 0 +#define TARGET_TO_SOURCE 1 +#define PUMP_EFFICIENCY 0.6 +#define TANK_FAILURE_PRESSURE (ONE_ATMOSPHERE*25) +#define PUMP_MAX_PRESSURE (ONE_ATMOSPHERE*24) +#define PUMP_MAX_VOLUME 100 + + +/obj/item/integrated_circuit/atmospherics + category_text = "Atmospherics" + cooldown_per_use = 2 SECONDS + complexity = 10 + size = 7 + outputs = list( + "self reference" = IC_PINTYPE_SELFREF, + "pressure" = IC_PINTYPE_NUMBER + ) + var/datum/gas_mixture/air_contents + var/volume = 2 //Pretty small, I know + +/obj/item/integrated_circuit/atmospherics/Initialize() + air_contents = new(volume) + ..() + +/obj/item/integrated_circuit/atmospherics/return_air() + return air_contents + +//Check if the gas container is adjacent and of the right type +/obj/item/integrated_circuit/atmospherics/proc/check_gassource(atom/gasholder) + if(!gasholder) + return FALSE + if(!gasholder.Adjacent(get_object())) + return FALSE + if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) + return FALSE + return TRUE + +//Needed in circuits where source and target types differ +/obj/item/integrated_circuit/atmospherics/proc/check_gastarget(atom/gasholder) + return check_gassource(gasholder) + + +// - gas pump - // **works** +/obj/item/integrated_circuit/atmospherics/pump + name = "gas pump" + desc = "Somehow moves gases between two tanks, canisters, and other gas containers." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "source" = IC_PINTYPE_REF, + "target" = IC_PINTYPE_REF, + "target pressure" = IC_PINTYPE_NUMBER + ) + activators = list( + "transfer" = IC_PINTYPE_PULSE_IN, + "on transfer" = IC_PINTYPE_PULSE_OUT + ) + var/direction = SOURCE_TO_TARGET + var/target_pressure = PUMP_MAX_PRESSURE + power_draw_per_use = 20 + +/obj/item/integrated_circuit/atmospherics/pump/Initialize() + air_contents = new(volume) + extended_desc += " Use negative pressure to move air from target to source. \ + Note that only part of the gas is moved on each transfer, \ + so multiple activations will be necessary to achieve target pressure. \ + The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." + . = ..() + +// This proc gets the direction of the gas flow depending on its value, by calling update target +/obj/item/integrated_circuit/atmospherics/pump/on_data_written() + var/amt = get_pin_data(IC_INPUT, 3) + update_target(amt) + +/obj/item/integrated_circuit/atmospherics/pump/proc/update_target(new_amount) + if(!isnum(new_amount)) + new_amount = 0 + // See in which direction the gas moves + if(new_amount < 0) + direction = TARGET_TO_SOURCE + else + direction = SOURCE_TO_TARGET + target_pressure = min(round(PUMP_MAX_PRESSURE),abs(new_amount)) + +/obj/item/integrated_circuit/atmospherics/pump/do_work() + var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) + var/obj/target = get_pin_data_as_type(IC_INPUT, 2, /obj) + perform_magic(source, target) + activate_pin(2) + +/obj/item/integrated_circuit/atmospherics/pump/proc/perform_magic(atom/source, atom/target) + //Check if both atoms are of the right type: atmos circuits/gas tanks/canisters. If one is the same, use the circuit var + if(!check_gassource(source)) + source = src + + if(!check_gastarget(target)) + target = src + + // If both are the same, this whole proc would do nothing and just waste performance + if(source == target) + return + + var/datum/gas_mixture/source_air = source.return_air() + var/datum/gas_mixture/target_air = target.return_air() + + if(!source_air || !target_air) + return + + // Swapping both source and target + if(direction == TARGET_TO_SOURCE) + var/temp = source_air + source_air = target_air + target_air = temp + + // If what you are pumping is empty, use the circuit's storage + if(source_air.total_moles() <= 0) + source_air = air_contents + + // Move gas from one place to another + move_gas(source_air, target_air) + air_update_turf() + +/obj/item/integrated_circuit/atmospherics/pump/proc/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air) + + // No moles = nothing to pump + if(source_air.total_moles() <= 0 || target_air.return_pressure() >= PUMP_MAX_PRESSURE) + return + + // Negative Kelvin temperatures should never happen and if they do, normalize them + if(source_air.temperature < TCMB) + source_air.temperature = TCMB + + var/pressure_delta = target_pressure - target_air.return_pressure() + if(pressure_delta > 0.1) + var/transfer_moles = (pressure_delta*target_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY + var/datum/gas_mixture/removed = source_air.remove(transfer_moles) + target_air.merge(removed) + + +// - volume pump - // **Works** +/obj/item/integrated_circuit/atmospherics/pump/volume + name = "volume pump" + desc = "Moves gases between two tanks, canisters, and other gas containers by using their volume, up to 200 L/s." + extended_desc = " Use negative volume to move air from target to source. Note that only part of the gas is moved on each transfer. Its maximum pumping volume is capped at 1000kPa." + + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "source" = IC_PINTYPE_REF, + "target" = IC_PINTYPE_REF, + "transfer volume" = IC_PINTYPE_NUMBER + ) + activators = list( + "transfer" = IC_PINTYPE_PULSE_IN, + "on transfer" = IC_PINTYPE_PULSE_OUT + ) + direction = SOURCE_TO_TARGET + var/transfer_rate = PUMP_MAX_VOLUME + power_draw_per_use = 20 + +/obj/item/integrated_circuit/atmospherics/pump/volume/update_target(new_amount) + if(!isnum(new_amount)) + new_amount = 0 + // See in which direction the gas moves + if(new_amount < 0) + direction = TARGET_TO_SOURCE + else + direction = SOURCE_TO_TARGET + target_pressure = min(PUMP_MAX_VOLUME,abs(new_amount)) + +/obj/item/integrated_circuit/atmospherics/pump/volume/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air) + // No moles = nothing to pump + if(source_air.total_moles() <= 0) + return + + // Negative Kelvin temperatures should never happen and if they do, normalize them + if(source_air.temperature < TCMB) + source_air.temperature = TCMB + + if((source_air.return_pressure() < 0.01) || (target_air.return_pressure() >= PUMP_MAX_PRESSURE)) + return + + //The second part of the min caps the pressure built by the volume pumps to the max pump pressure + var/transfer_ratio = min(transfer_rate,target_air.volume*PUMP_MAX_PRESSURE/source_air.return_pressure())/source_air.volume + + var/datum/gas_mixture/removed = source_air.remove_ratio(transfer_ratio * PUMP_EFFICIENCY) + + target_air.merge(removed) + + +// - gas vent - // **works** +/obj/item/integrated_circuit/atmospherics/pump/vent + name = "gas vent" + extended_desc = "Use negative volume to move air from target to environment. Note that only part of the gas is moved on each transfer. Unlike the gas pump, this one keeps pumping even further to pressures of 9000 pKa and it is not advised to use it on tank circuits." + desc = "Moves gases between the environment and adjacent gas containers." + inputs = list( + "container" = IC_PINTYPE_REF, + "target pressure" = IC_PINTYPE_NUMBER + ) + +/obj/item/integrated_circuit/atmospherics/pump/vent/on_data_written() + var/amt = get_pin_data(IC_INPUT, 2) + update_target(amt) + +/obj/item/integrated_circuit/atmospherics/pump/vent/do_work() + var/turf/source = get_turf(src) + var/obj/target = get_pin_data_as_type(IC_INPUT, 1, /obj) + perform_magic(source, target) + activate_pin(2) + +/obj/item/integrated_circuit/atmospherics/pump/vent/check_gastarget(atom/gasholder) + if(!gasholder) + return FALSE + if(!gasholder.Adjacent(get_object())) + return FALSE + if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) + return FALSE + return TRUE + + +/obj/item/integrated_circuit/atmospherics/pump/vent/check_gassource(atom/target) + if(!target) + return FALSE + if(!istype(target, /turf)) + return FALSE + return TRUE + + +// - integrated connector - // Can connect and disconnect properly +/obj/item/integrated_circuit/atmospherics/connector + name = "integrated connector" + desc = "Creates an airtight seal with standard connectors found on the floor, \ + allowing the assembly to exchange gases with a pipe network." + extended_desc = "This circuit will automatically attempt to locate and connect to ports on the floor beneath it when activated. \ + You must set a target before connecting." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "target" = IC_PINTYPE_REF + ) + activators = list( + "toggle connection" = IC_PINTYPE_PULSE_IN, + "on connected" = IC_PINTYPE_PULSE_OUT, + "on connection failed" = IC_PINTYPE_PULSE_OUT, + "on disconnected" = IC_PINTYPE_PULSE_OUT + ) + + var/obj/machinery/atmospherics/components/unary/portables_connector/connector + +/obj/item/integrated_circuit/atmospherics/connector/Initialize() + air_contents = new(volume) + START_PROCESSING(SSobj, src) + . = ..() + +//Sucks up the gas from the connector +/obj/item/integrated_circuit/atmospherics/connector/process() + set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) + +/obj/item/integrated_circuit/atmospherics/connector/check_gassource(atom/gasholder) + if(!gasholder) + return FALSE + if(!istype(gasholder,/obj/machinery/atmospherics/components/unary/portables_connector)) + return FALSE + return TRUE + +//If the assembly containing this is moved from the tile the connector pipe is in, the connection breaks +/obj/item/integrated_circuit/atmospherics/connector/ext_moved() + if(connector) + if(get_dist(get_object(), connector) > 0) + // The assembly is set as connected device and the connector handles the rest + connector.connected_device = null + connector = null + activate_pin(4) + +/obj/item/integrated_circuit/atmospherics/connector/do_work() + // If there is a connection, disconnect + if(connector) + connector.connected_device = null + connector = null + activate_pin(4) + return + + var/obj/machinery/atmospherics/components/unary/portables_connector/PC = locate() in get_turf(src) + // If no connector can't connect + if(!PC) + activate_pin(3) + return + connector = PC + connector.connected_device = src + activate_pin(2) + +// Required for making the connector port script work +obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir() + return air_contents + + +// - gas filter - // **works** +/obj/item/integrated_circuit/atmospherics/pump/filter + name = "gas filter" + desc = "Filters one gas out of a mixture." + complexity = 20 + size = 8 + spawn_flags = IC_SPAWN_RESEARCH + inputs = list( + "source" = IC_PINTYPE_REF, + "filtered output" = IC_PINTYPE_REF, + "contaminants output" = IC_PINTYPE_REF, + "wanted gases" = IC_PINTYPE_LIST, + "target pressure" = IC_PINTYPE_NUMBER + ) + power_draw_per_use = 30 + +/obj/item/integrated_circuit/atmospherics/pump/filter/on_data_written() + var/amt = get_pin_data(IC_INPUT, 5) + target_pressure = CLAMP(amt, 0, PUMP_MAX_PRESSURE) + +/obj/item/integrated_circuit/atmospherics/pump/filter/do_work() + activate_pin(2) + var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) + var/obj/filtered = get_pin_data_as_type(IC_INPUT, 2, /obj) + var/obj/contaminants = get_pin_data_as_type(IC_INPUT, 3, /obj) + + var/wanted = get_pin_data(IC_INPUT, 4) + + // If there is no filtered output, this whole thing makes no sense + if(!check_gassource(filtered)) + return + + var/datum/gas_mixture/filtered_air = filtered.return_air() + if(!filtered_air) + return + + // If no source is set, the source is possibly this circuit's content + if(!check_gassource(source)) + source = src + var/datum/gas_mixture/source_air = source.return_air() + + //No source air: source is this circuit + if(!source_air) + source_air = air_contents + + // If no filtering tank is set, filter through itself + if(!check_gassource(contaminants)) + contaminants = src + var/datum/gas_mixture/contaminated_air = contaminants.return_air() + + //If there is no gas mixture datum for unfiltered, pump the contaminants back into the circuit + if(!contaminated_air) + contaminated_air = air_contents + + if(contaminated_air.return_pressure() >= PUMP_MAX_PRESSURE || filtered_air.return_pressure() >= PUMP_MAX_PRESSURE) + return + + var/pressure_delta = target_pressure - contaminated_air.return_pressure() + var/transfer_moles + + //Negative Kelvins are an anomaly and should be normalized if encountered + if(source_air.temperature < TCMB) + source_air.temperature = TCMB + + transfer_moles = (pressure_delta*contaminated_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY + + //If there is nothing to transfer, just return + if(transfer_moles <= 0) + return + + //This is the var that holds the currently filtered part of the gas + var/datum/gas_mixture/removed = source_air.remove(transfer_moles) + if(!removed) + return + + //This is the gas that will be moved from source to filtered + var/datum/gas_mixture/filtered_out = new + + for(var/filtered_gas in removed.gases) + //Get the name of the gas and see if it is in the list + if(removed.gases[filtered_gas][GAS_META][META_GAS_NAME] in wanted) + //The gas that is put in all the filtered out gases + filtered_out.temperature = removed.temperature + filtered_out.add_gas(filtered_gas) + filtered_out.gases[filtered_gas][MOLES] = removed.gases[filtered_gas][MOLES] + + //The filtered out gas is entirely removed from the currently filtered gases + removed.gases[filtered_gas][MOLES] = 0 + removed.garbage_collect() + + //Check if the pressure is high enough to put stuff in filtered, or else just put it back in the source + var/datum/gas_mixture/target = (filtered_air.return_pressure() < target_pressure ? filtered_air : source_air) + target.merge(filtered_out) + contaminated_air.merge(removed) + + +/obj/item/integrated_circuit/atmospherics/pump/filter/Initialize() + air_contents = new(volume) + . = ..() + extended_desc = "Remember to properly spell and capitalize the filtered gas name. \ + Note that only part of the gas is moved on each transfer, \ + so multiple activations will be necessary to achieve target pressure. \ + The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." + + +// - gas mixer - // **works** +/obj/item/integrated_circuit/atmospherics/pump/mixer + name = "gas mixer" + desc = "Mixes 2 different types of gases." + complexity = 20 + size = 8 + spawn_flags = IC_SPAWN_RESEARCH + inputs = list( + "first source" = IC_PINTYPE_REF, + "second source" = IC_PINTYPE_REF, + "output" = IC_PINTYPE_REF, + "first source percentage" = IC_PINTYPE_NUMBER, + "target pressure" = IC_PINTYPE_NUMBER + ) + power_draw_per_use = 30 + +/obj/item/integrated_circuit/atmospherics/pump/mixer/do_work() + activate_pin(2) + var/obj/source_1 = get_pin_data(IC_INPUT, 1) + var/obj/source_2 = get_pin_data(IC_INPUT, 2) + var/obj/gas_output = get_pin_data(IC_INPUT, 3) + if(!check_gassource(source_1)) + source_1 = src + + if(!check_gassource(source_2)) + source_2 = src + + if(!check_gassource(gas_output)) + gas_output = src + + if(source_1 == gas_output || source_2 == gas_output) + return + + var/datum/gas_mixture/source_1_gases = source_1.return_air() + var/datum/gas_mixture/source_2_gases = source_2.return_air() + var/datum/gas_mixture/output_gases = gas_output.return_air() + + if(!source_1_gases || !source_2_gases || !output_gases) + return + + if(output_gases.return_pressure() >= PUMP_MAX_PRESSURE) + return + + if(source_1_gases.return_pressure() <= 0 || source_2_gases.return_pressure() <= 0) + return + + //This calculates how much should be sent + var/gas_percentage = round(max(min(get_pin_data(IC_INPUT, 4),100),0) / 100) + + //Basically: number of moles = percentage of pressure filled up * efficiency coefficient * (pressure from both gases * volume of output) / (R * Temperature) + var/transfer_moles = (get_pin_data(IC_INPUT, 5) / max(1,output_gases.return_pressure())) * PUMP_EFFICIENCY * (source_1_gases.return_pressure() * gas_percentage + source_2_gases.return_pressure() * (1 - gas_percentage)) * output_gases.volume/ (R_IDEAL_GAS_EQUATION * max(output_gases.temperature,TCMB)) + + + if(transfer_moles <= 0) + return + + var/datum/gas_mixture/mix = source_1_gases.remove(transfer_moles * gas_percentage) + output_gases.merge(mix) + mix = source_2_gases.remove(transfer_moles * (1-gas_percentage)) + output_gases.merge(mix) + + +// - integrated tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank + name = "integrated tank" + desc = "A small tank for the storage of gases." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + size = 4 + activators = list( + "push ref" = IC_PINTYPE_PULSE_IN + ) + volume = 3 //emergency tank sized + var/broken = FALSE + +/obj/item/integrated_circuit/atmospherics/tank/Initialize() + air_contents = new(volume) + START_PROCESSING(SSobj, src) + extended_desc = "Take care not to pressurize it above [round(TANK_FAILURE_PRESSURE)] kPa, or else it will break." + . = ..() + +/obj/item/integrated_circuit/atmospherics/tank/Destroy() + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/atmospherics/tank/do_work() + set_pin_data(IC_OUTPUT, 1, WEAKREF(src)) + push_data() + +/obj/item/integrated_circuit/atmospherics/tank/process() + var/tank_pressure = air_contents.return_pressure() + set_pin_data(IC_OUTPUT, 2, tank_pressure) + push_data() + + //Check if tank broken + if(!broken && tank_pressure > TANK_FAILURE_PRESSURE) + broken = TRUE + to_chat(view(2),"The [name] ruptures, releasing its gases!") + if(broken) + release() + +/obj/item/integrated_circuit/atmospherics/tank/proc/release() + if(air_contents.total_moles() > 0) + playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) + var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) + var/turf/current_turf = get_turf(src) + var/datum/gas_mixture/exterior_gas + if(!current_turf) + return + + exterior_gas = current_turf.return_air() + exterior_gas.merge(expelled_gas) + + +// - large integrated tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank/large + name = "large integrated tank" + desc = "A less small tank for the storage of gases." + volume = 9 + size = 12 + spawn_flags = IC_SPAWN_RESEARCH + + +// - freezer tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank/freezer + name = "freezer tank" + desc = "Cools the gas it contains to a preset temperature." + volume = 6 + size = 8 + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + inputs_default = list("1" = 300) + spawn_flags = IC_SPAWN_RESEARCH + var/temperature = 293.15 + var/heater_coefficient = 0.1 + +/obj/item/integrated_circuit/atmospherics/tank/freezer/on_data_written() + temperature = max(73.15,min(293.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/tank/freezer/process() + var/tank_pressure = air_contents.return_pressure() + set_pin_data(IC_OUTPUT, 2, tank_pressure) + push_data() + + //Cool the tank if the power is on and the temp is above + if(!power_draw_idle || air_contents.temperature < temperature) + return + + air_contents.temperature = max(73.15,air_contents.temperature - (air_contents.temperature - temperature) * heater_coefficient) + + +// - heater tank - // **works** +/obj/item/integrated_circuit/atmospherics/tank/freezer/heater + name = "heater tank" + desc = "Heats the gas it contains to a preset temperature." + volume = 6 + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + spawn_flags = IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/on_data_written() + temperature = max(293.15,min(573.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/process() + var/tank_pressure = air_contents.return_pressure() + set_pin_data(IC_OUTPUT, 2, tank_pressure) + push_data() + + //Heat the tank if the power is on or its temperature is below what is set + if(!power_draw_idle || air_contents.temperature > temperature) + return + + air_contents.temperature = min(573.15,air_contents.temperature + (temperature - air_contents.temperature) * heater_coefficient) + + +// - atmospheric cooler - // **works** +/obj/item/integrated_circuit/atmospherics/cooler + name = "atmospheric cooler circuit" + desc = "Cools the air around it." + volume = 6 + size = 13 + spawn_flags = IC_SPAWN_RESEARCH + inputs = list( + "target temperature" = IC_PINTYPE_NUMBER, + "on" = IC_PINTYPE_BOOLEAN + ) + var/temperature = 293.15 + var/heater_coefficient = 0.1 + +/obj/item/integrated_circuit/atmospherics/cooler/Initialize() + air_contents = new(volume) + START_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/atmospherics/cooler/Destroy() + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/atmospherics/cooler/on_data_written() + temperature = max(243.15,min(293.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/cooler/process() + set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) + push_data() + + + //Get the turf you're on and its gas mixture + var/turf/current_turf = get_turf(src) + if(!current_turf) + return + + var/datum/gas_mixture/turf_air = current_turf.return_air() + if(!power_draw_idle || turf_air.temperature < temperature) + return + + //Cool the gas + turf_air.temperature = max(243.15,turf_air.temperature - (turf_air.temperature - temperature) * heater_coefficient) + + +// - atmospheric heater - // **works** +/obj/item/integrated_circuit/atmospherics/cooler/heater + name = "atmospheric heater circuit" + desc = "Heats the air around it." + +/obj/item/integrated_circuit/atmospherics/cooler/heater/on_data_written() + temperature = max(293.15,min(323.15,get_pin_data(IC_INPUT, 1))) + if(get_pin_data(IC_INPUT, 2)) + power_draw_idle = 30 + else + power_draw_idle = 0 + +/obj/item/integrated_circuit/atmospherics/cooler/heater/process() + set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) + push_data() + + //Get the turf and its air mixture + var/turf/current_turf = get_turf(src) + if(!current_turf) + return + + var/datum/gas_mixture/turf_air = current_turf.return_air() + if(!power_draw_idle || turf_air.temperature > temperature) + return + + //Heat the gas + turf_air.temperature = min(323.15,turf_air.temperature + (temperature - turf_air.temperature) * heater_coefficient) + + +// - tank slot - // **works** +/obj/item/integrated_circuit/input/tank_slot + category_text = "Atmospherics" + cooldown_per_use = 1 + name = "tank slot" + desc = "Lets you add a tank to your assembly and remove it even when the assembly is closed." + extended_desc = "It can help you extract gases easier." + complexity = 25 + size = 30 + inputs = list() + outputs = list( + "pressure used" = IC_PINTYPE_NUMBER, + "current tank" = IC_PINTYPE_REF + ) + activators = list( + "push ref" = IC_PINTYPE_PULSE_IN, + "on insert" = IC_PINTYPE_PULSE_OUT, + "on remove" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + + can_be_asked_input = TRUE + demands_object_input = TRUE + can_input_object_when_closed = TRUE + + var/obj/item/tank/internals/current_tank + +/obj/item/integrated_circuit/input/tank_slot/Initialize() + START_PROCESSING(SSobj, src) + . = ..() + +/obj/item/integrated_circuit/input/tank_slot/process() + push_pressure() + +/obj/item/integrated_circuit/input/tank_slot/attackby(var/obj/item/tank/internals/I, var/mob/living/user) + //Check if it truly is a tank + if(!istype(I,/obj/item/tank/internals)) + to_chat(user,"The [I.name] doesn't seem to fit in here.") + return + + //Check if there is no other tank already inside + if(current_tank) + to_chat(user,"There is already a gas tank inside.") + return + + //The current tank is the one we just attached, its location is inside the circuit + current_tank = I + user.transferItemToLoc(I,src) + to_chat(user,"You put the [I.name] inside the tank slot.") + + //Set the pin to a weak reference of the current tank + push_pressure() + set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) + push_data() + do_work(1) + + +/obj/item/integrated_circuit/input/tank_slot/ask_for_input(mob/user) + attack_self(user) + +/obj/item/integrated_circuit/input/tank_slot/attack_self(mob/user) + //Check if no tank attached + if(!current_tank) + to_chat(user, "There is currently no tank attached.") + return + + //Remove tank and put in user's hands/location + to_chat(user, "You take [current_tank] out of the tank slot.") + user.put_in_hands(current_tank) + current_tank = null + + //Remove tank reference + push_pressure() + set_pin_data(IC_OUTPUT, 2, null) + push_data() + do_work(2) + +/obj/item/integrated_circuit/input/tank_slot/do_work() + set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) + push_data() + +/obj/item/integrated_circuit/input/tank_slot/proc/push_pressure() + if(!current_tank) + set_pin_data(IC_OUTPUT, 1, 0) + return + + var/datum/gas_mixture/tank_air = current_tank.return_air() + if(!tank_air) + set_pin_data(IC_OUTPUT, 1, 0) + return + + set_pin_data(IC_OUTPUT, 1, tank_air.return_pressure()) + push_data() + + +#undef SOURCE_TO_TARGET +#undef TARGET_TO_SOURCE +#undef PUMP_EFFICIENCY +#undef TANK_FAILURE_PRESSURE +#undef PUMP_MAX_PRESSURE +#undef PUMP_MAX_VOLUME diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index 20d405b71d..6e8b0cb777 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -1218,4 +1218,95 @@ activate_pin(2) else return FALSE - return TRUE \ No newline at end of file + return TRUE + + +//Hippie Ported Code-------------------------------------------------------------------------------------------------------- + + + //Adding some color to cards aswell, because why not +/obj/item/card/data/attackby(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 ..() + + + +// -Inputlist- // +/obj/item/integrated_circuit/input/selection + name = "selection circuit" + desc = "This circuit lets you choose between different strings from a selection." + extended_desc = "This circuit lets you choose between up to 4 different values from selection of up to 8 strings that you can set. Null values are ignored and the chosen value is put out in selected." + icon_state = "addition" + can_be_asked_input = 1 + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + 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 + ) + activators = list( + "on selected" = IC_PINTYPE_PULSE_OUT + ) + outputs = list( + "selected" = IC_PINTYPE_STRING + ) + +/obj/item/integrated_circuit/input/selection/ask_for_input(mob/user) + var/list/selection = list() + for(var/k in 1 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(istext(I)) + selection.Add(I) + var/selected = input(user,"Choose input.","Selection") in selection + if(!selected) + return + set_pin_data(IC_OUTPUT, 1, selected) + push_data() + activate_pin(1) + + +// -storage examiner- // **works** +/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() + 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) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm index 1c76c7648c..f63d58b707 100644 --- a/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -677,3 +677,140 @@ GET_COMPONENT(materials, /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 + inputs = list("target object" = IC_PINTYPE_REF, "target container" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER) + 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 = 20 + var/max_items = 10 + +/obj/item/integrated_circuit/manipulation/inserter/do_work() + //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) + return + + var/distance = get_dist(get_turf(src),get_turf(target_obj)) + if(distance > 1 || distance < 0) + return + + var/obj/item/storage/container = get_pin_data_as_type(IC_INPUT, 2, /obj/item) + var/mode = get_pin_data(IC_INPUT, 3) + switch(mode) + if(1) //Not working + if(!container || !istype(container,/obj/item/storage) || !Adjacent(container)) + return + + GET_COMPONENT_FROM(STR, /datum/component/storage, container) + if(!STR) + return + + STR.attackby(src, target_obj) + + else + GET_COMPONENT_FROM(STR, /datum/component/storage, target_obj.loc) + if(!STR) + return + + if(!container || !istype(container,/obj/item/storage) || !Adjacent(container)) + STR.remove_from_storage(target_obj,drop_location()) + else + STR.remove_from_storage(target_obj,container) + +// 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(var/n) + if(!assembly) + return + switch(n) + if(1) + var/new_name = get_pin_data(IC_INPUT, 1) + 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(var/n) + if(!assembly) + return + + switch(n) + 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(var/n) + if(!assembly) + return + + switch(n) + 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) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index 0ad2714e28..86efc4c74a 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -385,4 +385,30 @@ else assembly.prefered_hud_icon = "hudstat" //update the diagnostic hud - assembly.diag_hud_set_circuitstat() \ No newline at end of file + assembly.diag_hud_set_circuitstat() + + +//Hippie Ported Code-------------------------------------------------------------------------------------------------------- + + + +/obj/item/radio/headset/integrated + +/obj/item/integrated_circuit/output/screen/large + name = "medium screen" + +/obj/item/integrated_circuit/output/screen/extralarge // the subtype is called "extralarge" because tg brought back medium screens and they named the subtype /screen/large + name = "large screen" + desc = "Takes any data type as an input and displays it to the user upon examining, and to all nearby beings when pulsed." + icon_state = "screen_large" + power_draw_per_use = 40 + cooldown_per_use = 10 + +/obj/item/integrated_circuit/output/screen/extralarge/do_work() + ..() + var/obj/O = assembly ? get_turf(assembly) : loc + O.visible_message("[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]") + if(assembly) + assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT) + else + investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT) \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm index b8f326f389..af71c8605f 100644 --- a/code/modules/integrated_electronics/subtypes/reagents.dm +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -538,3 +538,318 @@ reagents.handle_reactions() set_pin_data(IC_OUTPUT, 3, reagents.chem_temp) push_data() + + +//Hippie Ported Code-------------------------------------------------------------------------------------------------------- + + +/obj/item/integrated_circuit/reagent/smoke + name = "smoke generator" + desc = "Unlike most electronics, creating smoke is completely intentional." + icon_state = "smoke" + extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \ + into the smoke clouds when activated. The reagents are consumed when the smoke is made." + ext_cooldown = 1 + volume = 100 + complexity = 20 + cooldown_per_use = 1 SECONDS + inputs = list() + 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, + "push ref" = IC_PINTYPE_PULSE_IN + ) + spawn_flags = IC_SPAWN_RESEARCH + power_draw_per_use = 20 + var/smoke_radius = 5 + var/notified = FALSE + +/obj/item/integrated_circuit/reagent/smoke/on_reagent_change(changetype) + //reset warning only if we have reagents now + if(changetype == ADD_REAGENT) + notified = FALSE + push_vol() +/obj/item/integrated_circuit/reagent/smoke/do_work(ord) + switch(ord) + if(1) + if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS)) + return + var/location = get_turf(src) + var/datum/effect_system/smoke_spread/chem/S = new + S.attach(location) + playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) + if(S) + S.set_up(reagents, smoke_radius, location, notified) + if(!notified) + notified = TRUE + S.start() + reagents.clear_reagents() + activate_pin(2) + if(3) + set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) + push_data() + +// - Integrated extinguisher - // +/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 + + complexity = 20 + cooldown_per_use = 6 SECONDS + inputs = list( + "target X rel" = IC_PINTYPE_NUMBER, + "target Y rel" = IC_PINTYPE_NUMBER + ) + 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 + ) + 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 + set_pin_data(IC_OUTPUT, 1, reagents.total_volume) + if(!reagents || reagents.total_volume < 5 || busy) + push_data() + activate_pin(3) + return + + playsound(loc, 'sound/effects/extinguish.ogg', 75, 1, -3) + //Get the tile on which the water particle spawns + var/turf/Spawnpoint = get_turf(src) + if(!Spawnpoint) + push_data() + activate_pin(3) + return + + //Get direction and target turf for each water particle + var/turf/T = locate(Spawnpoint.x + get_pin_data(IC_INPUT, 1),Spawnpoint.y + get_pin_data(IC_INPUT, 2),Spawnpoint.z) + if(!T) + push_data() + activate_pin(3) + return + var/direction = get_dir(Spawnpoint, T) + 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) + busy = TRUE + + // Create list with particles and their targets + var/list/water_particles=list() + for(var/a=0, a<5, a++) + var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water(get_turf(src)) + water_particles[W] = pick(the_targets) + var/datum/reagents/R = new/datum/reagents(5) + W.reagents = R + R.my_atom = W + reagents.trans_to(W,1) + + //Make em move dat ass, hun + addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, water_particles), 2) + +//This whole proc is a loop +/obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles(var/list/particles, var/repetitions=0) + //Check if there's anything in here first + if(!particles || particles.len == 0) + return + // Second loop: Get all the water particles and make them move to their target + for(var/obj/effect/particle_effect/water/W in particles) + var/turf/my_target = particles[W] + if(!W) + continue + step_towards(W,my_target) + if(!W.reagents) + continue + W.reagents.reaction(get_turf(W)) + for(var/A in get_turf(W)) + W.reagents.reaction(A) + if(W.loc == my_target) + break + if(repetitions < 4) + repetitions++ //Can't have math operations in addtimer(CALLBACK()) + addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, particles, repetitions), 2) + else + push_data() + activate_pin(2) + busy = FALSE + + +// - Drain circuit - // +/obj/item/integrated_circuit/reagent/drain + name = "chemical drain circuit" + desc = "This circuit either eliminates reagents by creating a puddle or can suck up chemicals on tiles." + icon_state = "injector" + extended_desc = "Set mode to FALSE to eliminate reagents and TRUE to drain." + + volume = 20 + + complexity = 10 + inputs = list( + "mode" = IC_PINTYPE_BOOLEAN + ) + outputs = list( + "volume" = IC_PINTYPE_NUMBER, + "self reference" = IC_PINTYPE_SELFREF + ) + activators = list( + "drain" = IC_PINTYPE_PULSE_IN, + "on drained" = IC_PINTYPE_PULSE_OUT, + "on fail" = IC_PINTYPE_PULSE_OUT + ) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 15 + + +/obj/item/integrated_circuit/reagent/drain/Initialize() + .=..() + set_pin_data(IC_OUTPUT,2, src) + + +/obj/item/integrated_circuit/reagent/drain/do_work() + if(get_pin_data(IC_OUTPUT, 1, reagents.total_volume)) + if(!reagents || !reagents.total_volume) + push_data() + activate_pin(3) + return + // Put the reagents on the floortile the assembly is on + reagents.reaction(get_turf(src)) + reagents.clear_reagents() + push_data() + activate_pin(2) + return + + else + if(reagents) + if(reagents.total_volume >= volume) + push_data() + activate_pin(3) + return + // Favorably, drain it from a chemicals pile, else, try something different + var/obj/effect/decal/cleanable/drainedchems = locate(/obj/effect/decal/cleanable) in get_turf(src) + if(!drainedchems || !drainedchems.reagents || drainedchems.reagents.total_volume == 0) + push_data() + activate_pin(3) + return + drainedchems.reagents.trans_to(src, 30, 0.5) + if(drainedchems.reagents.total_volume == 0) + qdel(drainedchems) + push_data() + activate_pin(2) + + +/obj/item/integrated_circuit/reagent/drain/on_reagent_change(changetype) + push_vol() + + +// - Beaker Connector - // +/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 + + 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 + demands_object_input = TRUE + can_input_object_when_closed = TRUE + + var/obj/item/reagent_containers/glass/beaker/current_beaker + + +/obj/item/integrated_circuit/input/beaker_connector/attackby(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() \ No newline at end of file diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index bd93ae8314..b9bd4627c4 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -114,3 +114,262 @@ set_pin_data(IC_OUTPUT, 2, Yn) push_data() activate_pin(2) + + +//Hippie Ported Code-------------------------------------------------------------------------------------------------------- + + + +// - 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 = 29 + 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(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() + return check_bot_self + +/obj/item/integrated_circuit/smart/advanced_pathfinder/proc/hippie_xor_decrypt() + var/Ps = get_pin_data(IC_INPUT, 4) + if(!Ps) + return + var/list/Pl = json_decode(XorEncrypt(hextostr(Ps, TRUE), SScircuit.cipherkey)) + if(Pl&&islist(Pl)) + idc.access = Pl + +// - 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 = 29 + 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(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() + return check_bot_self \ No newline at end of file diff --git a/tgstation.dme b/tgstation.dme index c43b1a7019..1cdc8cb21a 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -1711,6 +1711,7 @@ #include "code\modules\hydroponics\grown\tobacco.dm" #include "code\modules\hydroponics\grown\tomato.dm" #include "code\modules\hydroponics\grown\towercap.dm" +#include "code\modules\integrated_electronics\_defines.dm" #include "code\modules\integrated_electronics\core\analyzer.dm" #include "code\modules\integrated_electronics\core\assemblies.dm" #include "code\modules\integrated_electronics\core\debugger.dm" @@ -1735,6 +1736,7 @@ #include "code\modules\integrated_electronics\passive\power.dm" #include "code\modules\integrated_electronics\subtypes\access.dm" #include "code\modules\integrated_electronics\subtypes\arithmetic.dm" +#include "code\modules\integrated_electronics\subtypes\atmospherics.dm" #include "code\modules\integrated_electronics\subtypes\converters.dm" #include "code\modules\integrated_electronics\subtypes\data_transfer.dm" #include "code\modules\integrated_electronics\subtypes\input.dm"