diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index c18be89d9f9..685bc3c6b00 100644
--- a/code/game/machinery/syndicatebomb.dm
+++ b/code/game/machinery/syndicatebomb.dm
@@ -1,3 +1,4 @@
+
/obj/machinery/syndicatebomb
icon = 'icons/obj/assemblies.dmi'
name = "syndicate bomb"
@@ -9,7 +10,7 @@
layer = MOB_LAYER - 0.2 //so people can't hide it and it's REALLY OBVIOUS
unacidable = 1
- var/timer = 60
+ var/timer = 120
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?
@@ -35,7 +36,8 @@
/obj/machinery/syndicatebomb/New()
wires = new /datum/wires/syndicatebomb(src)
- payload = new payload(src)
+ if(src.payload)
+ payload = new payload(src)
update_icon()
..()
@@ -44,9 +46,21 @@
wires = null
return ..()
+/obj/machinery/syndicatebomb/emag_act(mob/user)
+ if(active && !emagged)
+ emagged = 1
+ timer = 60 // An emag can be used to reduce the timer, or give you more time, depending on when you use it.
+ if(user)
+ user.visible_message("Sparks fly out of the [src]!",
+ "You emag the [src], causing the timing mechanism to malfunction.")
+ playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1)
+
/obj/machinery/syndicatebomb/examine(mob/user)
..()
- user << "A digital display on it reads \"[timer]\"."
+ if(emagged)
+ user << "A digital display on it reads \"[rand(-1, 60)]\"."
+ else
+ user << "A digital display on it reads \"[timer]\"."
/obj/machinery/syndicatebomb/update_icon()
icon_state = "[initial(icon_state)][active ? "-active" : "-inactive"][open_panel ? "-wires" : ""]"
@@ -99,6 +113,25 @@
payload.loc = src
else
user << "[payload] is already loaded into [src]! You'll have to remove it first."
+ else if(istype(I, /obj/item/weapon/weldingtool))
+ if(payload || !wires.is_all_cut() || !open_panel)
+ return
+ var/obj/item/weapon/weldingtool/WT = I
+ if(!WT.isOn())
+ return
+ if(WT.get_fuel() < 5) //uses up 5 fuel.
+ user << "You need more fuel to complete this task!"
+ return
+
+ playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
+ 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
+ user << "You cut the [src] apart."
+ new /obj/item/stack/sheet/plasteel( loc, 5)
+ qdel(src)
+ return
else
..()
@@ -120,7 +153,7 @@
/obj/machinery/syndicatebomb/proc/settings(mob/user)
var/newtime = input(user, "Please set the timer.", "Timer", "[timer]") as num
- newtime = Clamp(newtime, 60, 60000)
+ newtime = Clamp(newtime, 120, 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.")
@@ -166,6 +199,17 @@
/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
+
+/obj/machinery/syndicatebomb/empty/New()
+ ..()
+ wires.cut_all()
+
///Bomb Cores///
/obj/item/weapon/bombcore
@@ -180,11 +224,18 @@
var/adminlog = null
/obj/item/weapon/bombcore/ex_act(severity, target) // Little boom can chain a big boom.
- detonate()
+ if(prob(50))
+ detonate()
+ else
+ qdel(src)
/obj/item/weapon/bombcore/burn()
- detonate()
- ..()
+ if(prob(25))
+ detonate()
+ ..()
+ else
+ ..()
+ qdel(src)
/obj/item/weapon/bombcore/proc/detonate()
if(adminlog)
@@ -292,6 +343,127 @@
explosion(src.loc, 1, 2, 4, flame_range = 2) //Identical to a minibomb
qdel(src)
+/obj/item/weapon/bombcore/chemical
+ name = "chemical payload"
+ desc = "An explosive payload designed to spread chemicals, dangerous or otherwise, across a large area. It is able to hold up to four chemical containers, and must be loaded before use."
+ origin_tech = "combat=4;materials=3"
+ icon_state = "chemcore"
+ var/list/beakers = list()
+ var/max_beakers = 4
+ var/spread_range = 5
+ var/temp_boost = 50
+ var/time_release = 0
+
+/obj/item/weapon/bombcore/chemical/detonate()
+
+ if(time_release > 0)
+ var/total_volume = 0
+ for(var/obj/item/weapon/reagent_containers/RC in beakers)
+ total_volume += RC.reagents.total_volume
+
+ if(total_volume < time_release) // If it's empty, the detonation is complete.
+ if(loc && istype(loc,/obj/machinery/syndicatebomb/))
+ qdel(loc)
+ qdel(src)
+ return
+
+ var/fraction = time_release/total_volume
+ var/datum/reagents/reactants = new(time_release)
+ reactants.my_atom = src
+ for(var/obj/item/weapon/reagent_containers/RC in beakers)
+ RC.reagents.trans_to(reactants, RC.reagents.total_volume*fraction, 1, 1, 1)
+ chem_splash(get_turf(src), spread_range, list(reactants), temp_boost)
+
+ spawn(10)
+ detonate() // Detonate it again in one second, until it's out of juice.
+
+
+ // If it's not a time release bomb, do normal explosion
+
+ var/list/reactants = list()
+
+ for(var/obj/item/weapon/reagent_containers/glass/G in beakers)
+ reactants += G.reagents
+
+ for(var/obj/item/slime_extract/S in beakers)
+ if(S.Uses)
+ for(var/obj/item/weapon/reagent_containers/glass/G in beakers)
+ G.reagents.trans_to(S, G.reagents.total_volume)
+
+ if(S && S.reagents && S.reagents.total_volume)
+ reactants += S.reagents
+
+ if(!chem_splash(get_turf(src), spread_range, reactants, temp_boost))
+ playsound(loc, 'sound/items/Screwdriver2.ogg', 50, 1)
+ return // The Explosion didn't do anything. No need to log, or disappear.
+
+ if(adminlog)
+ message_admins(adminlog)
+ log_game(adminlog)
+
+ playsound(loc, 'sound/effects/bamf.ogg', 75, 1, 5)
+
+ if(loc && istype(loc,/obj/machinery/syndicatebomb/))
+ qdel(loc)
+ qdel(src)
+
+/obj/item/weapon/bombcore/chemical/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/weapon/crowbar) && beakers.len > 0)
+ playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
+ for (var/obj/item/B in beakers)
+ B.loc = get_turf(src)
+ beakers -= B
+ return
+ else if(istype(I, /obj/item/weapon/reagent_containers/glass/beaker) || istype(I, /obj/item/weapon/reagent_containers/glass/bottle))
+ if(beakers.len < max_beakers)
+ if(!user.drop_item())
+ return
+ beakers += I
+ user << "You load [src] with [I]."
+ I.loc = src
+ else
+ user << "The [I] wont fit! The [src] can only hold up to [max_beakers] containers."
+ return
+ ..()
+
+/obj/item/weapon/bombcore/chemical/CheckParts()
+ // Using different grenade casings, causes the payload to have different properties.
+ for(var/obj/item/weapon/grenade/chem_grenade/G in src)
+
+ if(istype(G, /obj/item/weapon/grenade/chem_grenade/large))
+ var/obj/item/weapon/grenade/chem_grenade/large/LG = G
+ max_beakers += 1 // Adding two large grenades only allows for a maximum of 6 beakers.
+ spread_range += 2 // Extra range, reduced density.
+ temp_boost += 50 // maximum of +150K blast using only large beakers. Not enough to self ignite.
+ for(var/obj/item/slime_extract/S in LG.beakers) // And slime cores.
+ if(beakers.len < max_beakers)
+ beakers += S
+ S.loc = src
+ else
+ S.loc = get_turf(src)
+
+ if(istype(G, /obj/item/weapon/grenade/chem_grenade/cryo))
+ spread_range -= 1 // Reduced range, but increased density.
+ temp_boost -= 100 // minimum of -150K blast.
+
+ if(istype(G, /obj/item/weapon/grenade/chem_grenade/pyro))
+ temp_boost += 150 // maximum of +350K blast, which is enough to self ignite. Which means a self igniting bomb can't take advantage of other grenade casing properties. Sorry?
+
+ if(istype(G, /obj/item/weapon/grenade/chem_grenade/adv_release))
+ time_release += 25 // A typical bomb, using basic beakers, will explode over 2-4 seconds. Using two will make the reaction last for less time, but it will be more dangerous overall.
+
+ for(var/obj/item/weapon/reagent_containers/glass/B in G)
+ if(beakers.len < max_beakers)
+ beakers += B
+ B.loc = src
+ else
+ B.loc = get_turf(src)
+
+ qdel(G)
+ return
+
+
+
///Syndicate Detonator (aka the big red button)///
/obj/item/device/syndicatedetonator
@@ -326,7 +498,4 @@
detonated = 0
existant = 0
cooldown = 1
- spawn(30) cooldown = 0
-
-
-
+ spawn(30) cooldown = 0
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index e8370da2e50..1dab68627f7 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -79,6 +79,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list ( \
*/
var/global/list/datum/stack_recipe/plasteel_recipes = list ( \
new/datum/stack_recipe("AI core", /obj/structure/AIcore, 4, time = 50, one_per_turf = 1), \
+ new/datum/stack_recipe("bomb assembly", /obj/machinery/syndicatebomb/empty, 10, time = 50), \
)
/obj/item/stack/sheet/plasteel
@@ -110,7 +111,6 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \
new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20), \
new/datum/stack_recipe("wood table frame", /obj/structure/table_frame/wood, 2, time = 10), \
new/datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 40), \
- new/datum/stack_recipe("rolling pin", /obj/item/weapon/kitchen/rollingpin, 2, time = 30), \
new/datum/stack_recipe("wooden chair", /obj/structure/chair/wood/normal, 3, time = 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("wooden barricade", /obj/structure/barricade/wooden, 5, time = 50, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("wooden door", /obj/structure/mineral_door/wood, 10, time = 20, one_per_turf = 1, on_floor = 1), \
@@ -223,4 +223,4 @@ var/global/list/datum/stack_recipe/runed_metal_recipes = list ( \
desc = "Rare kind of gems which are only gained by blood sacrifice to minor deities. They are needed in crafting powerful objects."
singular_name = "greater gem"
icon_state = "sheet-greatergem"
- origin_tech = "materials=8"
+ origin_tech = "materials=8"
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm
index e448fdb374b..4e83773c9c1 100644
--- a/code/game/objects/items/weapons/grenades/chem_grenade.dm
+++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm
@@ -15,6 +15,8 @@
var/affected_area = 3
var/obj/item/device/assembly_holder/nadeassembly = null
var/assemblyattacher
+ var/ignition_temp = 10 // The amount of heat added to the reagents when this grenade goes off.
+ var/threatscale = 1 // Used by advanced grenades to make them slightly more worthy.
/obj/item/weapon/grenade/chem_grenade/New()
create_reagents(1000)
@@ -153,15 +155,14 @@
nadeassembly.on_found(finder)
/obj/item/weapon/grenade/chem_grenade/prime()
- if(stage != READY || !reagents)
+ if(stage != READY)
return
- var/has_reagents
- for(var/obj/item/I in beakers)
- if(I.reagents.total_volume)
- has_reagents = 1
+ var/list/datum/reagents/reactants = list()
+ for(var/obj/item/weapon/reagent_containers/glass/G in beakers)
+ reactants += G.reagents
- if(!has_reagents)
+ if(!chem_splash(get_turf(src), affected_area, reactants, ignition_temp, threatscale))
playsound(loc, 'sound/items/Screwdriver2.ogg', 50, 1)
return
@@ -173,79 +174,30 @@
message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) (FLW) and last touched by [key_name_admin(last)](?) (FLW) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).")
log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])")
- playsound(loc, 'sound/effects/bamf.ogg', 50, 1)
-
var/turf/DT = get_turf(src)
var/area/DA = get_area(DT)
log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])")
update_mob()
- mix_reagents()
-
- if(reagents.total_volume) //The possible reactions didnt use up all reagents, so we spread it around.
- var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread()
- steam.set_up(10, 0, get_turf(src))
- steam.attach(src)
- steam.start()
-
- var/list/viewable = view(affected_area, loc)
- var/list/accessible = can_flood_from(loc, affected_area)
- var/list/reactable = accessible
- var/mycontents = GetAllContents()
- for(var/turf/T in accessible)
- for(var/atom/A in T.GetAllContents())
- if(A in mycontents) continue
- if(!(A in viewable)) continue
- reactable |= A
- if(!reactable.len) //Nothing to react with. Probably means we're in nullspace.
- qdel(src)
- return
- var/fraction = 1/reactable.len
- for(var/atom/A in reactable)
- reagents.reaction(A, TOUCH, fraction)
-
qdel(src)
-/obj/item/weapon/grenade/chem_grenade/proc/mix_reagents()
- var/total_temp
- for(var/obj/item/weapon/reagent_containers/glass/G in beakers)
- G.reagents.trans_to(src, G.reagents.total_volume)
- total_temp += G.reagents.chem_temp
- reagents.chem_temp = total_temp
-
-/obj/item/weapon/grenade/chem_grenade/proc/can_flood_from(myloc, maxrange)
- var/turf/myturf = get_turf(myloc)
- var/list/reachable = list(myloc)
- for(var/i=1; i<=maxrange; i++)
- var/list/turflist = list()
- for(var/turf/T in (orange(i, myloc) - orange(i-1, myloc)))
- turflist |= T
- for(var/turf/T in turflist)
- if( !(get_dir(T,myloc) in cardinal) && (abs(T.x - myturf.x) == abs(T.y - myturf.y) ))
- turflist.Remove(T)
- turflist.Add(T) // we move the purely diagonal turfs to the end of the list.
- for(var/turf/T in turflist)
- if(T in reachable) continue
- for(var/turf/NT in orange(1, T))
- if(!(NT in reachable)) continue
- if(!(get_dir(T,NT) in cardinal)) continue
- if(!NT.CanAtmosPass(T)) continue
- reachable |= T
- break
- return reachable
-
//Large chem grenades accept slime cores and use the appropriately.
/obj/item/weapon/grenade/chem_grenade/large
name = "large grenade"
- desc = "A custom made large grenade."
+ desc = "A custom made large grenade. It affects a larger area."
icon_state = "large_grenade"
allowed_containers = list(/obj/item/weapon/reagent_containers/glass,/obj/item/weapon/reagent_containers/food/condiment,
/obj/item/weapon/reagent_containers/food/drinks)
origin_tech = "combat=3;materials=3"
- affected_area = 4
+ affected_area = 5
+ ignition_temp = 25 // Large grenades are slightly more effective at setting off heat-sensitive mixtures than smaller grenades.
+ threatscale = 1.1 // 10% more effective.
+
+/obj/item/weapon/grenade/chem_grenade/large/prime()
+ if(stage != READY)
+ return
-/obj/item/weapon/grenade/chem_grenade/large/mix_reagents()
for(var/obj/item/slime_extract/S in beakers)
if(S.Uses)
for(var/obj/item/weapon/reagent_containers/glass/G in beakers)
@@ -272,6 +224,76 @@
else
return ..()
+/obj/item/weapon/grenade/chem_grenade/cryo // Intended for rare cryogenic mixes. Cools the area moderately upon detonation.
+ name = "cryo grenade"
+ desc = "A custom made cryogenic grenade. It rapidly cools its contents upon detonation."
+ icon_state = "cryog"
+ affected_area = 2
+ ignition_temp = -100
+
+/obj/item/weapon/grenade/chem_grenade/pyro // Intended for pyrotechnical mixes. Produces a small fire upon detonation, igniting potentially flammable mixtures.
+ name = "pyro grenade"
+ desc = "A custom made pyrotechnical grenade. It heats up and ignites its contents upon detonation."
+ icon_state = "pyrog"
+ affected_area = 3
+ ignition_temp = 500 // This is enough to expose a hotspot.
+
+/obj/item/weapon/grenade/chem_grenade/adv_release // Intended for weaker, but longer lasting effects. Could have some interesting uses.
+ name = "advanced release grenade"
+ desc = "A custom made advanced release grenade. It is able to be triggered more than once. Can be configured using a multitool."
+ icon_state = "timeg"
+ var/unit_spread = 10 // Amount of units per repeat. Can be altered with a multitool.
+
+/obj/item/weapon/grenade/chem_grenade/adv_release/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/device/multitool))
+ if(unit_spread < 100)
+ unit_spread += 5
+ else
+ unit_spread = 5
+ user << " You set the time release to [unit_spread] units per detonation."
+ return
+ else
+ return ..()
+
+/obj/item/weapon/grenade/chem_grenade/adv_release/prime()
+ if(stage != READY)
+ return
+
+ var/total_volume = 0
+ for(var/obj/item/weapon/reagent_containers/RC in beakers)
+ total_volume += RC.reagents.total_volume
+
+ var/fraction = unit_spread/total_volume
+ var/datum/reagents/reactants = new(unit_spread)
+ reactants.my_atom = src
+ for(var/obj/item/weapon/reagent_containers/RC in beakers)
+ RC.reagents.trans_to(reactants, RC.reagents.total_volume*fraction, threatscale, 1, 1)
+
+ chem_splash(get_turf(src), affected_area, list(reactants), ignition_temp, threatscale)
+
+ if(nadeassembly)
+ var/mob/M = get_mob_by_ckey(assemblyattacher)
+ var/mob/last = get_mob_by_ckey(nadeassembly.fingerprintslast)
+ var/turf/T = get_turf(src)
+ var/area/A = get_area(T)
+ message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) (FLW) and last touched by [key_name_admin(last)](?) (FLW) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).")
+ log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])")
+
+ var/turf/DT = get_turf(src)
+ var/area/DA = get_area(DT)
+ log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])")
+
+ if(total_volume < unit_spread) // If that was the last detonation, delete the grenade to prevent reusing it. Keep in mind, an explosion might destroy the grenade before it can detonate again anyways.
+ update_mob()
+ qdel(src)
+
+
+
+
+
+//////////////////////////////
+////// PREMADE GRENADES //////
+//////////////////////////////
/obj/item/weapon/grenade/chem_grenade/metalfoam
name = "metal foam grenade"
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
index 83f27e555f2..33fd8889965 100644
--- a/code/modules/crafting/recipes.dm
+++ b/code/modules/crafting/recipes.dm
@@ -327,3 +327,27 @@
time = 10
reqs = list(/obj/item/weapon/paper = 5)
category = CAT_MISC
+
+/datum/table_recipe/chemical_payload
+ name = "Chemical Bomb Core (C4)"
+ result = /obj/item/weapon/bombcore/chemical
+ reqs = list(
+ /obj/item/weapon/stock_parts/matter_bin/super = 1,
+ /obj/item/weapon/c4 = 1,
+ /obj/item/weapon/grenade/chem_grenade = 2
+ )
+ parts = list(/obj/item/weapon/grenade/chem_grenade = 2)
+ time = 30
+ category = CAT_WEAPON
+
+/datum/table_recipe/chemical_payload2
+ name = "Chemical Bomb Core (gibtonite)"
+ result = /obj/item/weapon/bombcore/chemical
+ reqs = list(
+ /obj/item/weapon/stock_parts/matter_bin/super = 1,
+ /obj/item/weapon/twohanded/required/gibtonite = 1,
+ /obj/item/weapon/grenade/chem_grenade = 2
+ )
+ parts = list(/obj/item/weapon/grenade/chem_grenade = 2)
+ time = 50
+ category = CAT_WEAPON
diff --git a/code/modules/reagents/chem_splash.dm b/code/modules/reagents/chem_splash.dm
new file mode 100644
index 00000000000..b32e9da1753
--- /dev/null
+++ b/code/modules/reagents/chem_splash.dm
@@ -0,0 +1,72 @@
+// Replaces chemgrenade stuff, allowing reagent explosions to be called from anywhere.
+// It should be called using a location, the range, and a list of reagents involved.
+
+// Threatscale is a multiplier for the 'threat' of the grenade. If you're increasing the affected range drastically, you might want to improve this.
+// Extra heat affects the temperature of the mixture, and may cause it to react in different ways.
+
+
+proc/chem_splash(turf/epicenter, affected_range = 3, list/datum/reagents/reactants = list(), extra_heat = 0, threatscale = 1, adminlog = 1)
+ if(!istype(epicenter, /turf) || !reactants.len || threatscale <= 0)
+ return
+ var/has_reagents
+ var/total_reagents
+ for(var/datum/reagents/R in reactants)
+ if(R.total_volume)
+ has_reagents = 1
+ total_reagents += R.total_volume
+
+ if(!has_reagents)
+ return
+
+ var/datum/reagents/splash_holder = new/datum/reagents(total_reagents*threatscale)
+ splash_holder.my_atom = epicenter // For some reason this is setting my_atom to null, and causing runtime errors.
+ var/total_temp = 0
+
+ for(var/datum/reagents/R in reactants)
+ R.trans_to(splash_holder, R.total_volume, threatscale, 1, 1)
+ total_temp += R.chem_temp
+ splash_holder.chem_temp = (total_temp/reactants.len) + extra_heat // Average temperature of reagents + extra heat.
+ splash_holder.handle_reactions() // React them now.
+
+ if(splash_holder.total_volume && affected_range >= 0) //The possible reactions didnt use up all reagents, so we spread it around.
+ var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread()
+ steam.set_up(10, 0, epicenter)
+ steam.attach(epicenter)
+ steam.start()
+
+ var/list/viewable = view(affected_range, epicenter)
+
+ var/list/accessible = list(epicenter)
+ for(var/i=1; i<=affected_range; i++)
+ var/list/turflist = list()
+ for(var/turf/T in (orange(i, epicenter) - orange(i-1, epicenter)))
+ turflist |= T
+ for(var/turf/T in turflist)
+ if( !(get_dir(T,epicenter) in cardinal) && (abs(T.x - epicenter.x) == abs(T.y - epicenter.y) ))
+ turflist.Remove(T)
+ turflist.Add(T) // we move the purely diagonal turfs to the end of the list.
+ for(var/turf/T in turflist)
+ if(T in accessible) continue
+ for(var/turf/NT in orange(1, T))
+ if(!(NT in accessible)) continue
+ if(!(get_dir(T,NT) in cardinal)) continue
+ if(!NT.CanAtmosPass(T)) continue
+ accessible |= T
+ break
+ var/list/reactable = accessible
+ for(var/turf/T in accessible)
+ for(var/atom/A in T.GetAllContents())
+ if(!(A in viewable)) continue
+ reactable |= A
+ if(extra_heat >= 300)
+ T.hotspot_expose(extra_heat*2, 5)
+ if(!reactable.len) //Nothing to react with. Probably means we're in nullspace.
+ return
+ var/fraction = 0.5/accessible.len // In a 100u mix. A small grenade spreads ~1.5u units per affected tile. A large grenade spreads ~0.75u, and a bomb spreads ~0.4u
+ for(var/atom/A in reactable)
+ splash_holder.reaction(A, TOUCH, fraction)
+
+ qdel(splash_holder)
+ return 1
+
+
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index 4297b94732e..b028b1d8bc1 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -123,7 +123,7 @@ var/const/INJECT = 5 //injection
return id
-/datum/reagents/proc/trans_to(obj/target, amount=1, multiplier=1, preserve_data=1)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred.
+/datum/reagents/proc/trans_to(obj/target, amount=1, multiplier=1, preserve_data=1, no_react = 0)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred.
if(!target )
return
var/datum/reagents/R
@@ -150,8 +150,9 @@ var/const/INJECT = 5 //injection
update_total()
R.update_total()
- R.handle_reactions()
- src.handle_reactions()
+ if(!no_react)
+ R.handle_reactions()
+ src.handle_reactions()
return amount
/datum/reagents/proc/copy_to(obj/target, amount=1, multiplier=1, preserve_data=1)
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index fa84cbd950f..60ab85b1519 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -127,6 +127,42 @@
build_path = /obj/item/weapon/grenade/chem_grenade/large
category = list("Weapons")
+/datum/design/pyro_grenade
+ name = "Pyro Grenade"
+ desc = "An advanced grenade that is able to self ignite its mixture."
+ id = "pyro_Grenade"
+ req_tech = list("combat" = 3, "materials" = 3)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 2000, MAT_PLASMA = 500)
+ reliability = 79
+ build_path = /obj/item/weapon/grenade/chem_grenade/pyro
+ category = list("Weapons")
+
+/* // Currently commented out, because it has no worthwhile useage yet.
+
+/datum/design/cryo_grenade
+ name = "Cryo Grenade"
+ desc = "An advanced grenade that rapidly cools its contents upon detonation."
+ id = "cryo_Grenade"
+ req_tech = list("combat" = 3, "materials" = 3)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 2000, MAT_SILVER = 500)
+ reliability = 79
+ build_path = /obj/item/weapon/grenade/chem_grenade/cryo
+ category = list("Weapons")
+*/
+
+/datum/design/adv_grenade
+ name = "Advanced Release Grenade"
+ desc = "An advanced grenade that can be detonated several times, best used with a repeating igniter."
+ id = "adv_Grenade"
+ req_tech = list("combat" = 3, "materials" = 3)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 3000, MAT_GLASS = 500)
+ reliability = 79
+ build_path = /obj/item/weapon/grenade/chem_grenade/adv_release
+ category = list("Weapons")
+
/datum/design/smg
name = "NanoTrasen Saber SMG"
desc = "A prototype advancment over the WT-550 auto rifle made using lightweight materials on a traditional frame, designed to fire standard 9mm rounds."
diff --git a/html/changelogs/chembombs.yml b/html/changelogs/chembombs.yml
new file mode 100644
index 00000000000..61602bf0deb
--- /dev/null
+++ b/html/changelogs/chembombs.yml
@@ -0,0 +1,13 @@
+
+author: "nullbear"
+
+delete-after: True
+
+changes:
+ - rscadd: "Added several craftable explosive chemical weapons."
+ - rscadd: "Made it possible to craft bomb assemblies using plasteel. Note: they will need a payload, and to have their wires set, before it can be detonated."
+ - rscadd: "Made it possible to emag bombs in order to cause the timer to malfunction and reset itself to 60 seconds. This can be used to give yourself more time, or to shorten the timer."
+ - tweak: "Large Grenades are now 10% larger and more effective."
+ - tweak: "Grenades now typically heat up their contents when they explode."
+ - tweak: "Non-typical grenades are slightly more potent than they were before."
+ - tweak: "Reduces metagaming potential for bypassing the timer and setting off bombs with c4 or fire."
diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi
index fc79b379f48..5a8719da0d8 100644
Binary files a/icons/obj/assemblies.dmi and b/icons/obj/assemblies.dmi differ
diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi
index c85b9eaa4c2..e17974ecd19 100644
Binary files a/icons/obj/grenade.dmi and b/icons/obj/grenade.dmi differ