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