diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
index c187f49473..682e76a46e 100644
--- a/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -1,139 +1,6 @@
/obj/item/integrated_circuit/manipulation
category_text = "Manipulation"
-/obj/item/integrated_circuit/manipulation/weapon_firing
- name = "weapon firing mechanism"
- desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it."
- extended_desc = "The firing mechanism can slot in any energy weapon. \
- The first and second inputs need to be numbers which correspond to coordinates for the gun to fire at relative to the machine itself. \
- The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode will switch between \
- lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon itself, not the assembly. If you wish to fire the gun while the circuit is in \
- hand, you will need to use an assembly that is a gun."
- complexity = 20
- w_class = WEIGHT_CLASS_SMALL
- size = 3
- inputs = list(
- "target X rel" = IC_PINTYPE_NUMBER,
- "target Y rel" = IC_PINTYPE_NUMBER,
- "mode" = IC_PINTYPE_BOOLEAN
- )
- outputs = list("reference to gun" = IC_PINTYPE_REF)
- activators = list(
- "fire" = IC_PINTYPE_PULSE_IN
-
- )
- var/obj/item/gun/energy/installed_gun = null
- spawn_flags = IC_SPAWN_RESEARCH
- action_flags = IC_ACTION_COMBAT
- power_draw_per_use = 0
- ext_cooldown = 1
- var/mode = FALSE
-
- var/stun_projectile = null //stun mode projectile type
- var/stun_projectile_sound
- var/lethal_projectile = null //lethal mode projectile type
- var/lethal_projectile_sound
-
- demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby()
-
-
-
-/obj/item/integrated_circuit/manipulation/weapon_firing/Destroy()
- qdel(installed_gun)
- return ..()
-
-/obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user)
- if(istype(O, /obj/item/gun/energy))
- var/obj/item/gun/gun = O
- if(installed_gun)
- to_chat(user, "There's already a weapon installed.")
- return
- user.transferItemToLoc(gun,src)
- installed_gun = gun
- var/list/gun_properties = gun.get_turret_properties()
- to_chat(user, "You slide \the [gun] into the firing mechanism.")
- playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
- stun_projectile = gun_properties["stun_projectile"]
- stun_projectile_sound = gun_properties["stun_projectile_sound"]
- lethal_projectile = gun_properties["lethal_projectile"]
- lethal_projectile_sound = gun_properties["lethal_projectile_sound"]
- if(gun_properties["shot_delay"])
- cooldown_per_use = gun_properties["shot_delay"]*10
- if(cooldown_per_use<30)
- cooldown_per_use = 30
- if(gun_properties["reqpower"])
- power_draw_per_use = gun_properties["reqpower"]
- set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun))
- push_data()
- else
- ..()
-
-/obj/item/integrated_circuit/manipulation/weapon_firing/attack_self(var/mob/user)
- if(installed_gun)
- installed_gun.forceMove(drop_location())
- to_chat(user, "You slide \the [installed_gun] out of the firing mechanism.")
- size = initial(size)
- playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
- installed_gun = null
- set_pin_data(IC_OUTPUT, 1, WEAKREF(null))
- push_data()
- else
- to_chat(user, "There's no weapon to remove from the mechanism.")
-
-/obj/item/integrated_circuit/manipulation/weapon_firing/do_work()
- if(!installed_gun || !installed_gun.handle_pins())
- return
- if(!isturf(assembly.loc) && !(assembly.can_fire_equipped && ishuman(assembly.loc)))
- return
- set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun))
- push_data()
- var/datum/integrated_io/xo = inputs[1]
- var/datum/integrated_io/yo = inputs[2]
- var/datum/integrated_io/mode1 = inputs[3]
-
- mode = mode1.data
- if(assembly)
- if(isnum(xo.data))
- xo.data = round(xo.data, 1)
- if(isnum(yo.data))
- yo.data = round(yo.data, 1)
-
- var/turf/T = get_turf(assembly)
- var/target_x = CLAMP(T.x + xo.data, 0, world.maxx)
- var/target_y = CLAMP(T.y + yo.data, 0, world.maxy)
-
- assembly.visible_message("[assembly] fires [installed_gun]!")
- shootAt(locate(target_x, target_y, T.z))
-
-/obj/item/integrated_circuit/manipulation/weapon_firing/proc/shootAt(turf/target)
- var/turf/T = get_turf(src)
- var/turf/U = target
- if(!istype(T) || !istype(U))
- return
- if(!installed_gun.cell)
- return
- if(!installed_gun.cell.charge)
- return
- var/obj/item/ammo_casing/energy/shot = installed_gun.ammo_type[mode?2:1]
- if(installed_gun.cell.charge < shot.e_cost)
- return
- if(!shot)
- return
- update_icon()
- var/obj/item/projectile/A
- if(!mode)
- A = new stun_projectile(T)
- playsound(loc, stun_projectile_sound, 75, 1)
- else
- A = new lethal_projectile(T)
- playsound(loc, lethal_projectile_sound, 75, 1)
- installed_gun.cell.use(shot.e_cost)
- //Shooting Code:
- A.preparePixelProjectile(target, src)
- A.fire()
- log_attack("[assembly] [REF(assembly)] has fired [installed_gun].")
- return A
-
/obj/item/integrated_circuit/manipulation/locomotion
name = "locomotion circuit"
desc = "This allows a machine to move in a given direction."
@@ -171,80 +38,6 @@
return FALSE
return FALSE
-/obj/item/integrated_circuit/manipulation/grenade
- name = "grenade primer"
- desc = "This circuit comes with the ability to attach most types of grenades and prime them at will."
- extended_desc = "The time between priming and detonation is limited to between 1 to 12 seconds, but is optional. \
- If the input is not set, not a number, or a number less than 1, the grenade's built-in timing will be used. \
- Beware: Once primed, there is no aborting the process!"
- icon_state = "grenade"
- complexity = 30
- cooldown_per_use = 10
- inputs = list("detonation time" = IC_PINTYPE_NUMBER)
- outputs = list()
- activators = list("prime grenade" = IC_PINTYPE_PULSE_IN)
- spawn_flags = IC_SPAWN_RESEARCH
- action_flags = IC_ACTION_COMBAT
- var/obj/item/grenade/attached_grenade
- var/pre_attached_grenade_type
- demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby()
-
-/obj/item/integrated_circuit/manipulation/grenade/Initialize()
- . = ..()
- if(pre_attached_grenade_type)
- var/grenade = new pre_attached_grenade_type(src)
- attach_grenade(grenade)
-
-/obj/item/integrated_circuit/manipulation/grenade/Destroy()
- if(attached_grenade && !attached_grenade.active)
- attached_grenade.forceMove(loc)
- detach_grenade()
- return ..()
-
-/obj/item/integrated_circuit/manipulation/grenade/attackby(var/obj/item/grenade/G, var/mob/user)
- if(istype(G))
- if(attached_grenade)
- to_chat(user, "There is already a grenade attached!")
- else if(user.transferItemToLoc(G,src))
- user.visible_message("\The [user] attaches \a [G] to \the [src]!", "You attach \the [G] to \the [src].")
- attach_grenade(G)
- G.forceMove(src)
- else
- return ..()
-
-/obj/item/integrated_circuit/manipulation/grenade/attack_self(var/mob/user)
- if(attached_grenade)
- user.visible_message("\The [user] removes \an [attached_grenade] from \the [src]!", "You remove \the [attached_grenade] from \the [src].")
- user.put_in_hands(attached_grenade)
- detach_grenade()
- else
- return ..()
-
-/obj/item/integrated_circuit/manipulation/grenade/do_work()
- if(attached_grenade && !attached_grenade.active)
- var/datum/integrated_io/detonation_time = inputs[1]
- var/dt
- if(isnum(detonation_time.data) && detonation_time.data > 0)
- dt = CLAMP(detonation_time.data, 1, 12)*10
- else
- dt = 15
- addtimer(CALLBACK(attached_grenade, /obj/item/grenade.proc/prime), dt)
- var/atom/holder = loc
- message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]")
-
-// These procs do not relocate the grenade, that's the callers responsibility
-/obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/grenade/G)
- attached_grenade = G
- G.forceMove(src)
- desc += " \An [attached_grenade] is attached to it!"
-
-/obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade()
- if(!attached_grenade)
- return
- attached_grenade.forceMove(drop_location())
- attached_grenade = null
- desc = initial(desc)
-
/obj/item/integrated_circuit/manipulation/plant_module
name = "plant manipulation module"
desc = "Used to uproot weeds and harvest/plant trays."
@@ -478,7 +271,7 @@
/obj/item/integrated_circuit/manipulation/thrower
name = "thrower"
- desc = "A compact launcher to throw things from inside or nearby tiles."
+ 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 \
@@ -544,7 +337,7 @@
A.embedding = list("embed_chance" = 0)
//throw it
assembly.visible_message("[assembly] has thrown [A]!")
- log_attack("[assembly] [REF(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, , , , CALLBACK(src, .proc/post_throw, A))
@@ -813,4 +606,7 @@
set_pin_data(IC_OUTPUT, 1, assembly.detail_color)
push_data()
- activate_pin(3)
\ No newline at end of file
+ activate_pin(3)
+
+
+
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
index 0d8bf96afc..fb8fce10b2 100644
--- a/code/modules/integrated_electronics/subtypes/reagents.dm
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -548,9 +548,12 @@
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."
+ into the smoke clouds when activated. The reagents are consumed when the smoke is made. Requires at least 10 units of reagents to generate smoke."
ext_cooldown = 1
+
volume = 100
+ container_type = OPENCONTAINER
+
complexity = 20
cooldown_per_use = 1 SECONDS
inputs = list()
@@ -573,6 +576,7 @@
if(changetype == ADD_REAGENT)
notified = FALSE
push_vol()
+
/obj/item/integrated_circuit/reagent/smoke/do_work(ord)
switch(ord)
if(1)
@@ -598,9 +602,10 @@
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."
+ 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. Requires at least 10 units of reagents to work."
volume = 30
+ container_type = OPENCONTAINER
complexity = 20
cooldown_per_use = 6 SECONDS
@@ -631,7 +636,7 @@
/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)
+ if(!reagents || reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS || busy)
push_data()
activate_pin(3)
return
diff --git a/code/modules/integrated_electronics/subtypes/weaponized.dm b/code/modules/integrated_electronics/subtypes/weaponized.dm
new file mode 100644
index 0000000000..086b53f417
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/weaponized.dm
@@ -0,0 +1,344 @@
+/obj/item/integrated_circuit/weaponized
+ category_text = "Weaponized"
+
+/obj/item/integrated_circuit/weaponized/weapon_firing
+ name = "weapon firing mechanism"
+ desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it."
+ extended_desc = "The firing mechanism can slot in any energy weapon. \
+ The first and second inputs need to be numbers which correspond to coordinates for the gun to fire at relative to the machine itself. \
+ The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode will switch between \
+ lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon itself, not the assembly. If you wish to fire the gun while the circuit is in \
+ hand, you will need to use an assembly that is a gun."
+ complexity = 20
+ w_class = WEIGHT_CLASS_SMALL
+ size = 3
+ inputs = list(
+ "target X rel" = IC_PINTYPE_NUMBER,
+ "target Y rel" = IC_PINTYPE_NUMBER,
+ "mode" = IC_PINTYPE_BOOLEAN
+ )
+ outputs = list("reference to gun" = IC_PINTYPE_REF)
+ activators = list(
+ "fire" = IC_PINTYPE_PULSE_IN
+
+ )
+ var/obj/item/gun/energy/installed_gun = null
+ spawn_flags = IC_SPAWN_RESEARCH
+ action_flags = IC_ACTION_COMBAT
+ power_draw_per_use = 0
+ ext_cooldown = 1
+ var/mode = FALSE
+
+ var/stun_projectile = null //stun mode projectile type
+ var/stun_projectile_sound
+ var/lethal_projectile = null //lethal mode projectile type
+ var/lethal_projectile_sound
+
+ demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby()
+
+
+
+/obj/item/integrated_circuit/weaponized/weapon_firing/Destroy()
+ qdel(installed_gun)
+ return ..()
+
+/obj/item/integrated_circuit/weaponized/weapon_firing/attackby(var/obj/O, var/mob/user)
+ if(istype(O, /obj/item/gun/energy))
+ var/obj/item/gun/gun = O
+ if(installed_gun)
+ to_chat(user, "There's already a weapon installed.")
+ return
+ user.transferItemToLoc(gun,src)
+ installed_gun = gun
+ var/list/gun_properties = gun.get_turret_properties()
+ to_chat(user, "You slide \the [gun] into the firing mechanism.")
+ playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
+ stun_projectile = gun_properties["stun_projectile"]
+ stun_projectile_sound = gun_properties["stun_projectile_sound"]
+ lethal_projectile = gun_properties["lethal_projectile"]
+ lethal_projectile_sound = gun_properties["lethal_projectile_sound"]
+ if(gun_properties["shot_delay"])
+ cooldown_per_use = gun_properties["shot_delay"]*10
+ if(cooldown_per_use<30)
+ cooldown_per_use = 30
+ if(gun_properties["reqpower"])
+ power_draw_per_use = gun_properties["reqpower"]
+ set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun))
+ push_data()
+ else
+ ..()
+
+/obj/item/integrated_circuit/weaponized/weapon_firing/attack_self(var/mob/user)
+ if(installed_gun)
+ installed_gun.forceMove(drop_location())
+ to_chat(user, "You slide \the [installed_gun] out of the firing mechanism.")
+ size = initial(size)
+ playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
+ installed_gun = null
+ set_pin_data(IC_OUTPUT, 1, WEAKREF(null))
+ push_data()
+ else
+ to_chat(user, "There's no weapon to remove from the mechanism.")
+
+/obj/item/integrated_circuit/weaponized/weapon_firing/do_work()
+ if(!installed_gun || !installed_gun.handle_pins())
+ return
+ if(!isturf(assembly.loc) && !(assembly.can_fire_equipped && ishuman(assembly.loc)))
+ return
+ set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun))
+ push_data()
+ var/datum/integrated_io/xo = inputs[1]
+ var/datum/integrated_io/yo = inputs[2]
+ var/datum/integrated_io/mode1 = inputs[3]
+
+ mode = mode1.data
+ if(assembly)
+ if(isnum(xo.data))
+ xo.data = round(xo.data, 1)
+ if(isnum(yo.data))
+ yo.data = round(yo.data, 1)
+
+ var/turf/T = get_turf(assembly)
+ var/target_x = CLAMP(T.x + xo.data, 0, world.maxx)
+ var/target_y = CLAMP(T.y + yo.data, 0, world.maxy)
+
+ assembly.visible_message("[assembly] fires [installed_gun]!")
+ shootAt(locate(target_x, target_y, T.z))
+
+/obj/item/integrated_circuit/weaponized/weapon_firing/proc/shootAt(turf/target)
+ var/turf/T = get_turf(src)
+ var/turf/U = target
+ if(!istype(T) || !istype(U))
+ return
+ if(!installed_gun.cell)
+ return
+ if(!installed_gun.cell.charge)
+ return
+ var/obj/item/ammo_casing/energy/shot = installed_gun.ammo_type[mode?2:1]
+ if(installed_gun.cell.charge < shot.e_cost)
+ return
+ if(!shot)
+ return
+ update_icon()
+ var/obj/item/projectile/A
+ if(!mode)
+ A = new stun_projectile(T)
+ playsound(loc, stun_projectile_sound, 75, 1)
+ else
+ A = new lethal_projectile(T)
+ playsound(loc, lethal_projectile_sound, 75, 1)
+ installed_gun.cell.use(shot.e_cost)
+ //Shooting Code:
+ A.preparePixelProjectile(target, src)
+ A.fire()
+ log_attack("[assembly] [REF(assembly)] has fired [installed_gun].")
+ return A
+
+/obj/item/integrated_circuit/weaponized/grenade
+ name = "grenade primer"
+ desc = "This circuit comes with the ability to attach most types of grenades and prime them at will."
+ extended_desc = "The time between priming and detonation is limited to between 1 to 12 seconds, but is optional. \
+ If the input is not set, not a number, or a number less than 1, the grenade's built-in timing will be used. \
+ Beware: Once primed, there is no aborting the process!"
+ icon_state = "grenade"
+ complexity = 30
+ cooldown_per_use = 10
+ inputs = list("detonation time" = IC_PINTYPE_NUMBER)
+ outputs = list()
+ activators = list("prime grenade" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_RESEARCH
+ action_flags = IC_ACTION_COMBAT
+ var/obj/item/grenade/attached_grenade
+ var/pre_attached_grenade_type
+ demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby()
+
+/obj/item/integrated_circuit/weaponized/grenade/Initialize()
+ . = ..()
+ if(pre_attached_grenade_type)
+ var/grenade = new pre_attached_grenade_type(src)
+ attach_grenade(grenade)
+
+/obj/item/integrated_circuit/weaponized/grenade/Destroy()
+ if(attached_grenade && !attached_grenade.active)
+ attached_grenade.forceMove(loc)
+ detach_grenade()
+ return ..()
+
+/obj/item/integrated_circuit/weaponized/grenade/attackby(var/obj/item/grenade/G, var/mob/user)
+ if(istype(G))
+ if(attached_grenade)
+ to_chat(user, "There is already a grenade attached!")
+ else if(user.transferItemToLoc(G,src))
+ user.visible_message("\The [user] attaches \a [G] to \the [src]!", "You attach \the [G] to \the [src].")
+ attach_grenade(G)
+ G.forceMove(src)
+ else
+ return ..()
+
+/obj/item/integrated_circuit/weaponized/grenade/attack_self(var/mob/user)
+ if(attached_grenade)
+ user.visible_message("\The [user] removes \an [attached_grenade] from \the [src]!", "You remove \the [attached_grenade] from \the [src].")
+ user.put_in_hands(attached_grenade)
+ detach_grenade()
+ else
+ return ..()
+
+/obj/item/integrated_circuit/weaponized/grenade/do_work()
+ if(attached_grenade && !attached_grenade.active)
+ var/datum/integrated_io/detonation_time = inputs[1]
+ var/dt
+ if(isnum(detonation_time.data) && detonation_time.data > 0)
+ dt = CLAMP(detonation_time.data, 1, 12)*10
+ else
+ dt = 15
+ addtimer(CALLBACK(attached_grenade, /obj/item/grenade.proc/prime), dt)
+ var/atom/holder = loc
+ message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]")
+
+// These procs do not relocate the grenade, that's the callers responsibility
+/obj/item/integrated_circuit/weaponized/grenade/proc/attach_grenade(var/obj/item/grenade/G)
+ attached_grenade = G
+ G.forceMove(src)
+ desc += " \An [attached_grenade] is attached to it!"
+
+/obj/item/integrated_circuit/weaponized/grenade/proc/detach_grenade()
+ if(!attached_grenade)
+ return
+ attached_grenade.forceMove(drop_location())
+ attached_grenade = null
+ desc = initial(desc)
+
+/obj/item/integrated_circuit/weaponized/air_cannon
+ name = "pneumatic cannon"
+ desc = "A compact pneumatic cannon to throw things from inside or nearby tiles at a high enough velocity to cause damage. Requires air from a canister to fire."
+ 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 launch 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 launch something while the assembly is in hand."
+ complexity = 75
+ w_class = WEIGHT_CLASS_SMALL
+ size = 4
+ cooldown_per_use = 30
+ ext_cooldown = 15
+ inputs = list(
+ "target X rel" = IC_PINTYPE_NUMBER,
+ "target Y rel" = IC_PINTYPE_NUMBER,
+ "projectile" = IC_PINTYPE_REF,
+ "canister" = IC_PINTYPE_REF
+ )
+ outputs = list()
+ activators = list(
+ "fire" = IC_PINTYPE_PULSE_IN
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+ action_flags = IC_ACTION_COMBAT
+ power_draw_per_use = 50
+ var/mole_requirement = 1
+
+/obj/item/integrated_circuit/weaponized/air_cannon/do_work()
+
+ 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)
+ var/obj/item/integrated_circuit/atmospherics/AT = get_pin_data_as_type(IC_INPUT, 4, /obj/item/integrated_circuit/atmospherics)
+
+ if(!A || A.anchored || A.throwing || A == assembly || istype(A, /obj/item/twohanded) || istype(A, /obj/item/transfer_valve))
+ return
+
+ if(!AT || !AT.air_contents)
+ return
+
+ if (istype(assembly.loc, /obj/item/implant/storage)) //Prevents the more abusive form of chestgun.
+ return
+
+ if(max_w_class && (A.w_class > max_w_class))
+ return
+
+ if(!assembly.can_fire_equipped && ishuman(assembly.loc))
+ return
+
+ // Is the target inside the assembly or close to it?
+ if(!check_target(A, exclude_components = TRUE))
+ return
+
+ var/turf/T = get_turf(get_object())
+ if(!T)
+ 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(!M.temporarilyRemoveItemFromInventory(A))
+ return
+
+ var/datum/gas_mixture/source_air = AT.return_air()
+ var/datum/gas_mixture/target_air = T.return_air()
+
+ if(!source_air || !target_air)
+ return
+
+ if(source_air.total_moles() <= mole_requirement)
+ 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)
+ assembly.visible_message("[assembly] has thrown [A]!")
+ log_attack("[assembly] [REF(assembly)] has thrown [A] with lethal force.")
+ A.forceMove(drop_location())
+ A.throw_at(locate(x_abs, y_abs, T.z), range, 3)
+
+ var/datum/gas_mixture/removed = source_air.remove(mole_requirement)
+ target_air.merge(removed)
+
+ air_update_turf()
+
+ // If the item came from a grabber now we can update the outputs since we've thrown it.
+ if(istype(G))
+ G.update_outputs()
+
+
+/obj/item/integrated_circuit/weaponized/stun
+ name = "electronic stun module"
+ desc = "Used to stun a target holding the device via electricity."
+ icon_state = "power_relay"
+ extended_desc = "Attempts to stun the holder of this device, with the strength input being the strength of the stun, from 1 to 70."
+ complexity = 60
+ size = 4
+ inputs = list("strength" = IC_PINTYPE_NUMBER)
+ activators = list("stun" = IC_PINTYPE_PULSE_IN, "on success" = IC_PINTYPE_PULSE_OUT, "on fail" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 2000
+ cooldown_per_use = 50
+ ext_cooldown = 25
+
+
+/obj/item/integrated_circuit/weaponized/stun/do_work()
+ var/stunforce = CLAMP(get_pin_data(IC_INPUT, 1),1,70)
+ var/mob/living/L = assembly.loc
+ if(attempt_stun(L,stunforce))
+ activate_pin(2)
+ else
+ activate_pin(3)
+
+/obj/item/integrated_circuit/weaponized/proc/attempt_stun(var/mob/living/L,var/stunforce = 70) //Copied from stunbaton code.
+
+ if(!L || !isliving(L))
+ return 0
+
+ L.Knockdown(stunforce)
+ SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
+
+ message_admins("stunned someone with an assembly. Last touches: Assembly: [assembly.fingerprintslast] Circuit: [fingerprintslast]")
+
+ L.visible_message("\The [assembly] has stunned \the [L] with \the [src]!", "\The [assembly] has stunned you with \the [src]!")
+ playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ H.forcesay(GLOB.hit_appends)
+
+ return 1
\ No newline at end of file
diff --git a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm
index 246205f7b6..547c1a5768 100644
--- a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -14,7 +14,6 @@
ext_cooldown = 25
/obj/item/integrated_circuit/manipulation/electric_stimulator/do_work()
- ..()
set_pin_data(IC_OUTPUT, 1, 0)
var/mob/living/M = get_pin_data_as_type(IC_INPUT, 1, /mob/living)
if(!check_target(M))
diff --git a/tgstation.dme b/tgstation.dme
index 9e9c6fe593..b792852358 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1753,6 +1753,7 @@
#include "code\modules\integrated_electronics\subtypes\text.dm"
#include "code\modules\integrated_electronics\subtypes\time.dm"
#include "code\modules\integrated_electronics\subtypes\trig.dm"
+#include "code\modules\integrated_electronics\subtypes\weaponized.dm"
#include "code\modules\jobs\access.dm"
#include "code\modules\jobs\job_exp.dm"
#include "code\modules\jobs\jobs.dm"