From b664f73a6843b54c26206e1c5fae94b478c07eb4 Mon Sep 17 00:00:00 2001 From: Fox-McCloud Date: Thu, 29 Jun 2017 20:02:27 -0400 Subject: [PATCH] chemical bombs --- code/game/machinery/syndicatebomb.dm | 122 ++++++++++++ .../items/weapons/grenades/chem_grenade.dm | 173 ++++++++++-------- code/modules/crafting/recipes.dm | 24 +++ code/modules/reagents/chem_splash.dm | 78 ++++++++ code/modules/reagents/chemistry/holder.dm | 17 +- .../reagent_containers/glass_containers.dm | 3 +- code/modules/reagents/reagent_dispenser.dm | 10 +- .../research/designs/weapon_designs.dm | 30 +++ icons/obj/grenade.dmi | Bin 4597 -> 4872 bytes paradise.dme | 1 + 10 files changed, 367 insertions(+), 91 deletions(-) create mode 100644 code/modules/reagents/chem_splash.dm diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index ba3ed604e00..a4503e427d8 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -406,6 +406,128 @@ range_light = 4 range_flame = 2 +/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 = 1 + 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) + + // Detonate it again in one second, until it's out of juice. + addtimer(src, "detonate", 10) + + // 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, I.usesound, 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 + to_chat(user, "You load [src] with [I].") + I.loc = src + else + to_chat(user, "The [I] wont fit! The [src] can only hold up to [max_beakers] containers.") + return + ..() + +/obj/item/weapon/bombcore/chemical/CheckParts(list/parts_list) + ..() + // Using different grenade casings, causes the payload to have different properties. + var/obj/item/weapon/stock_parts/matter_bin/MB = locate(/obj/item/weapon/stock_parts/matter_bin) in src + if(MB) + max_beakers += MB.rating // max beakers = 2-5. + qdel(MB) + 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 7 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 += 50 // 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) + ///Syndicate Detonator (aka the big red button)/// /obj/item/device/syndicatedetonator diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index f21914a7c32..9ba1950d2a0 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -19,6 +19,9 @@ var/obj/item/device/assembly_holder/nadeassembly = null var/label = 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. + var/no_splash = FALSE //If the grenade deletes even if it has no reagents to splash with. Used for slime core reactions. /obj/item/weapon/grenade/chem_grenade/New() create_reagents(1000) @@ -99,6 +102,7 @@ log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])") bombers += "[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])" to_chat(user, "You prime the [name]! [det_time / 10] second\s!") + playsound(user.loc, 'sound/weapons/armbomb.ogg', 60, 1) active = 1 update_icon() if(iscarbon(user)) @@ -264,13 +268,18 @@ if(stage != READY) return - var/has_reagents = 0 + var/list/datum/reagents/reactants = list() for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - if(G.reagents.total_volume) - has_reagents = 1 + reactants += G.reagents - if(!has_reagents) - playsound(loc, usesound, 50, 1) + if(!chem_splash(get_turf(src), affected_area, reactants, ignition_temp, threatscale) && !no_splash) + playsound(loc, 'sound/items/Screwdriver2.ogg', 50, 1) + if(beakers.len) + for(var/obj/O in beakers) + O.forceMove(get_turf(src)) + beakers = list() + stage = EMPTY + update_icon() return if(nadeassembly) @@ -281,31 +290,9 @@ message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) ([admin_jump_link(M)]) and last touched by [key_name_admin(last)](?) ([admin_jump_link(last)]) ([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) - update_mob() - invisibility = INVISIBILITY_MAXIMUM //kaboom - qdel(nadeassembly) // do this now to stop infrared beams - var/end_temp = 0 - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - G.reagents.trans_to(src, G.reagents.total_volume) - end_temp += G.reagents.chem_temp - reagents.chem_temp = end_temp - if(reagents.total_volume) //The possible reactions didnt use up all reagents. - 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() - - for(var/atom/A in view(affected_area, loc)) - if(A == src) - continue - reagents.reaction(A, 1, 10) - - - spawn(15) //Making sure all reagents can work - qdel(src) //correctly before deleting the grenade. + qdel(src) /obj/item/weapon/grenade/chem_grenade/proc/CreateDefaultTrigger(var/typekey) if(ispath(typekey,/obj/item/device/assembly)) @@ -327,64 +314,37 @@ //Large chem grenades accept slime cores and use the appropriately. /obj/item/weapon/grenade/chem_grenade/large name = "large grenade casing" - desc = "For oversized grenades; fits additional contents and affects a greater area." + desc = "A custom made large grenade. It affects a larger area." icon_state = "large_grenade" bomb_state = "largebomb" 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;engineering=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 - var/has_reagents = 0 - var/obj/item/slime_extract/valid_core = null + 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) - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - if(!istype(G)) continue - if(G.reagents.total_volume) has_reagents = 1 - for(var/obj/item/slime_extract/E in beakers) - if(!istype(E)) continue - if(E.Uses) valid_core = E - if(E.reagents.total_volume) has_reagents = 1 + //If there is still a core (sometimes it's used up) + //and there are reagents left, behave normally, + //otherwise drop it on the ground for timed reactions like gold. - if(!has_reagents) - playsound(loc, prime_sound, 50, 1) - return - - playsound(loc, 'sound/effects/bamf.ogg', 50, 1) - - update_mob() - - if(valid_core) - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - G.reagents.trans_to(valid_core, G.reagents.total_volume) - - //If there is still a core (sometimes it's used up) - //and there are reagents left, behave normally - - if(valid_core && valid_core.reagents && valid_core.reagents.total_volume) - valid_core.reagents.trans_to(src,valid_core.reagents.total_volume) - else - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - G.reagents.trans_to(src, G.reagents.total_volume) - - if(reagents.total_volume) //The possible reactions didnt use up all reagents. - 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() - - for(var/atom/A in view(affected_area, loc)) - if( A == src ) continue - reagents.reaction(A, 1, 10) - - invisibility = INVISIBILITY_MAXIMUM //Why am i doing this? - spawn(50) //To make sure all reagents can work - qdel(src) //correctly before deleting the grenade. + if(S) + if(S.reagents && S.reagents.total_volume) + for(var/obj/item/weapon/reagent_containers/glass/G in beakers) + S.reagents.trans_to(G, S.reagents.total_volume) + else + S.forceMove(get_turf(src)) + no_splash = TRUE + ..() //I tried to just put it in the allowed_containers list but @@ -399,6 +359,71 @@ 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" + origin_tech = "combat=4;engineering=4" + 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 detonated more than once. Can be configured using a multitool." + icon_state = "timeg" + origin_tech = "combat=3;engineering=4" + 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)) + switch(unit_spread) + if(0 to 24) + unit_spread += 5 + if(25 to 99) + unit_spread += 25 + else + unit_spread = 5 + to_chat(user, " You set the time release to [unit_spread] units per detonation.") + 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 + if(!total_volume) + qdel(src) + qdel(nadeassembly) + return + 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])") + else + addtimer(src, "prime", det_time) + 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])") /obj/item/weapon/grenade/chem_grenade/metalfoam payload_name = "metal foam" diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 6f266c121e1..3fbcf39ed08 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -324,6 +324,30 @@ /obj/item/stack/rods = 1) category = CAT_MISC +/datum/crafting_recipe/chemical_payload + name = "Chemical Payload (C4)" + result = /obj/item/weapon/bombcore/chemical + reqs = list( + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/grenade/plastic/c4 = 1, + /obj/item/weapon/grenade/chem_grenade = 2 + ) + parts = list(/obj/item/weapon/stock_parts/matter_bin = 1, /obj/item/weapon/grenade/chem_grenade = 2) + time = 30 + category = CAT_WEAPON + +/datum/crafting_recipe/chemical_payload2 + name = "Chemical Payload (gibtonite)" + result = /obj/item/weapon/bombcore/chemical + reqs = list( + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/twohanded/required/gibtonite = 1, + /obj/item/weapon/grenade/chem_grenade = 2 + ) + parts = list(/obj/item/weapon/stock_parts/matter_bin = 1, /obj/item/weapon/grenade/chem_grenade = 2) + time = 50 + category = CAT_WEAPON + /datum/crafting_recipe/bonfire name = "Bonfire" time = 60 diff --git a/code/modules/reagents/chem_splash.dm b/code/modules/reagents/chem_splash.dm new file mode 100644 index 00000000000..7c16bf1ab9a --- /dev/null +++ b/code/modules/reagents/chem_splash.dm @@ -0,0 +1,78 @@ +// 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(!isturf(epicenter) || !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(accessible[T]) + continue + for(var/thing in T.GetAtmosAdjacentTurfs(alldir = TRUE)) + var/turf/NT = thing + if(!(NT in accessible)) + continue + if(!(get_dir(T,NT) in cardinal)) + continue + accessible[T] = 1 + 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 + for(var/thing in reactable) + var/atom/A = thing + var/distance = max(1,get_dist(A, epicenter)) + var/fraction = 0.5/(2 ** distance) //50/25/12/6... for a 200u splash, 25/12/6/3... for a 100u, 12/6/3/1 for a 50u + 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 c65c628353b..662843e337a 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -108,7 +108,7 @@ var/const/INGEST = 2 return the_id -/datum/reagents/proc/trans_to(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(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 if(total_volume <= 0) @@ -143,13 +143,14 @@ var/const/INGEST = 2 if(preserve_data) trans_data = copy_data(current_reagent) - R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, chem_temp) + R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, chem_temp, no_react = 1) remove_reagent(current_reagent.id, current_reagent_transfer) update_total() R.update_total() - R.handle_reactions() - handle_reactions() + if(!no_react) + R.handle_reactions() + handle_reactions() return amount /datum/reagents/proc/copy_to(obj/target, amount=1, multiplier=1, preserve_data=1, safety = 0) @@ -537,7 +538,7 @@ var/const/INGEST = 2 var/amt = list_reagents[r_id] add_reagent(r_id, amt, data) -/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300) +/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, no_react = 0) if(!isnum(amount)) return 1 update_total() @@ -554,7 +555,8 @@ var/const/INGEST = 2 update_total() my_atom.on_reagent_change() R.on_merge(data) - handle_reactions() + if(!no_react) + handle_reactions() return 0 var/datum/reagent/D = chemical_reagents_list[reagent] @@ -570,7 +572,8 @@ var/const/INGEST = 2 update_total() my_atom.on_reagent_change() - handle_reactions() + if(!no_react) + handle_reactions() return 0 else warning("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index f60efa3b784..63db086cef8 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -40,7 +40,8 @@ /obj/machinery/biogenerator, /obj/machinery/hydroponics, /obj/machinery/constructable_frame, - /obj/machinery/icemachine) + /obj/machinery/icemachine, + /obj/item/weapon/bombcore/chemical) /obj/item/weapon/reagent_containers/glass/New() ..() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index d406dcaf0ef..eb43698d451 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -29,6 +29,7 @@ /obj/structure/reagent_dispensers/proc/boom() visible_message("[src] ruptures!") + chem_splash(loc, 5, list(reagents)) qdel(src) /obj/structure/reagent_dispensers/ex_act(severity) @@ -53,15 +54,6 @@ desc = "A water tank." icon_state = "water" -/obj/structure/reagent_dispensers/watertank/boom() - playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) - new /obj/effect/effect/water(loc) - for(var/turf/simulated/T in view(5, loc)) - T.MakeSlippery() - for(var/mob/living/L in T) - L.adjust_fire_stacks(-20) - ..() - /obj/structure/reagent_dispensers/watertank/high name = "high-capacity water tank" desc = "A highly-pressurized water tank made to hold gargantuan amounts of water.." diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 9391af77424..09b17748dfd 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -80,6 +80,36 @@ 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" = 4, "engineering" = 4) + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_PLASMA = 500) + build_path = /obj/item/weapon/grenade/chem_grenade/pyro + category = list("Weapons") + +/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) + 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, "engineering" = 4) + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 500) + build_path = /obj/item/weapon/grenade/chem_grenade/adv_release + category = list("Weapons") + /datum/design/tele_shield name = "Telescopic Riot Shield" desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi index ad8268ad59dac163681ac1710bcef5c578d09864..f33135baec632b21c8229bcddbc360d2f3bda207 100644 GIT binary patch literal 4872 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 diff --git a/paradise.dme b/paradise.dme index 0775e9b2577..0f7332878f4 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1948,6 +1948,7 @@ #include "code\modules\projectiles\projectile\magic.dm" #include "code\modules\projectiles\projectile\reusable.dm" #include "code\modules\projectiles\projectile\special.dm" +#include "code\modules\reagents\chem_splash.dm" #include "code\modules\reagents\reagent_containers.dm" #include "code\modules\reagents\reagent_dispenser.dm" #include "code\modules\reagents\chemistry\colors.dm"