diff --git a/baystation12.dme b/baystation12.dme
index cf62483361c..cef04a6edfd 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -225,6 +225,7 @@
#include "code\datums\wires\particle_accelerator.dm"
#include "code\datums\wires\radio.dm"
#include "code\datums\wires\robot.dm"
+#include "code\datums\wires\syndicatebomb.dm"
#include "code\datums\wires\vending.dm"
#include "code\datums\wires\wires.dm"
#include "code\defines\obj.dm"
@@ -418,6 +419,7 @@
#include "code\game\machinery\status_display.dm"
#include "code\game\machinery\suit_storage_unit.dm"
#include "code\game\machinery\syndicatebeacon.dm"
+#include "code\game\machinery\syndicatebomb.dm"
#include "code\game\machinery\telepad.dm"
#include "code\game\machinery\teleporter.dm"
#include "code\game\machinery\transformer.dm"
@@ -646,6 +648,7 @@
#include "code\game\objects\items\weapons\cosmetics.dm"
#include "code\game\objects\items\weapons\dice.dm"
#include "code\game\objects\items\weapons\dna_injector.dm"
+#include "code\game\objects\items\weapons\dnascrambler.dm"
#include "code\game\objects\items\weapons\explosives.dm"
#include "code\game\objects\items\weapons\extinguisher.dm"
#include "code\game\objects\items\weapons\flamethrower.dm"
@@ -684,6 +687,7 @@
#include "code\game\objects\items\weapons\grenades\grenade.dm"
#include "code\game\objects\items\weapons\grenades\smokebomb.dm"
#include "code\game\objects\items\weapons\grenades\spawnergrenade.dm"
+#include "code\game\objects\items\weapons\grenades\syndieminibomb.dm"
#include "code\game\objects\items\weapons\implants\deadman.dm"
#include "code\game\objects\items\weapons\implants\implant.dm"
#include "code\game\objects\items\weapons\implants\implantcase.dm"
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index 4bb61e2c9bc..2c9b9142f56 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -467,6 +467,21 @@ var/list/uplink_items = list()
item = /obj/item/device/radio/beacon/syndicate
cost = 7
+/datum/uplink_item/device_tools/syndicate_bomb
+ name = "Syndicate Bomb"
+ desc = "The Syndicate Bomb has an adjustable timer with a minimum setting of 60 seconds. Ordering the bomb sends you a small beacon, which will teleport the explosive to your location when you activate it. \
+ You can wrench the bomb down to prevent removal. The crew may attempt to defuse the bomb."
+ item = /obj/item/device/radio/beacon/syndicate/bomb
+ cost = 6
+
+/datum/uplink_item/device_tools/syndicate_detonator
+ name = "Syndicate Detonator"
+ desc = "The Syndicate Detonator is a companion device to the Syndicate Bomb. Simply press the included button and an encrypted radio frequency will instruct all live syndicate bombs to detonate. \
+ Useful for when speed matters or you wish to synchronize multiple bomb blasts. Be sure to stand clear of the blast radius before using the detonator."
+ item = /obj/item/device/syndicatedetonator
+ cost = 1
+ gamemodes = list("nuclear emergency")
+
/datum/uplink_item/device_tools/pdapinpointer
name = "PDA Pinpointer"
desc = "A pinpointer that tracks any PDA on the station. Useful for locating assassination targets or other high-value targets that you can't find. WARNING: Can only set once."
diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm
index 8c60b0e4546..af084ee00f9 100644
--- a/code/datums/wires/syndicatebomb.dm
+++ b/code/datums/wires/syndicatebomb.dm
@@ -9,24 +9,29 @@ var/const/WIRE_DELAY = 4 // Raises the timer on pulse, does nothing on cut
var/const/WIRE_PROCEED = 8 // Lowers the timer, explodes if cut while the bomb is active
var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut
+
+/datum/wires/syndicatebomb/CanUse(var/mob/living/L)
+ var/obj/machinery/syndicatebomb/P = holder
+ if(P.open_panel)
+ return 1
+ return 0
+
/datum/wires/syndicatebomb/UpdatePulsed(var/index)
var/obj/machinery/syndicatebomb/P = holder
- if(P.degutted)
- return
switch(index)
if(WIRE_BOOM)
if (P.active)
- P.loc.visible_message("\red \icon[holder] An alarm sounds! It's go-")
+ P.loc.visible_message("\icon[holder] An alarm sounds! It's go-")
P.timer = 0
if(WIRE_UNBOLT)
- P.loc.visible_message("\blue \icon[holder] The bolts spin in place for a moment.")
+ P.loc.visible_message("\icon[holder] The bolts spin in place for a moment.")
if(WIRE_DELAY)
playsound(P.loc, 'sound/machines/chime.ogg', 30, 1)
- P.loc.visible_message("\blue \icon[holder] The bomb chirps.")
+ P.loc.visible_message("\icon[holder] The bomb chirps.")
P.timer += 10
if(WIRE_PROCEED)
playsound(P.loc, 'sound/machines/buzz-sigh.ogg', 30, 1)
- P.loc.visible_message("\red \icon[holder] The bomb buzzes ominously!")
+ P.loc.visible_message("\icon[holder] The bomb buzzes ominously!")
if (P.timer >= 61) //Long fuse bombs can suddenly become more dangerous if you tinker with them
P.timer = 60
if (P.timer >= 21)
@@ -36,26 +41,20 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if
if(WIRE_ACTIVATE)
if(!P.active && !P.defused)
playsound(P.loc, 'sound/machines/click.ogg', 30, 1)
- P.loc.visible_message("\red \icon[holder] You hear the bomb start ticking!")
+ P.loc.visible_message("\icon[holder] You hear the bomb start ticking!")
P.active = 1
- if(!P.open_panel) //Needs to exist in case the wire is pulsed with a signaler while the panel is closed
- P.icon_state = "syndicate-bomb-active"
- else
- P.icon_state = "syndicate-bomb-active-wires"
- processing_objects.Add(P)
+ P.icon_state = "[initial(P.icon_state)]-active[P.open_panel ? "-wires" : ""]"
else
- P.loc.visible_message("\blue \icon[holder] The bomb seems to hesitate for a moment.")
+ P.loc.visible_message("\icon[holder] The bomb seems to hesitate for a moment.")
P.timer += 5
/datum/wires/syndicatebomb/UpdateCut(var/index, var/mended)
var/obj/machinery/syndicatebomb/P = holder
- if(P.degutted)
- return
switch(index)
if(WIRE_EXPLODE)
if(!mended)
if(P.active)
- P.loc.visible_message("\red \icon[holder] An alarm sounds! It's go-")
+ P.loc.visible_message("\icon[holder] An alarm sounds! It's go-")
P.timer = 0
else
P.defused = 1
@@ -64,15 +63,15 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if
if(WIRE_UNBOLT)
if (!mended && P.anchored)
playsound(P.loc, 'sound/effects/stealthoff.ogg', 30, 1)
- P.loc.visible_message("\blue \icon[holder] The bolts lift out of the ground!")
+ P.loc.visible_message("\icon[holder] The bolts lift out of the ground!")
P.anchored = 0
if(WIRE_PROCEED)
if(!mended && P.active)
- P.loc.visible_message("\red \icon[holder] An alarm sounds! It's go-")
+ P.loc.visible_message("\icon[holder] An alarm sounds! It's go-")
P.timer = 0
if(WIRE_ACTIVATE)
if (!mended && P.active)
- P.loc.visible_message("\blue \icon[holder] The timer stops! The bomb has been defused!")
- P.icon_state = "syndicate-bomb-inactive-wires" //no cutting possible with the panel closed
+ P.loc.visible_message("\icon[holder] The timer stops! The bomb has been defused!")
+ P.icon_state = "[initial(P.icon_state)]-inactive[P.open_panel ? "-wires" : ""]"
P.active = 0
P.defused = 1
\ No newline at end of file
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index 3f3c67b8245..c3d07013d14 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -278,4 +278,12 @@ var/const/POWER = 8
/datum/wires/proc/IsAllCut()
if(wires_status == (1 << wire_count) - 1)
return 1
- return 0
\ No newline at end of file
+ return 0
+
+//
+//Shuffle and Mend
+//
+
+/datum/wires/proc/Shuffle()
+ wires_status = 0
+ GenerateWires()
\ No newline at end of file
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
new file mode 100644
index 00000000000..5fd683fffb5
--- /dev/null
+++ b/code/game/machinery/syndicatebomb.dm
@@ -0,0 +1,307 @@
+/obj/machinery/syndicatebomb
+ icon = 'icons/obj/assemblies.dmi'
+ name = "syndicate bomb"
+ icon_state = "syndicate-bomb"
+ desc = "A large and menacing device. Can be bolted down with a wrench."
+
+ anchored = 0
+ density = 0
+ layer = MOB_LAYER - 0.1 //so people can't hide it and it's REALLY OBVIOUS
+ unacidable = 1
+
+ var/datum/wires/syndicatebomb/wires = null
+ var/timer = 60
+ var/open_panel = 0 //are the wires exposed?
+ var/active = 0 //is the bomb counting down?
+ var/defused = 0 //is the bomb capable of exploding?
+ var/obj/item/weapon/bombcore/payload = /obj/item/weapon/bombcore/
+ var/beepsound = 'sound/items/timer.ogg'
+
+/obj/machinery/syndicatebomb/process()
+ if(active && !defused && (timer > 0)) //Tick Tock
+ var/volume = (timer <= 10 ? 30 : 5) // Tick louder when the bomb is closer to being detonated.
+ playsound(loc, beepsound, volume, 0)
+ timer--
+ if(active && !defused && (timer <= 0)) //Boom
+ active = 0
+ timer = 60
+ update_icon()
+ if(payload in src)
+ payload.detonate()
+ return
+ if(!active || defused) //Counter terrorists win
+ if(defused && payload in src)
+ payload.defuse()
+ return
+
+/obj/machinery/syndicatebomb/New()
+ wires = new(src)
+ payload = new payload(src)
+ update_icon()
+ ..()
+
+
+/obj/machinery/syndicatebomb/examine()
+ ..()
+ usr << "A digital display on it reads \"[timer]\"."
+
+/obj/machinery/syndicatebomb/update_icon()
+ icon_state = "[initial(icon_state)][active ? "-active" : "-inactive"][open_panel ? "-wires" : ""]"
+
+/obj/machinery/syndicatebomb/attackby(var/obj/item/I, var/mob/user)
+ if(istype(I, /obj/item/weapon/wrench))
+ if(!anchored)
+ if(!isturf(src.loc) || istype(src.loc, /turf/space))
+ user << "The bomb must be placed on solid ground to attach it"
+ else
+ user << "You firmly wrench the bomb to the floor"
+ playsound(loc, 'sound/items/ratchet.ogg', 50, 1)
+ anchored = 1
+ if(active)
+ user << "The bolts lock in place"
+ else
+ if(!active)
+ user << "You wrench the bomb from the floor"
+ playsound(loc, 'sound/items/ratchet.ogg', 50, 1)
+ anchored = 0
+ else
+ user << "The bolts are locked down!"
+
+ else if(istype(I, /obj/item/weapon/screwdriver))
+ open_panel = !open_panel
+ update_icon()
+ user << "You [open_panel ? "open" : "close"] the wire panel."
+
+ else if(istype(I, /obj/item/weapon/wirecutters) || istype(I, /obj/item/device/multitool) || istype(I, /obj/item/device/assembly/signaler ))
+ if(open_panel)
+ wires.Interact(user)
+
+ else if(istype(I, /obj/item/weapon/crowbar))
+ if(open_panel && isWireCut(WIRE_BOOM) && isWireCut(WIRE_UNBOLT) && isWireCut(WIRE_DELAY) && isWireCut(WIRE_PROCEED) && isWireCut(WIRE_ACTIVATE))
+ if(payload)
+ user << "You carefully pry out [payload]."
+ payload.loc = user.loc
+ payload = null
+ else
+ user << "There isn't anything in here to remove!"
+ else if (open_panel)
+ user << "The wires conneting the shell to the explosives are holding it down!"
+ else
+ user << "The cover is screwed on, it won't pry off!"
+ else if(istype(I, /obj/item/weapon/bombcore))
+ if(!payload)
+ payload = I
+ user << "You place [payload] into [src]."
+ user.drop_item()
+ payload.loc = src
+ else
+ user << "[payload] is already loaded into [src], you'll have to remove it first."
+ else
+ ..()
+
+/obj/machinery/syndicatebomb/attack_hand(var/mob/user)
+ interact(user)
+
+/obj/machinery/syndicatebomb/attack_ai()
+ return
+
+/obj/machinery/syndicatebomb/interact(var/mob/user)
+ if(wires)
+ wires.Interact(user)
+ if(!open_panel)
+ if(!active)
+ settings(user)
+ return
+ else if(anchored)
+ user << "The bomb is bolted to the floor!"
+ return
+
+/obj/machinery/syndicatebomb/proc/settings(var/mob/user)
+ var/newtime = input(user, "Please set the timer.", "Timer", "[timer]") as num
+ newtime = Clamp(newtime, 60, 60000)
+ if(in_range(src, user) && isliving(user)) //No running off and setting bombs from across the station
+ timer = newtime
+ src.loc.visible_message("\icon[src] timer set for [timer] seconds.")
+ if(alert(user,"Would you like to start the countdown now?",,"Yes","No") == "Yes" && in_range(src, user) && isliving(user))
+ if(defused || active)
+ if(defused)
+ src.loc.visible_message("\icon[src] Device error: User intervention required.")
+ return
+ else
+ src.loc.visible_message("\icon[src] [timer] seconds until detonation, please clear the area.")
+ playsound(loc, 'sound/machines/click.ogg', 30, 1)
+ active = 1
+ update_icon()
+ add_fingerprint(user)
+
+ var/turf/bombturf = get_turf(src)
+ var/area/A = get_area(bombturf)
+ if(payload && !istype(payload, /obj/item/weapon/bombcore/training))
+ message_admins("[key_name(user)]? has primed a [name] ([payload]) for detonation at [A.name] (JMP).")
+ log_game("[key_name(user)] has primed a [name] ([payload]) for detonation at [A.name]([bombturf.x],[bombturf.y],[bombturf.z])")
+ payload.adminlog = "The [src.name] that [key_name(user)] had primed detonated!"
+
+/obj/machinery/syndicatebomb/proc/isWireCut(var/index)
+ return wires.IsIndexCut(index)
+
+///Bomb Subtypes///
+
+/obj/machinery/syndicatebomb/training
+ name = "training bomb"
+ icon_state = "training-bomb"
+ desc = "A salvaged syndicate device gutted of its explosives to be used as a training aid for aspiring bomb defusers."
+ payload = /obj/item/weapon/bombcore/training/
+
+/obj/machinery/syndicatebomb/badmin
+ name = "generic summoning badmin bomb"
+ desc = "Oh god what is in this thing?"
+ payload = /obj/item/weapon/bombcore/badmin/summon/
+
+/obj/machinery/syndicatebomb/badmin/clown
+ name = "clown bomb"
+ icon_state = "clown-bomb"
+ desc = "HONK."
+ payload = /obj/item/weapon/bombcore/badmin/summon/clown
+ beepsound = 'sound/items/bikehorn.ogg'
+
+/obj/machinery/syndicatebomb/badmin/varplosion
+ payload = /obj/item/weapon/bombcore/badmin/explosion/
+
+///Bomb Cores///
+
+/obj/item/weapon/bombcore
+ name = "bomb payload"
+ desc = "A powerful secondary explosive of syndicate design and unknown composition, it should be stable under normal conditions..."
+ icon = 'icons/obj/assemblies.dmi'
+ icon_state = "bombcore"
+ item_state = "eshield0"
+ w_class = 3.0
+ origin_tech = "syndicate=6;combat=5"
+ var/adminlog = null
+
+/obj/item/weapon/bombcore/ex_act(severity) //Little boom can chain a big boom
+ src.detonate()
+
+/obj/item/weapon/bombcore/proc/detonate()
+ if(adminlog)
+ message_admins(adminlog)
+ log_game(adminlog)
+ explosion(get_turf(src),2,5,11)
+ del(src)
+
+/obj/item/weapon/bombcore/proc/defuse()
+//Note: Because of how var/defused is used you shouldn't override this UNLESS you intend to set the var to 0 or
+// otherwise remove the core/reset the wires before the end of defuse(). It will repeatedly be called otherwise.
+
+///Bomb Core Subtypes///
+
+/obj/item/weapon/bombcore/training
+ name = "dummy payload"
+ desc = "A nanotrasen replica of a syndicate payload. Its not intended to explode but to announce that it WOULD have exploded, then rewire itself to allow for more training."
+ origin_tech = null
+ var/defusals = 0
+ var/attempts = 0
+
+/obj/item/weapon/bombcore/training/proc/reset()
+ var/obj/machinery/syndicatebomb/holder = src.loc
+ if(istype(holder))
+ holder.open_panel = 0
+ if(holder.wires)
+ holder.wires.Shuffle()
+ holder.defused = 0
+ holder.update_icon()
+ holder.updateDialog()
+
+/obj/item/weapon/bombcore/training/detonate()
+ var/obj/machinery/syndicatebomb/holder = src.loc
+ if(istype(holder))
+ attempts++
+ holder.loc.visible_message("\icon[holder] Alert: Bomb has detonated. Your score is now [defusals] for [attempts]. Resetting wires...")
+ reset()
+ else
+ del(src)
+
+/obj/item/weapon/bombcore/training/defuse()
+ var/obj/machinery/syndicatebomb/holder = src.loc
+ if(istype(holder))
+ attempts++
+ defusals++
+ holder.loc.visible_message("\icon[holder] Alert: Bomb has been defused. Your score is now [defusals] for [attempts]! Resetting wires...")
+ reset()
+
+/obj/item/weapon/bombcore/badmin
+ name = "badmin payload"
+ desc = "If you're seeing this someone has either made a mistake or gotten dangerously savvy with var editing!"
+ origin_tech = null
+
+/obj/item/weapon/bombcore/badmin/defuse() //because we wouldn't want them being harvested by players
+ var/obj/machinery/syndicatebomb/B = src.loc
+ del(B)
+ del(src)
+
+/obj/item/weapon/bombcore/badmin/summon/
+ var/summon_path = /obj/item/weapon/reagent_containers/food/snacks/cookie
+ var/amt_summon = 1
+
+/obj/item/weapon/bombcore/badmin/summon/detonate()
+ var/obj/machinery/syndicatebomb/B = src.loc
+ for(var/i = 0; i < amt_summon; i++)
+ var/atom/movable/X = new summon_path
+ X.loc = get_turf(src)
+ if(prob(50))
+ for(var/j = 1, j <= rand(1, 3), j++)
+ step(X, pick(NORTH,SOUTH,EAST,WEST))
+ del(B)
+ del(src)
+
+/obj/item/weapon/bombcore/badmin/summon/clown
+ summon_path = /mob/living/simple_animal/hostile/retaliate/clown
+ amt_summon = 100
+
+/obj/item/weapon/bombcore/badmin/summon/clown/defuse()
+ playsound(src.loc, 'sound/misc/sadtrombone.ogg', 50)
+ ..()
+
+/obj/item/weapon/bombcore/badmin/explosion/
+ var/HeavyExplosion = 2
+ var/MediumExplosion = 5
+ var/LightExplosion = 11
+
+/obj/item/weapon/bombcore/badmin/explosion/detonate()
+ explosion(get_turf(src),HeavyExplosion,MediumExplosion,LightExplosion)
+
+///Syndicate Detonator (aka the big red button)///
+
+/obj/item/device/syndicatedetonator
+ name = "big red button"
+ desc = "Nothing good can come of pressing a button this garish..."
+ icon = 'icons/obj/assemblies.dmi'
+ icon_state = "bigred"
+ item_state = "electronic"
+ w_class = 1.0
+ origin_tech = "syndicate=2"
+ var/cooldown = 0
+ var/detonated = 0
+ var/existant = 0
+
+/obj/item/device/syndicatedetonator/attack_self(mob/user as mob)
+ if(!cooldown)
+ for(var/obj/machinery/syndicatebomb/B in machines)
+ if(B.active)
+ B.timer = 0
+ detonated++
+ existant++
+ playsound(user, 'sound/machines/click.ogg', 20, 1)
+ user << "[existant] found, [detonated] triggered."
+ if(detonated)
+ var/turf/T = get_turf(src)
+ var/area/A = get_area(T)
+ detonated--
+ var/log_str = "[key_name(user)]? has remotely detonated [detonated ? "syndicate bombs" : "a syndicate bomb"] using a [name] at [A.name] (JMP)."
+ bombers += log_str
+ message_admins(log_str)
+ log_game("[key_name(user)] has remotely detonated [detonated ? "syndicate bombs" : "a syndicate bomb"] using a [name] at [A.name]([T.x],[T.y],[T.z])")
+ detonated = 0
+ existant = 0
+ cooldown = 1
+ spawn(30) cooldown = 0
\ No newline at end of file
diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm
index 6780d0e4977..0fd6dddf69b 100644
--- a/code/game/objects/items/devices/radio/beacon.dm
+++ b/code/game/objects/items/devices/radio/beacon.dm
@@ -48,6 +48,18 @@
del(src)
return
+/obj/item/device/radio/beacon/syndicate/bomb
+ name = "suspicious beacon"
+ desc = "A label on it reads: Warning: Activating this device will send a high-ordinance explosive to your location."
+ origin_tech = "bluespace=1;syndicate=7"
+
+/obj/item/device/radio/beacon/syndicate/bomb/attack_self(mob/user as mob)
+ if(user)
+ user << "\blue Locked In"
+ new /obj/machinery/syndicatebomb( user.loc )
+ playsound(src, 'sound/effects/pop.ogg', 100, 1, 1)
+ del(src)
+ return
/obj/item/device/telepad_beacon
diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi
index 554d31c0837..060f2af9c11 100644
Binary files a/icons/obj/assemblies.dmi and b/icons/obj/assemblies.dmi differ
diff --git a/sound/items/timer.ogg b/sound/items/timer.ogg
new file mode 100644
index 00000000000..a13db015be3
Binary files /dev/null and b/sound/items/timer.ogg differ