Integrated Circuitry Miniupdate (#21971)

- Updates the **medical analysers** to get health values significant to
Brainmed instead of Pointmed. About 6 years late?
- Adds a **radiation scanner**.
- Adds a **light scanner**.
- Adds a **custom ticker** that can be calibrated between 2 (fast ticker
equivalent) and 600 seconds.
- Adds a **face scanner** that can get basic information about
humanoids, including: name (same as an examiner), general flavourtext,
face flavourtext, species, height, estimated age, and eye colour. Has
applications for species-specific circuitry, identity confirmation, or
flavour usage.
- Adds a **flasher** that functions similarly to the **shocker**, but
applies a non-weakening/non-stunning flash instead. Shocker nerfed to
have much higher power draw, which the flasher also mimics.
- Redid all of the **tickers** to actually use seconds (as advertised in
their descriptions). Their intervals have also been changed to be more
significant: 10-secs, 30-secs, 5-mins, 2-secs (upgraded printer only),
custom 2sec to 10mins (upgraded printer only).
- Adds an **Alden-Saraspova counter circuit**. Scans only immediate
(in-view) surroundings for mineral deposits with artefacts inside and
returns a distance.
- Adds a **bluespace portal circuit**. Consumes bluespace crystals
(either tear apart Telescience OR get high tech levels + every material)
to open a portal with a destination based on a supplied *direction* and
*power*. Worse than telescience in every way (5x5m variance;
cardinal/ordinals only; no cross z-level teleporting; 1-way; BUT it's
portable!)
- Adds a **bubble shield circuit**. Relies on relative coordinate inputs
to generate a low health shield. Max range of 3 tiles. Max lifespan of
60 seconds. Can only project 3 shields at a time.

**To-Do:**
- [x] Test everything
- [x] Add any other quick requests before I hit review
- [x] Fuck around with circuits ingame for a bit to figure out any other
gaps
- [x] Test everything again

---------

Signed-off-by: c#min7 <sadkermit01@gmail.com>
Co-authored-by: SleepyGemmy <99297919+SleepyGemmy@users.noreply.github.com>
This commit is contained in:
c#min7
2026-03-18 13:05:35 +00:00
committed by GitHub
co-authored by SleepyGemmy
parent b5a3a165e1
commit 2c4163a121
4 changed files with 465 additions and 48 deletions
@@ -102,8 +102,13 @@
complexity = 4
inputs = list("target" = IC_PINTYPE_REF)
outputs = list(
"total health %" = IC_PINTYPE_NUMBER,
"total missing health" = IC_PINTYPE_NUMBER
// Basic medical analyser stuff
"brain activity" = IC_PINTYPE_NUMBER,
"heart pulse" = IC_PINTYPE_NUMBER,
"blood pressure" = IC_PINTYPE_LIST,
"blood oxygenation" = IC_PINTYPE_NUMBER,
"body temperature" = IC_PINTYPE_NUMBER,
"in cardiac arrest?" = IC_PINTYPE_BOOLEAN,
)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -114,12 +119,18 @@
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)
if(H.isSynthetic() || H.is_diona()) // incompatible anatomy
return
if(H.Adjacent(get_turf(src))) // Like normal analysers, it can't be used at range.
// Basic medical analyser stuff
set_pin_data(IC_OUTPUT, 1, H.get_brain_result())
set_pin_data(IC_OUTPUT, 2, H.get_pulse_as_number(GETPULSE_TOOL))
set_pin_data(IC_OUTPUT, 3, H.blood_pressure())
set_pin_data(IC_OUTPUT, 4, H.get_blood_oxygenation())
set_pin_data(IC_OUTPUT, 5, H.bodytemperature)
set_pin_data(IC_OUTPUT, 6, H.is_asystole())
push_data()
activate_pin(2)
@@ -132,13 +143,19 @@
complexity = 12
inputs = list("target" = IC_PINTYPE_REF)
outputs = list(
"total health %" = IC_PINTYPE_NUMBER,
"total missing health" = IC_PINTYPE_NUMBER,
"brute damage" = IC_PINTYPE_NUMBER,
"burn damage" = IC_PINTYPE_NUMBER,
"tox damage" = IC_PINTYPE_NUMBER,
"oxy damage" = IC_PINTYPE_NUMBER,
"clone damage" = IC_PINTYPE_NUMBER
// Basic medical analyser stuff
"brain activity" = IC_PINTYPE_NUMBER,
"heart pulse" = IC_PINTYPE_NUMBER,
"blood pressure" = IC_PINTYPE_LIST,
"blood oxygenation" = IC_PINTYPE_NUMBER,
"body temperature" = IC_PINTYPE_NUMBER,
"in cardiac arrest?" = IC_PINTYPE_BOOLEAN,
// Advanced
"brute damage" = IC_PINTYPE_NUMBER,
"burn damage" = IC_PINTYPE_NUMBER,
"toxin damage" = IC_PINTYPE_NUMBER,
"radiation dose" = IC_PINTYPE_NUMBER,
"genetic instability" = IC_PINTYPE_NUMBER
)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
@@ -149,17 +166,24 @@
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, 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())
if(H.isSynthetic() || H.is_diona()) // incompatible anatomy
return
if(H.Adjacent(get_turf(src))) // Like normal analysers, it can't be used at range.
// Basic medical analyser stuff
set_pin_data(IC_OUTPUT, 1, H.get_brain_result())
set_pin_data(IC_OUTPUT, 2, H.get_pulse_as_number(GETPULSE_TOOL))
set_pin_data(IC_OUTPUT, 3, H.blood_pressure())
set_pin_data(IC_OUTPUT, 4, H.get_blood_oxygenation())
set_pin_data(IC_OUTPUT, 5, H.bodytemperature)
set_pin_data(IC_OUTPUT, 6, H.is_asystole())
// Advanced
set_pin_data(IC_OUTPUT, 7, H.getBruteLoss())
set_pin_data(IC_OUTPUT, 8, H.getFireLoss())
set_pin_data(IC_OUTPUT, 9, H.getToxLoss())
set_pin_data(IC_OUTPUT, 10, H.total_radiation)
set_pin_data(IC_OUTPUT, 11, H.getCloneLoss())
push_data()
activate_pin(2)
@@ -532,7 +556,7 @@
GAS_DEUTERIUM = IC_PINTYPE_NUMBER,
GAS_TRITIUM = IC_PINTYPE_NUMBER,
GAS_HELIUM = IC_PINTYPE_NUMBER,
GAS_HELIUMFUE = IC_PINTYPE_NUMBER,
GAS_HELIUMFUEL = IC_PINTYPE_NUMBER,
GAS_SULFUR = IC_PINTYPE_NUMBER,
GAS_NO2 = IC_PINTYPE_NUMBER,
GAS_CHLORINE = IC_PINTYPE_NUMBER,
@@ -760,9 +784,6 @@
var/gas_display_name = GAS_OXYGEN
/obj/item/integrated_circuit/input/gas_sensor/Initialize()
name = "[gas_display_name] sensor"
displayed_name = "[gas_display_name] sensor"
desc = "A tiny [gas_display_name] sensor module similar to that found in a PDA atmosphere analyser."
outputs = list(
gas_name = IC_PINTYPE_NUMBER
)
@@ -785,50 +806,86 @@
activate_pin(2)
/obj/item/integrated_circuit/input/gas_sensor/co2
name = "carbon dioxide sensor"
displayed_name = "carbon dioxide sensor"
desc = "A tiny carbon dioxide sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_CO2
gas_display_name = "carbon dioxide"
/obj/item/integrated_circuit/input/gas_sensor/nitrogen
name = "nitrogen sensor"
displayed_name = "nitrogen sensor"
desc = "A tiny nitrogen sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_NITROGEN
gas_display_name = "nitrogen"
/obj/item/integrated_circuit/input/gas_sensor/phoron
name = "phoron sensor"
displayed_name = "phoron sensor"
desc = "A tiny phoron sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_PHORON
gas_display_name = GAS_PHORON
/obj/item/integrated_circuit/input/gas_sensor/hydrogen_level
name = "hydrogen sensor"
displayed_name = "hydrogen sensor"
desc = "A tiny hydrogen sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_HYDROGEN
gas_display_name = GAS_HYDROGEN
/obj/item/integrated_circuit/input/gas_sensor/deuterium_level
name = "deuterium sensor"
displayed_name = "deuterium sensor"
desc = "A tiny deuterium sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_DEUTERIUM
gas_display_name = GAS_DEUTERIUM
/obj/item/integrated_circuit/input/gas_sensor/tritium_level
name = "tritium sensor"
displayed_name = "tritium sensor"
desc = "A tiny tritium sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_TRITIUM
gas_display_name = GAS_TRITIUM
/obj/item/integrated_circuit/input/gas_sensor/helium_level
name = "helium sensor"
displayed_name = "helium sensor"
desc = "A tiny helium sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_HELIUM
gas_display_name = GAS_HELIUM
/obj/item/integrated_circuit/input/gas_sensor/helium3_level
name = "helium-3 sensor"
displayed_name = "helium-3 sensor"
desc = "A tiny helium-3 sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_HELIUMFUEL
gas_display_name = GAS_HELIUMFUEL
/obj/item/integrated_circuit/input/gas_sensor/sulfurdioxide_level
name = "sulphur dioxide sensor"
displayed_name = "sulphur dioxide sensor"
desc = "A tiny sulphur dioxide sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_SULFUR
gas_display_name = GAS_SULFUR
/obj/item/integrated_circuit/input/gas_sensor/nitrogendioxide_level
name = "nitrogen dioxide sensor"
displayed_name = "nitrogen dioxide sensor"
desc = "A tiny nitrogen dioxide sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_NO2
gas_display_name = GAS_NO2
/obj/item/integrated_circuit/input/gas_sensor/chlorine_level
name = "chlorine sensor"
displayed_name = "chlorine sensor"
desc = "A tiny chlorine sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_CHLORINE
gas_display_name = GAS_CHLORINE
/obj/item/integrated_circuit/input/gas_sensor/watervapor_level
name = "water vapour sensor"
displayed_name = "water vapour sensor"
desc = "A tiny water vapour sensor module similar to that found in a PDA atmosphere analyser."
gas_name = GAS_WATERVAPOR
gas_display_name = GAS_WATERVAPOR
@@ -1073,3 +1130,129 @@
push_data()
activate_pin(1)
return TRUE
/obj/item/integrated_circuit/input/radiation_sensor
name = "radiation sensor"
desc = "A tiny radiation sensor module similar to that found in a geiger counter."
icon_state = "medscan_adv"
complexity = 3
inputs = list()
outputs = list(
"radiation" = IC_PINTYPE_NUMBER
)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT
origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
power_draw_per_use = 20
/obj/item/integrated_circuit/input/radiation_sensor/do_work()
var/turf/T = get_turf(src)
if(!istype(T)) //Invalid input
return
var/radiation_count = SSradiation.get_rads_at_turf(get_turf(src))
if(radiation_count)
set_pin_data(IC_OUTPUT, 1, round(radiation_count,0.1))
else
set_pin_data(IC_OUTPUT, 1, 0)
push_data()
/obj/item/integrated_circuit/input/light_sensor
name = "light sensor"
desc = "A tiny light sensor module."
icon_state = "medscan_adv"
complexity = 3
inputs = list()
outputs = list(
"lumens" = IC_PINTYPE_NUMBER
)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT
origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
power_draw_per_use = 20
/obj/item/integrated_circuit/input/light_sensor/do_work()
var/turf/T = get_turf(src)
if(!istype(T)) //Invalid input
return
var/lumens = T.get_lumcount(0, 3) * 5
if(lumens)
set_pin_data(IC_OUTPUT, 1, round(lumens,0.1))
else
set_pin_data(IC_OUTPUT, 1, 0)
push_data()
/obj/item/integrated_circuit/input/face_scanner
name = "face scanner"
desc = "A complex camera and in-built scanner, similar to an examiner but specifically for people.\
It can identify species type, height and eye colour, and it can estimate age (to the half-decade).\
It also provides a short description of the person and their face (if able)."
icon_state = "video_camera"
complexity = 12
inputs = list("target" = IC_PINTYPE_REF)
outputs = list(
"name" = IC_PINTYPE_STRING,
"description (general)" = IC_PINTYPE_STRING,
"description (face)" = IC_PINTYPE_STRING,
"species" = IC_PINTYPE_STRING,
"height" = IC_PINTYPE_NUMBER,
"estimated age" = IC_PINTYPE_NUMBER,
"eye colour" = IC_PINTYPE_COLOR
)
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 = 5)
power_draw_per_use = 100
/obj/item/integrated_circuit/input/face_scanner/do_work()
var/mob/living/carbon/human/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living/carbon/human)
if(!istype(H)) //Invalid input
return
if(H in view(get_turf(src))) // This is a camera. It can't examine things that it can't see.
set_pin_data(IC_OUTPUT, 1, H.name)
set_pin_data(IC_OUTPUT, 2, H.flavor_texts["general"])
set_pin_data(IC_OUTPUT, 3, H.flavor_texts["face"])
set_pin_data(IC_OUTPUT, 4, H.species.name)
set_pin_data(IC_OUTPUT, 5, H.height)
set_pin_data(IC_OUTPUT, 6, round(H.age, 5))
set_pin_data(IC_OUTPUT, 7, rgb(H.r_eyes, H.g_eyes, H.b_eyes))
push_data()
activate_pin(2)
else
activate_pin(3)
/obj/item/integrated_circuit/input/anomaly_scanner
name = "alden-saraspova counter"
desc = "A miniaturised Alden-Saraspova counter, used to detected anomalous readings. Often used by xenoarchaeologists."
extended_desc = "It can only scan a 14x14 area and has an built-in 15 second cooldown."
icon_state = "medscan_adv"
complexity = 12
outputs = list(
"exotic particle type" = IC_PINTYPE_STRING,
"range detected at" = IC_PINTYPE_NUMBER
)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "anomaly found" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_MAGNET = 6, TECH_BLUESPACE = 4)
power_draw_per_use = 200
cooldown_per_use = 15 SECONDS // sizeable cooldown to prevent view spam
/obj/item/integrated_circuit/input/anomaly_scanner/do_work()
if(!SSxenoarch)
return
var/turf/our_turf = get_turf(src)
for(var/turf/simulated/mineral/scanned_turf in view(our_turf)) // Restrict range to only visible tiles, instead of the entire Z-level like standalone AS counters
if(scanned_turf.artifact_find)
if(scanned_turf.artifact_find.artifact_id)
set_pin_data(IC_OUTPUT, 1, scanned_turf.artifact_find.artifact_id)
else
set_pin_data(IC_OUTPUT, 1, "Exotic Particles (Various)")
set_pin_data(IC_OUTPUT, 2, get_dist(our_turf, scanned_turf))
push_data()
activate_pin(2)
break
..()
@@ -313,7 +313,7 @@
desc = "Used to shock adjacent creatures with electricity."
icon_state = "shocker"
extended_desc = "The circuit accepts a reference to creature to shock. It can shock a target on adjacent tiles. \
Severity determines the power draw and usage of each shock. It accepts values between 0 and 20."
Severity determines the power draw and usage of each shock. It accepts values between 0 and 20. It has a 5 second cooldown."
w_class = WEIGHT_CLASS_TINY
complexity = 24
inputs = list("target" = IC_PINTYPE_REF,"severity" = IC_PINTYPE_NUMBER)
@@ -325,7 +325,7 @@
/obj/item/integrated_circuit/manipulation/shocker/on_data_written()
var/s = get_pin_data(IC_INPUT, 2)
power_draw_per_use = clamp(s,0,20)*8
power_draw_per_use = clamp(s,0,20)*20 // big power draw to shock large creatures
/obj/item/integrated_circuit/manipulation/shocker/do_work()
..()
@@ -339,8 +339,211 @@
to_chat(M, SPAN_DANGER("You feel a light tingle from [src]. Luckily it was charging!"))
return
else
msg_admin_attack("An integrated circuit with a shocker circuit was used to shock [M.name] ([M.ckey]) (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
to_chat(M, SPAN_DANGER("You feel a sharp shock from the [src]!"))
spark(get_turf(M), 3, 1)
M.stun_effect_act(0, clamp(get_pin_data(IC_INPUT, 2),0,20), null)
shocktime = world.time
return
/obj/item/integrated_circuit/manipulation/flasher
name = "flasher circuit"
desc = "Used to flash adjacent creatures."
icon_state = "video_camera"
extended_desc = "The circuit accepts a reference to creature to flash. It can shock a target on adjacent tiles. \
Severity determines the power draw and duration of each flash. It accepts values between 1 and 4. It has a 5 second cooldown."
w_class = WEIGHT_CLASS_TINY
complexity = 24
inputs = list("target" = IC_PINTYPE_REF,"severity" = IC_PINTYPE_NUMBER)
outputs = list()
activators = list("flash" = IC_PINTYPE_PULSE_IN)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 0
var/flashtime
/obj/item/integrated_circuit/manipulation/flasher/on_data_written()
var/s = get_pin_data(IC_INPUT, 2)
power_draw_per_use = clamp(s,1,4)*200 // big power draw to achieve a blinding flash
/obj/item/integrated_circuit/manipulation/flasher/do_work()
..()
var/turf/T = get_turf(src)
var/mob/living/scanned_mob = get_pin_data_as_type(IC_INPUT, 1, /mob/living)
if(!istype(scanned_mob)) //Invalid input
return
if(!T.Adjacent(scanned_mob))
return //Can't reach
if((flashtime + (5 SECONDS)) > world.time) // Cooldown
visible_message(SPAN_WARNING("You see a weak flash from the [src]."))
else
var/flash_duration = clamp(get_pin_data(IC_INPUT, 2),1,4)
scanned_mob.flash_act(length = flash_duration SECONDS)
flashtime = world.time
msg_admin_attack("An integrated circuit with a flasher was used to flash [scanned_mob.name] ([scanned_mob.ckey]) (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
return
/obj/item/integrated_circuit/manipulation/portal_opener // basically a mini telescience setup, but consumes bluespace crystals, is one-way, only along cardinals and ordinals, cannot cross z-levels, and more imprecise (higher chance of getting beamed into a wall/table).
name = "bluespace portal circuit"
desc = "A miniaturised circuit that uses bluespace crystals to open a one-way bluespace portal. Costlier to use than a standard telescience set up, but portable."
extended_desc = "The circuit can store 3 bluespace crystals; each crystal is 1 use. Power determines number of tiles jumped (max 50m). Lifespan determines how long the portal is there (3-15 sec). Imprecise with a high variance <b>(DANGER IN CLOSED SPACES!)</b>. It has a cooldown of 30 seconds."
icon_state = "shocker"
w_class = WEIGHT_CLASS_TINY
size = 20
complexity = 30 // needs a big assembly if you want to use this in any non-simplistic way
inputs = list("direction" = IC_PINTYPE_DIR, "power" = IC_PINTYPE_NUMBER, "lifespan" = IC_PINTYPE_NUMBER)
outputs = list("bluespace crystals stored" = IC_PINTYPE_NUMBER)
activators = list("open portal" = IC_PINTYPE_PULSE_IN, "portal opened" = IC_PINTYPE_PULSE_OUT, "cannot open portal" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 100 // multiplied by up to 50 during on_data_written(). should slurp up power in smaller assemblies or those without power generation
cooldown_per_use = 30 SECONDS // big cooldown
origin_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 5)
/// Lazylist of stored bluespace crystals stored in the device for amount checks/deletions.
var/list/obj/item/bluespace_crystal/crystals
/// Maximum number of crystals the circuit can store.
var/max_crystals = 3
/obj/item/integrated_circuit/manipulation/portal_opener/Destroy()
for(var/obj/item/bluespace_crystal/crystal in crystals)
crystal.dropInto(get_turf(assembly))
LAZYNULL(crystals)
return ..()
/obj/item/integrated_circuit/manipulation/portal_opener/attackby(obj/item/attacking_item, mob/user, params)
if(istype(attacking_item, /obj/item/bluespace_crystal))
if(LAZYLEN(crystals) >= max_crystals)
to_chat(user, SPAN_WARNING("There are not enough crystal slots."))
return
user.visible_message("[user] inserts [attacking_item] into \the [src]'s crystal slot.", SPAN_NOTICE("You insert [attacking_item] into \the [src]'s crystal slot."))
user.drop_item(src)
LAZYADD(crystals, attacking_item)
attacking_item.forceMove(null)
else
..()
/obj/item/integrated_circuit/manipulation/portal_opener/on_data_written()
power_draw_per_use = round(between(100, 100*(get_pin_data(IC_INPUT, 2)), 5000), 50) // potentially big power draw for a maximum jump range
..()
/obj/item/integrated_circuit/manipulation/portal_opener/do_work()
if(!LAZYLEN(crystals) >= 1) // no crystals, no portal
activate_pin(3)
return
var/turf/assembly_turf = get_turf(assembly)
if(assembly_turf)
// maffs
var/proj_power = round(between(1, get_pin_data(IC_INPUT, 2), 50), 1) // max range of 50 metres
var/proj_rotation = dir2angle(get_pin_data(IC_INPUT, 1)) // in degrees please
var/proj_angle = 60
var/datum/projectile_data/proj_data = projectile_trajectory(assembly_turf.x, assembly_turf.y, proj_rotation, proj_angle, proj_power)
var/destination_x = clamp(round(proj_data.dest_x, 1), 1, world.maxx) // dont want to exit map
var/destination_y = clamp(round(proj_data.dest_y, 1), 1, world.maxy)
var/turf/portal_destination = locate(destination_x, destination_y, assembly_turf.z)
// effects
playsound(assembly.loc, 'sound/weapons/flash.ogg', 25, 1)
spark(assembly, 5, GLOB.alldirs)
// portal creation
var/lifespan = between(30, SecondsToTicks(get_pin_data(IC_INPUT, 2)), 150)
var/obj/effect/portal/our_portal = new /obj/effect/portal(assembly_turf, null, null, lifespan, 0)
our_portal.set_target(portal_destination)
our_portal.precision = 2 // very imprecise, 5x5 turf variance. best used in very open spaces
our_portal.has_failed = FALSE
// consume a bluespace crystal
var/obj/item/bluespace_crystal/consumed_bsc = LAZYACCESS(crystals, 1)
LAZYREMOVE(crystals, consumed_bsc)
qdel(consumed_bsc)
activate_pin(2)
else
activate_pin(3)
set_pin_data(IC_OUTPUT, 1, LAZYLEN(crystals)) // set the bluespace crystals counter
push_data()
..()
/obj/item/integrated_circuit/manipulation/bubble_shield
name = "bubble shield circuit"
desc = "A large bubble shield generator circuit, like those found in atmospheric emergency shields... sans all the automated features."
extended_desc = "Receives relative coordinates to project a shield upon (must be within 3 tiles). Strength determines shield hitpoints (range: 10-50). Lifespan given in seconds (range: 2-60 secs). Can only sustain 3 shields."
icon_state = "power_transmitter"
w_class = WEIGHT_CLASS_TINY
size = 30 // needs a medium-sized assembly minimum
complexity = 20
inputs = list("relative X" = IC_PINTYPE_NUMBER, "relative Y" = IC_PINTYPE_NUMBER, "strength" = IC_PINTYPE_NUMBER, "lifespan" = IC_PINTYPE_NUMBER)
outputs = list("shields projected" = IC_PINTYPE_NUMBER)
activators = list("create shield" = IC_PINTYPE_PULSE_IN, "shield created" = IC_PINTYPE_PULSE_OUT, "shield capacity reached" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 100
origin_tech = list(TECH_ENGINEERING = 4, TECH_MAGNET = 5)
/// A list of shields deployed by this circuit for amount checks/shield deletions.
var/list/obj/machinery/shield/deployed_shields
/obj/item/integrated_circuit/manipulation/bubble_shield/Destroy()
QDEL_LAZYLIST(deployed_shields)
return ..()
/obj/item/integrated_circuit/manipulation/bubble_shield/proc/kill_shield(var/obj/machinery/shield/shield)
LAZYREMOVE(deployed_shields, shield)
qdel(shield)
set_pin_data(IC_OUTPUT, 1, LAZYLEN(deployed_shields))
/obj/item/integrated_circuit/manipulation/bubble_shield/do_work()
if(!assembly)
return
var/strength = between(10, get_pin_data(IC_INPUT, 3), 50)
var/lifespan = between(2, get_pin_data(IC_INPUT, 4), 60)
// find target turf based on relative coordinates
var/target_relative_x = round(get_pin_data(IC_INPUT, 1))
var/target_relative_y = round(get_pin_data(IC_INPUT, 2))
var/turf/our_turf = get_turf(assembly)
var/target_x = our_turf.x + target_relative_x
var/target_y = our_turf.y + target_relative_y
if (target_x > world.maxx || target_x < 1 || target_y > world.maxy || target_y < 1)
return // Off the edge of the map.
var/target_turf = locate(target_x, target_y, our_turf.z)
// create the shield
if(target_turf && (target_turf in view(3, our_turf))) // must be in view and within 3 tiles
if(locate(/obj/machinery/shield) in target_turf) // already got a shield
return
if(LAZYLEN(deployed_shields) >= 3) // do not generate more than 3 shields
activate_pin(2)
return
//actually creating the shield
var/obj/machinery/shield/shield = new /obj/machinery/shield(target_turf)
shield.name = "bubble shield"
shield.health = strength
LAZYADD(deployed_shields, shield)
addtimer(CALLBACK(src, PROC_REF(kill_shield), shield), lifespan SECONDS, TIMER_DELETE_ME) // clean up after ourselves later
set_pin_data(IC_OUTPUT, 1, LAZYLEN(deployed_shields))
activate_pin(1)
// power stuff
if(deployed_shields)
power_draw_per_use = initial(power_draw_per_use)*(strength/10)*LAZYLEN(deployed_shields)
else
power_draw_per_use = initial(power_draw_per_use)
..()
/obj/item/integrated_circuit/manipulation/bubble_shield/power_fail()
for(var/obj/machinery/shield/shield in deployed_shields)
kill_shield(shield)
@@ -59,25 +59,21 @@
inputs = list("delay time" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/time/delay/custom/do_work()
var/delay_input = get_pin_data(IC_INPUT, 1)
if(delay_input && isnum(delay_input) )
var/new_delay = between(1, delay_input, 1 HOUR)
delay = new_delay
/obj/item/integrated_circuit/time/delay/custom/on_data_written()
delay = between(1, get_pin_data(IC_INPUT, 1), 1 HOUR)
..()
/obj/item/integrated_circuit/time/ticker
name = "ticker circuit"
desc = "This circuit sends an automatic pulse every four seconds."
name = "ten second ticker"
desc = "This circuit sends an automatic pulse every ten seconds."
icon_state = "tick-m"
complexity = 8
var/ticks_to_pulse = 4
var/seconds_to_pulse = 10 SECONDS
var/ticks_completed = 0
var/is_running = FALSE
inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN)
activators = list("outgoing pulse" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 4
/obj/item/integrated_circuit/time/ticker/Destroy()
@@ -97,31 +93,54 @@
/obj/item/integrated_circuit/time/ticker/process()
var/process_ticks = SSelectronics.wait
ticks_completed += process_ticks
if(ticks_completed >= ticks_to_pulse)
if(ticks_to_pulse >= process_ticks)
ticks_completed -= ticks_to_pulse
if(ticks_completed >= SecondsToTicks(seconds_to_pulse))
if(SecondsToTicks(seconds_to_pulse) >= process_ticks)
ticks_completed -= SecondsToTicks(seconds_to_pulse)
else
ticks_completed = 0
activate_pin(1)
/obj/item/integrated_circuit/time/ticker/fast
name = "fast ticker"
name = "two second ticker"
desc = "This advanced circuit sends an automatic pulse every two seconds."
icon_state = "tick-f"
complexity = 12
ticks_to_pulse = 2
seconds_to_pulse = 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."
name = "thirty second ticker"
desc = "This simple circuit sends an automatic pulse every thirty seconds."
icon_state = "tick-s"
complexity = 4
ticks_to_pulse = 6
seconds_to_pulse = 30 SECONDS
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 2
/obj/item/integrated_circuit/time/ticker/very_slow
name = "five minute ticker"
desc = "This simple circuit sends an automatic pulse every five minutes (three hundred seconds)."
icon_state = "tick-s"
complexity = 4
seconds_to_pulse = 300 SECONDS
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 2
/obj/item/integrated_circuit/time/ticker/custom
name = "custom ticker"
desc = "This advanced circuit sends an automatic pulse with a configurable interval between 2 and 600 seconds. Default: 60 seconds."
icon_state = "tick-f"
complexity = 15
seconds_to_pulse = 60 SECONDS
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 8
inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN, "ticker time" = IC_PINTYPE_NUMBER)
/obj/item/integrated_circuit/time/ticker/custom/on_data_written()
seconds_to_pulse = between(2 SECONDS, get_pin_data(IC_INPUT, 2), 600 SECONDS)
..()
/obj/item/integrated_circuit/time/clock
name = "integrated clock"
desc = "Tells you what the local time is, specific to your station, planet, or facility."