diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm index d1fe6217058..616429b8cd8 100644 --- a/code/datums/wires/syndicatebomb.dm +++ b/code/datums/wires/syndicatebomb.dm @@ -1,5 +1,5 @@ /datum/wires/syndicatebomb - random = 1 + random = TRUE holder_type = /obj/machinery/syndicatebomb wire_count = 5 @@ -13,83 +13,90 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if switch(index) if(WIRE_BOOM) return "Explode" - + if(WIRE_UNBOLT) return "Unbolt" - + if(WIRE_DELAY) return "Delay" - + if(WIRE_PROCEED) return "Proceed" - + if(WIRE_ACTIVATE) return "Activate" /datum/wires/syndicatebomb/CanUse(mob/living/L) var/obj/machinery/syndicatebomb/P = holder if(P.open_panel) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/syndicatebomb/UpdatePulsed(index) - var/obj/machinery/syndicatebomb/P = holder + var/obj/machinery/syndicatebomb/B = holder switch(index) if(WIRE_BOOM) - if(P.active) - P.loc.visible_message("[bicon(holder)] An alarm sounds! It's go-") - P.timer = 0 + if(B.active) + holder.visible_message("[bicon(B)] An alarm sounds! It's go-") + B.explode_now = TRUE if(WIRE_UNBOLT) - P.loc.visible_message("[bicon(holder)] The bolts spin in place for a moment.") + holder.visible_message("[bicon(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("[bicon(holder)] The bomb chirps.") - P.timer += 10 - if(WIRE_PROCEED) - playsound(P.loc, 'sound/machines/buzz-sigh.ogg', 30, 1) - P.loc.visible_message("[bicon(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) - P.timer -= 10 - else if(P.timer >= 11) //both to prevent negative timers and to have a little mercy - P.timer = 10 - if(WIRE_ACTIVATE) - if(!P.active && !P.defused) - playsound(P.loc, 'sound/machines/click.ogg', 30, 1) - P.loc.visible_message("[bicon(holder)] You hear the bomb start ticking!") - P.active = 1 - P.icon_state = "[initial(P.icon_state)]-active[P.open_panel ? "-wires" : ""]" + if(B.delayedbig) + holder.visible_message("[bicon(B)] The bomb has already been delayed.") else - P.loc.visible_message("[bicon(holder)] The bomb seems to hesitate for a moment.") - P.timer += 5 + holder.visible_message("[bicon(B)] The bomb chirps.") + playsound(B, 'sound/machines/chime.ogg', 30, 1) + B.detonation_timer += 300 + B.delayedbig = TRUE + if(WIRE_PROCEED) + holder.visible_message("[bicon(B)] The bomb buzzes ominously!") + playsound(B, 'sound/machines/buzz-sigh.ogg', 30, 1) + var/seconds = B.seconds_remaining() + if(seconds >= 61) // Long fuse bombs can suddenly become more dangerous if you tinker with them. + B.detonation_timer = world.time + 600 + else if(seconds >= 21) + B.detonation_timer -= 100 + else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy. + B.detonation_timer = world.time + 100 + if(WIRE_ACTIVATE) + if(!B.active && !B.defused) + holder.visible_message("[bicon(B)] You hear the bomb start ticking!") + B.activate() + B.update_icon() + else if(B.delayedlittle) + holder.visible_message("[bicon(B)] Nothing happens.") + else + holder.visible_message("[bicon(B)] The bomb seems to hesitate for a moment.") + B.detonation_timer += 100 + B.delayedlittle = TRUE ..() /datum/wires/syndicatebomb/UpdateCut(index, mended) - var/obj/machinery/syndicatebomb/P = holder + var/obj/machinery/syndicatebomb/B = holder switch(index) if(WIRE_EXPLODE) - if(!mended) - if(P.active) - P.loc.visible_message("[bicon(holder)] An alarm sounds! It's go-") - P.timer = 0 - else - P.defused = 1 + if(mended) + B.defused = FALSE // Cutting and mending all the wires of an inactive bomb will thus cure any sabotage. else - P.defused = 0 //cutting and mending all the wires of an inactive bomb will thus cure any sabotage + if(B.active) + holder.visible_message("[bicon(B)] An alarm sounds! It's go-") + B.explode_now = TRUE + else + B.defused = TRUE if(WIRE_UNBOLT) - if(!mended && P.anchored) - playsound(P.loc, 'sound/effects/stealthoff.ogg', 30, 1) - P.loc.visible_message("[bicon(holder)] The bolts lift out of the ground!") - P.anchored = 0 + if(!mended && B.anchored) + holder.visible_message("[bicon(B)] The bolts lift out of the ground!") + playsound(B, 'sound/effects/stealthoff.ogg', 30, 1) + B.anchored = FALSE if(WIRE_PROCEED) - if(!mended && P.active) - P.loc.visible_message("[bicon(holder)] An alarm sounds! It's go-") - P.timer = 0 + if(!mended && B.active) + holder.visible_message("[bicon(B)] An alarm sounds! It's go-") + B.explode_now = TRUE if(WIRE_ACTIVATE) - if(!mended && P.active) - P.loc.visible_message("[bicon(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 + if(!mended && B.active) + holder.visible_message("[bicon(B)] The timer stops! The bomb has been defused!") + B.active = FALSE + B.defused = TRUE + B.update_icon() ..() \ No newline at end of file diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index 10f6fc1c8c5..ba3ed604e00 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -1,3 +1,6 @@ +#define BUTTON_COOLDOWN 60 // cant delay the bomb forever +#define BUTTON_DELAY 50 //five seconds + /obj/machinery/syndicatebomb icon = 'icons/obj/assemblies.dmi' name = "syndicate bomb" @@ -6,71 +9,119 @@ anchored = 0 density = 0 - layer = MOB_LAYER - 0.1 //so people can't hide it and it's REALLY OBVIOUS + layer = BELOW_MOB_LAYER //so people can't hide it and it's REALLY OBVIOUS unacidable = 1 var/datum/wires/syndicatebomb/wires = null - var/timer = 120 - 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/minimum_timer = 90 + var/timer_set = 90 + var/maximum_timer = 60000 + + var/can_unanchor = TRUE + + var/open_panel = FALSE //are the wires exposed? + var/active = FALSE //is the bomb counting down? + var/defused = FALSE //is the bomb capable of exploding? + var/obj/item/weapon/bombcore/payload = /obj/item/weapon/bombcore var/beepsound = 'sound/items/timer.ogg' + var/delayedbig = FALSE //delay wire pulsed? + var/delayedlittle = FALSE //activation wire pulsed? + var/obj/effect/countdown/syndicatebomb/countdown + + var/next_beep + var/detonation_timer + var/explode_now = FALSE + +/obj/machinery/syndicatebomb/proc/try_detonate(ignore_active = FALSE) + . = (payload in src) && (active || ignore_active) && !defused + if(.) + payload.detonate() /obj/machinery/syndicatebomb/process() - if(active && !defused && (timer > 0)) //Tick Tock - var/volume = (timer <= 20 ? 40 : 10) // Tick louder when the bomb is closer to being detonated. - playsound(loc, beepsound, volume, 0) - timer = max(timer - 2,0) // 2 seconds per process() - if(active && !defused && (timer <= 0)) //Boom - active = 0 - timer = 120 - update_icon() - if(payload in src) - payload.detonate() + if(!active) + fast_processing -= src + detonation_timer = null + next_beep = null + countdown.stop() return - if(!active || defused) //Counter terrorists win + + if(!isnull(next_beep) && (next_beep <= world.time)) + var/volume + switch(seconds_remaining()) + if(0 to 5) + volume = 50 + if(5 to 10) + volume = 40 + if(10 to 15) + volume = 30 + if(15 to 20) + volume = 20 + if(20 to 25) + volume = 10 + else + volume = 5 + playsound(loc, beepsound, volume, 0) + next_beep = world.time + 10 + + if(active && !defused && ((detonation_timer <= world.time) || explode_now)) + active = FALSE + timer_set = initial(timer_set) + update_icon() + try_detonate(TRUE) + //Counter terrorists win + else if(!active || defused) if(defused && payload in src) payload.defuse() - return + countdown.stop() + fast_processing -= src /obj/machinery/syndicatebomb/New() wires = new(src) - payload = new payload(src) + if(payload) + payload = new payload(src) update_icon() + countdown = new(src) ..() /obj/machinery/syndicatebomb/Destroy() QDEL_NULL(wires) + QDEL_NULL(countdown) + fast_processing -= src return ..() /obj/machinery/syndicatebomb/examine(mob/user) ..(user) - to_chat(user, "A digital display on it reads \"[timer]\".") + to_chat(user, "A digital display on it reads \"[seconds_remaining()]\".") /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, params) - if(istype(I, /obj/item/weapon/wrench)) +/obj/machinery/syndicatebomb/proc/seconds_remaining() + if(active) + . = max(0, round((detonation_timer - world.time) / 10)) + else + . = timer_set + +/obj/machinery/syndicatebomb/attackby(obj/item/I, mob/user, params) + if(iswrench(I) && can_unanchor) if(!anchored) - if(!isturf(src.loc) || istype(src.loc, /turf/space)) - to_chat(user, "The bomb must be placed on solid ground to attach it") + if(!isturf(loc) || isspaceturf(loc)) + to_chat(user, "The bomb must be placed on solid ground to attach it.") else - to_chat(user, "You firmly wrench the bomb to the floor") + to_chat(user, "You firmly wrench the bomb to the floor.") playsound(loc, I.usesound, 50, 1) anchored = 1 if(active) - to_chat(user, "The bolts lock in place") + to_chat(user, "The bolts lock in place.") else if(!active) - to_chat(user, "You wrench the bomb from the floor") + to_chat(user, "You wrench the bomb from the floor.") playsound(loc, I.usesound, 50, 1) anchored = 0 else to_chat(user, "The bolts are locked down!") - else if(istype(I, /obj/item/weapon/screwdriver)) + else if(isscrewdriver(I)) open_panel = !open_panel update_icon() to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") @@ -79,28 +130,47 @@ 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)) + else if(iscrowbar(I)) + if(open_panel && wires.IsAllCut()) if(payload) to_chat(user, "You carefully pry out [payload].") payload.loc = user.loc payload = null else - to_chat(user, "There isn't anything in here to remove!") + to_chat(user, "There isn't anything in here to remove!") else if(open_panel) - to_chat(user, "The wires connecting the shell to the explosives are holding it down!") + to_chat(user, "The wires connecting the shell to the explosives are holding it down!") else - to_chat(user, "The cover is screwed on, it won't pry off!") + to_chat(user, "The cover is screwed on, it won't pry off!") else if(istype(I, /obj/item/weapon/bombcore)) if(!payload) + if(!user.drop_item()) + return payload = I to_chat(user, "You place [payload] into [src].") - user.drop_item() - payload.loc = src + payload.forceMove(src) else to_chat(user, "[payload] is already loaded into [src], you'll have to remove it first.") + else if(iswelder(I)) + if(payload || !wires.IsAllCut() || !open_panel) + return + var/obj/item/weapon/weldingtool/WT = I + if(!WT.isOn()) + return + if(WT.get_fuel() < 5) //uses up 5 fuel. + to_chat(user, "You need more fuel to complete this task!") + return + + playsound(loc, WT.usesound, 50, 1) + to_chat(user, "You start to cut the [src] apart...") + if(do_after(user, 20*I.toolspeed, target = src)) + if(!WT.isOn() || !WT.remove_fuel(5, user)) + return + to_chat(user, "You cut the [src] apart.") + new /obj/item/stack/sheet/plasteel(loc, 5) + qdel(src) else - ..() + return ..() /obj/machinery/syndicatebomb/attack_ghost(mob/user) interact(user) @@ -133,21 +203,27 @@ return FALSE return TRUE +/obj/machinery/syndicatebomb/proc/activate() + active = TRUE + fast_processing += src + countdown.start() + next_beep = world.time + 10 + detonation_timer = world.time + (timer_set * 10) + playsound(loc, 'sound/machines/click.ogg', 30, 1) + /obj/machinery/syndicatebomb/proc/settings(mob/user) - var/newtime = input(user, "Please set the timer.", "Timer", "[timer]") as num - newtime = Clamp(newtime, 120, 60000) + var/new_timer = input(user, "Please set the timer.", "Timer", "[timer_set]") as num if(can_interact(user)) //No running off and setting bombs from across the station - timer = newtime - loc.visible_message("[bicon(src)] timer set for [timer] seconds.") + timer_set = Clamp(new_timer, minimum_timer, maximum_timer) + loc.visible_message("[bicon(src)] timer set for [timer_set] seconds.") if(alert(user,"Would you like to start the countdown now?",,"Yes","No") == "Yes" && can_interact(user)) if(defused || active) if(defused) loc.visible_message("[bicon(src)] Device error: User intervention required.") return else - loc.visible_message("[bicon(src)] [timer] seconds until detonation, please clear the area.") - playsound(loc, 'sound/machines/click.ogg', 30, 1) - active = 1 + loc.visible_message("[bicon(src)] [timer_set] seconds until detonation, please clear the area.") + activate() update_icon() add_fingerprint(user) @@ -167,12 +243,12 @@ 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/ + 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/ + payload = /obj/item/weapon/bombcore/badmin/summon /obj/machinery/syndicatebomb/badmin/clown name = "clown bomb" @@ -181,8 +257,23 @@ 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/ +/obj/machinery/syndicatebomb/empty + name = "bomb" + icon_state = "base-bomb" + desc = "An ominous looking device designed to detonate an explosive payload. Can be bolted down using a wrench." + payload = null + open_panel = TRUE + timer_set = 120 + +/obj/machinery/syndicatebomb/empty/New() + ..() + wires.CutAll() + +/obj/machinery/syndicatebomb/self_destruct + name = "self destruct device" + desc = "Do not taunt. Warranty invalid if exposed to high temperature. Not suitable for agents under 3 years of age." + payload = /obj/item/weapon/bombcore/large + can_unanchor = FALSE ///Bomb Cores/// @@ -196,17 +287,26 @@ origin_tech = "syndicate=5;combat=6" burn_state = FLAMMABLE //Burnable (but the casing isn't) var/adminlog = null + var/range_heavy = 3 + var/range_medium = 9 + var/range_light = 17 + var/range_flame = 17 /obj/item/weapon/bombcore/ex_act(severity) //Little boom can chain a big boom - src.detonate() + detonate() + + +/obj/item/weapon/bombcore/burn() + detonate() + ..() /obj/item/weapon/bombcore/proc/detonate() if(adminlog) message_admins(adminlog) log_game(adminlog) - explosion(get_turf(src),3,9,17, flame_range = 17) - if(src.loc && istype(src.loc,/obj/machinery/syndicatebomb/)) - qdel(src.loc) + explosion(get_turf(src), range_heavy, range_medium, range_light, flame_range = range_flame) + if(loc && istype(loc,/obj/machinery/syndicatebomb/)) + qdel(loc) qdel(src) /obj/item/weapon/bombcore/proc/defuse() @@ -223,17 +323,20 @@ var/attempts = 0 /obj/item/weapon/bombcore/training/proc/reset() - var/obj/machinery/syndicatebomb/holder = src.loc + var/obj/machinery/syndicatebomb/holder = loc if(istype(holder)) if(holder.wires) holder.wires.Shuffle() holder.defused = 0 holder.open_panel = 0 + holder.delayedbig = FALSE + holder.delayedlittle = FALSE + holder.explode_now = FALSE holder.update_icon() holder.updateDialog() /obj/item/weapon/bombcore/training/detonate() - var/obj/machinery/syndicatebomb/holder = src.loc + var/obj/machinery/syndicatebomb/holder = loc if(istype(holder)) attempts++ holder.loc.visible_message("[bicon(holder)] Alert: Bomb has detonated. Your score is now [defusals] for [attempts]. Resetting wires...") @@ -242,7 +345,7 @@ qdel(src) /obj/item/weapon/bombcore/training/defuse() - var/obj/machinery/syndicatebomb/holder = src.loc + var/obj/machinery/syndicatebomb/holder = loc if(istype(holder)) attempts++ defusals++ @@ -257,11 +360,11 @@ 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 + var/obj/machinery/syndicatebomb/B = loc qdel(B) qdel(src) -/obj/item/weapon/bombcore/badmin/summon/ +/obj/item/weapon/bombcore/badmin/summon var/summon_path = /obj/item/weapon/reagent_containers/food/snacks/cookie var/amt_summon = 1 @@ -285,42 +388,43 @@ playsound(src.loc, 'sound/misc/sadtrombone.ogg', 50) ..() -/obj/item/weapon/bombcore/badmin/explosion/ - var/HeavyExplosion = 2 - var/MediumExplosion = 5 - var/LightExplosion = 11 - var/Flames = 11 +/obj/item/weapon/bombcore/large + name = "large bomb payload" + range_heavy = 5 + range_medium = 10 + range_light = 20 + range_flame = 20 -/obj/item/weapon/bombcore/badmin/explosion/detonate() - explosion(get_turf(src),HeavyExplosion,MediumExplosion,LightExplosion, flame_range = Flames) +/obj/item/weapon/bombcore/large/underwall + layer = ABOVE_OPEN_TURF_LAYER /obj/item/weapon/bombcore/miniature name = "small bomb core" w_class = WEIGHT_CLASS_SMALL - -/obj/item/weapon/bombcore/miniature/detonate() - explosion(src.loc,1,2,4,flame_range = 2) //Identical to a minibomb - qdel(src) + range_heavy = 1 + range_medium = 2 + range_light = 4 + range_flame = 2 ///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..." + desc = "Your standard issue bomb synchronizing button. Five second safety delay to prevent 'accidents'" icon = 'icons/obj/assemblies.dmi' icon_state = "bigred" item_state = "electronic" w_class = WEIGHT_CLASS_TINY origin_tech = "syndicate=3" - var/cooldown = 0 + var/timer = 0 var/detonated = 0 var/existant = 0 -/obj/item/device/syndicatedetonator/attack_self(mob/user as mob) - if(!cooldown) +/obj/item/device/syndicatedetonator/attack_self(mob/user) + if(timer < world.time) for(var/obj/machinery/syndicatebomb/B in machines) if(B.active) - B.timer = 0 + B.detonation_timer = world.time + BUTTON_DELAY detonated++ existant++ playsound(user, 'sound/machines/click.ogg', 20, 1) @@ -334,5 +438,7 @@ 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 + timer = world.time + BUTTON_COOLDOWN + +#undef BUTTON_COOLDOWN +#undef BUTTON_DELAY \ No newline at end of file diff --git a/code/modules/countdown/countdown.dm b/code/modules/countdown/countdown.dm index 2ef85d6a9d7..7e651fb910f 100644 --- a/code/modules/countdown/countdown.dm +++ b/code/modules/countdown/countdown.dm @@ -9,15 +9,19 @@ var/atom/attached_to color = "#ff0000" var/text_size = 4 - var/started = 0 + var/started = FALSE invisibility = INVISIBILITY_OBSERVER - anchored = 1 - layer = 5 + anchored = TRUE + layer = GHOST_LAYER /obj/effect/countdown/New(atom/A) . = ..() attach(A) +/obj/effect/countdown/examine(mob/user) + . = ..() + to_chat(user, "This countdown is displaying: [displayed_text]") + /obj/effect/countdown/proc/attach(atom/A) attached_to = A loc = get_turf(A) @@ -25,13 +29,13 @@ /obj/effect/countdown/proc/start() if(!started) fast_processing += src - started = 1 + started = TRUE /obj/effect/countdown/proc/stop() if(started) maptext = null fast_processing -= src - started = 0 + started = FALSE /obj/effect/countdown/proc/get_value() // Get the value from our atom @@ -56,6 +60,19 @@ fast_processing -= src return ..() +/obj/effect/countdown/ex_act(severity) //immune to explosions + return + +/obj/effect/countdown/syndicatebomb + name = "syndicate bomb countdown" + +/obj/effect/countdown/syndicatebomb/get_value() + var/obj/machinery/syndicatebomb/S = attached_to + if(!istype(S)) + return + else if(S.active) + return S.seconds_remaining() + /obj/effect/countdown/clonepod name = "cloning pod countdown" text_size = 1 diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi index 2fd457f5638..62a1c314388 100644 Binary files a/icons/obj/assemblies.dmi and b/icons/obj/assemblies.dmi differ