diff --git a/_maps/map_files/wawastation/wawastation.dmm b/_maps/map_files/wawastation/wawastation.dmm index d1d71be9d4b..9fd655eda86 100644 --- a/_maps/map_files/wawastation/wawastation.dmm +++ b/_maps/map_files/wawastation/wawastation.dmm @@ -62053,7 +62053,7 @@ /obj/item/stock_parts/power_store/cell/bluespace{ pixel_x = -5; pixel_y = -8; - rigged = 1 + corrupted = 1 }, /turf/open/misc/asteroid, /area/station/asteroid) diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index f0629886ef2..80413080462 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -296,3 +296,17 @@ /// Cooldown between patch reagent messages #define PATCH_MESSAGE_COOLDOWN 10 SECONDS + +// on_spark_act returns +/// We caused a non-destructive reaction +#define SPARK_ACT_NON_DESTRUCTIVE (1 << 0) +/// We caused a destructive action or an explosion, the holder should probably delete itself +#define SPARK_ACT_DESTRUCTIVE (1 << 1) +/// Don't remove this reagent, even if it reacted to spark_act +#define SPARK_ACT_KEEP_REAGENT (1 << 2) +/// Remove all reagents even if they didn't react and stop the processing +/// Usually means an explosion or a destroyed parent +#define SPARK_ACT_CLEAR_ALL (1 << 3) + +/// Valid values to be returned from reagent holder's spark_act +#define SPARK_ACT_RETURNS (SPARK_ACT_NON_DESTRUCTIVE | SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL) diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm index f8062a8a224..4ca6932cb3c 100644 --- a/code/controllers/subsystem/explosions.dm +++ b/code/controllers/subsystem/explosions.dm @@ -169,6 +169,8 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec * 5 explosion power is a (0, 1, 3) explosion. * 1 explosion power is a (0, 0, 1) explosion. * + * The easy formula for defaults is sqrt(power * 2) * (0.25 | 0.5 | 1) + * * Arguments: * * epicenter: Turf the explosion is centered at. * * power - Dyn explosion power. See reference above. @@ -176,7 +178,6 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec * * flash_range: The range at which the explosion flashes people. Equal to the equivalent of the light impact range multiplied by this value. * * adminlog: Whether to log the explosion/report it to the administration. * * ignorecap: Whether to ignore the relevant bombcap. Defaults to FALSE. - * * flame_range: The range at which the explosion should produce hotspots. * * silent: Whether to generate/execute sound effects. * * smoke: Whether to generate a smoke cloud provided the explosion is powerful enough to warrant it. * * explosion_cause: [Optional] The atom that caused the explosion, when different to the origin. Used for logging. @@ -186,7 +187,7 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec return var/range = 0 range = round((2 * power)**GLOB.DYN_EX_SCALE) - explosion(epicenter, devastation_range = round(range * 0.25), heavy_impact_range = round(range * 0.5), light_impact_range = round(range), flame_range = flame_range*range, flash_range = flash_range*range, adminlog = adminlog, ignorecap = ignorecap, silent = silent, smoke = smoke, explosion_cause = explosion_cause) + explosion(epicenter, devastation_range = round(range * 0.25), heavy_impact_range = round(range * 0.5), light_impact_range = round(range), flame_range = isnull(flame_range) ? null : flame_range * range, flash_range = isnull(flash_range) ? null : flash_range * range, adminlog = adminlog, ignorecap = ignorecap, silent = silent, smoke = smoke, explosion_cause = explosion_cause) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 2b2d1dd7918..fdf6806046a 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -1092,9 +1092,7 @@ if(istype(secondary_part, /obj/item/stock_parts/power_store/cell) && works_from_distance) var/obj/item/stock_parts/power_store/cell/checked_cell = secondary_part // If it's rigged or corrupted, max the charge. Then explode it. - if(checked_cell.rigged || checked_cell.corrupted) - checked_cell.charge = checked_cell.maxcharge - checked_cell.explode() + if(checked_cell.try_explode(max_charge = TRUE)) break if(secondary_part.get_part_rating() > current_rating) //store name of part incase we qdel it below diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm index fd41f739e33..c47f4684283 100644 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ b/code/game/objects/effects/effect_system/effects_other.dm @@ -88,20 +88,19 @@ /datum/effect_system/reagents_explosion var/amount // TNT equivalent - var/flashing = FALSE // does explosion creates flash effect? - var/flashing_factor = 0 // factor of how powerful the flash effect relatively to the explosion + var/flashing_factor = null // factor of how powerful the flash effect relatively to the explosion + var/flaming_factor = null // factor of how powerful the flame effect is relatively to explosion var/explosion_message = 1 //whether we show a message to mobs. -/datum/effect_system/reagents_explosion/set_up(amt, loca, flash = FALSE, flash_fact = 0, message = TRUE) +/datum/effect_system/reagents_explosion/set_up(amt, loca, flash_fact = null, flame_fact = null, message = TRUE) amount = amt explosion_message = message if(isturf(loca)) location = loca else location = get_turf(loca) - - flashing = flash flashing_factor = flash_fact + flaming_factor = flame_fact /// Starts the explosion. The explosion_source is as part of logging and identifying the source of the explosion for logs. /datum/effect_system/reagents_explosion/start(atom/explosion_source = null) @@ -111,4 +110,4 @@ if(explosion_message) location.visible_message(span_danger("The solution violently explodes!"), span_hear("You hear an explosion!")) - dyn_explosion(location, amount, flash_range = flashing_factor, explosion_cause = explosion_source) + dyn_explosion(location, amount, flash_range = flashing_factor, flame_range = flaming_factor, explosion_cause = explosion_source) diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index 5275f69df20..7e008a750d3 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -426,6 +426,12 @@ CIGARETTE PACKETS ARE IN FANCY.DM damtype = BURN force = 4 + if(reagents?.spark_act(0, FALSE, banned_reagents = /datum/reagent/flash_powder) & SPARK_ACT_DESTRUCTIVE) + usr?.log_message("lit a rigged cigarette", LOG_VICTIM) + qdel(src) + return + + // Custom handling for the hallucination effect if(reagents?.has_reagent(/datum/reagent/flash_powder)) if(!isliving(loc)) loc.visible_message(span_hear("\The [src] burns up!")) @@ -442,18 +448,6 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(reagents?.has_reagent(/datum/reagent/drug/methamphetamine)) reagents.flags |= NO_REACT - if(reagents?.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount(/datum/reagent/toxin/plasma) / 2.5, 1), get_turf(src), 0, 0) - e.start(src) - qdel(src) - return - if(reagents?.get_reagent_amount(/datum/reagent/fuel)) // the fuel explodes, too, but much less violently - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount(/datum/reagent/fuel) / 5, 1), get_turf(src), 0, 0) - e.start(src) - qdel(src) - return // allowing reagents to react after being lit update_appearance(UPDATE_ICON) if(flavor_text) @@ -1178,7 +1172,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(reagents.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire var/datum/effect_system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount(/datum/reagent/toxin/plasma) / 2.5, 1), get_turf(src), 0, 0) + e.set_up(round(reagents.get_reagent_amount(/datum/reagent/toxin/plasma) / 2.5, 1), get_turf(src), 0) e.start(src) qdel(src) diff --git a/code/game/objects/items/lighter.dm b/code/game/objects/items/lighter.dm index bd87f67ea22..1bf94097f32 100644 --- a/code/game/objects/items/lighter.dm +++ b/code/game/objects/items/lighter.dm @@ -245,6 +245,10 @@ if(!lit) return FALSE + if (reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) + qdel(src) + return FALSE + if(used > 0) burned_fuel_for = 0 @@ -257,7 +261,7 @@ ///Returns the amount of fuel /obj/item/lighter/proc/get_fuel() - return reagents.get_reagent_amount(/datum/reagent/fuel) + return reagents.get_reagent_amount(/datum/reagent/fuel) + reagents.get_reagent_amount(/datum/reagent/toxin/plasma) /obj/item/lighter/greyscale name = "cheap lighter" diff --git a/code/game/objects/items/tools/engineering/weldingtool.dm b/code/game/objects/items/tools/engineering/weldingtool.dm index 142bf0be111..ec2412ca835 100644 --- a/code/game/objects/items/tools/engineering/weldingtool.dm +++ b/code/game/objects/items/tools/engineering/weldingtool.dm @@ -119,11 +119,6 @@ . = ..() update_appearance() -/obj/item/weldingtool/proc/explode() - var/plasmaAmount = reagents.get_reagent_amount(/datum/reagent/toxin/plasma) - dyn_explosion(src, plasmaAmount/5, explosion_cause = src) // 20 plasma in a standard welder has a 4 power explosion. no breaches, but enough to kill/dismember holder - qdel(src) - /obj/item/weldingtool/cyborg_unequip(mob/user) if(!isOn()) return @@ -189,10 +184,10 @@ user.log_message("set [key_name(attacked_mob)] on fire with [src].", LOG_ATTACK) /obj/item/weldingtool/attack_self(mob/user) - if(reagents.has_reagent(/datum/reagent/toxin/plasma)) + if(reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) message_admins("[ADMIN_LOOKUPFLW(user)] activated a rigged welder at [AREACOORD(user)].") user.log_message("activated a rigged welder", LOG_VICTIM) - explode() + qdel(src) return switched_on(user) diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm index 3c9af66ca24..659ed72854b 100644 --- a/code/modules/antagonists/heretic/influences.dm +++ b/code/modules/antagonists/heretic/influences.dm @@ -148,7 +148,7 @@ human_user.gib(DROP_ALL_REMAINS) human_user.investigate_log("has died from using telekinesis on a heretic influence.", INVESTIGATE_DEATHS) var/datum/effect_system/reagents_explosion/explosion = new() - explosion.set_up(1, get_turf(human_user), TRUE, 0) + explosion.set_up(1, get_turf(human_user), 1) explosion.start(src) /obj/effect/visible_heretic_influence/examine(mob/living/user) diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm index 5ceb818ded5..f099050ecfb 100644 --- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm @@ -304,7 +304,7 @@ if(!head?.dismember()) carbon_view.gib(DROP_ALL_REMAINS) var/datum/effect_system/reagents_explosion/explosion = new() - explosion.set_up(1, get_turf(carbon_view), TRUE, 0) + explosion.set_up(1, get_turf(carbon_view), 1) explosion.start(src) else attempt_conversion(carbon_view, source) diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index 76892cd2870..4437353b1d3 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -624,10 +624,6 @@ pocell.charge = pocell.maxcharge pocell.name = "[our_plant.name] battery" pocell.desc = "A rechargeable plant-based power cell. This one has a rating of [display_energy(pocell.maxcharge)], and you should not swallow it." - - if(our_plant.reagents.has_reagent(/datum/reagent/toxin/plasma, 2)) - pocell.rigged = TRUE - qdel(our_plant) /* diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 52cd8ec6ad1..bafaeafe7d4 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -668,7 +668,7 @@ return TRUE //called when the mob receives a loud bang -/mob/living/proc/soundbang_act(intensity = SOUNDBANG_NORMAL, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15, ignore_deafness = FALSE, send_sound = TRUE) +/mob/living/proc/soundbang_act(intensity = SOUNDBANG_NORMAL, stun_pwr = 2 SECONDS, damage_pwr = 5, deafen_pwr = 1.5 SECONDS, ignore_deafness = FALSE, send_sound = TRUE) var/protection = get_ear_protection(ignore_deafness) if(protection >= intensity) return FALSE diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index c4265c35e95..2f268d5746f 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -231,6 +231,7 @@ if(LIGHT_BROKEN,LIGHT_BURNED,LIGHT_EMPTY) on = FALSE low_power_mode = FALSE + if(on) var/brightness_set = brightness var/power_set = bulb_power @@ -239,6 +240,10 @@ color_set = color if(reagents || !is_full_charge()) START_PROCESSING(SSmachines, src) + if (reagents.spark_act(active_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE) + message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.") + qdel(src) + return var/area/local_area = get_room_area() if (flickering) brightness_set = brightness * bulb_low_power_brightness_mul @@ -274,6 +279,10 @@ else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE * SSMACHINES_DT) && !turned_off()) use_power = IDLE_POWER_USE low_power_mode = TRUE + if (reagents?.spark_act(idle_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE) + message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.") + qdel(src) + return START_PROCESSING(SSmachines, src) else use_power = IDLE_POWER_USE diff --git a/code/modules/power/power_store.dm b/code/modules/power/power_store.dm index dee7151f523..adca94543e0 100644 --- a/code/modules/power/power_store.dm +++ b/code/modules/power/power_store.dm @@ -18,9 +18,7 @@ var/rating_base = STANDARD_CELL_CHARGE ///Maximum charge in cell units var/maxcharge = STANDARD_CELL_CHARGE - ///If the cell has been booby-trapped by injecting it with plasma. Chance on use() to explode. - var/rigged = FALSE - ///If the power cell was damaged by an explosion, chance for it to become corrupted and function the same as rigged. + ///If the power cell was damaged by an explosion, chance for it to become corrupted and function the same as if it was rigged with plasma. var/corrupted = FALSE ///How much power is given per second in a recharger. var/chargerate = STANDARD_CELL_RATE * 0.05 @@ -43,7 +41,7 @@ /obj/item/stock_parts/power_store/get_save_vars() . = ..() . += NAMEOF(src, charge) - . += NAMEOF(src, rigged) + . += NAMEOF(src, corrupted) return . /obj/item/stock_parts/power_store/Initialize(mapload, override_maxcharge) @@ -107,10 +105,6 @@ return . -/obj/item/stock_parts/power_store/create_reagents(max_vol, flags) - . = ..() - RegisterSignal(reagents, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(on_reagent_change)) - /obj/item/stock_parts/power_store/update_overlays() . = ..() if(grown_battery) @@ -163,8 +157,7 @@ /// Returns: The power used from the cell in joules. /obj/item/stock_parts/power_store/use(used, force = FALSE) var/power_used = min(used, charge) - if(rigged && power_used > 0) - explode() + if(power_used > 0 && try_explode()) return 0 // The cell decided to explode so we won't be able to use it. if(!force && charge < used) return 0 @@ -180,8 +173,8 @@ /obj/item/stock_parts/power_store/proc/give(amount) var/power_used = min(maxcharge-charge,amount) charge += power_used - if(rigged && amount > 0) - explode() + if (amount) + try_explode() return power_used /** @@ -193,46 +186,51 @@ /obj/item/stock_parts/power_store/proc/change(amount) var/energy_used = clamp(amount, -charge, maxcharge - charge) charge += energy_used - if(rigged && energy_used) - explode() + if(energy_used) + try_explode() return energy_used /obj/item/stock_parts/power_store/examine(mob/user) . = ..() - if(rigged) + if(corrupted) . += span_danger("This [name] seems to be faulty!") else if(!isnull(charge_light_type)) . += "The charge meter reads [CEILING(percent(), 0.1)]%." //so it doesn't say 0% charge when the overlay indicates it still has charge -/obj/item/stock_parts/power_store/proc/on_reagent_change(datum/reagents/holder) - SIGNAL_HANDLER +/obj/item/stock_parts/power_store/proc/try_explode(max_charge = FALSE) + var/check_charge = charge + if (max_charge) + check_charge = maxcharge - rigged = corrupted || !!holder.has_reagent(/datum/reagent/toxin/plasma, 5) //has_reagent returns the reagent datum + if(!check_charge) + return FALSE -/obj/item/stock_parts/power_store/proc/explode() - if(!charge) - return - var/range_devastation = -1 - var/range_heavy = round(sqrt(charge / (3.6 * rating_base))) - var/range_light = round(sqrt(charge / (0.9 * rating_base))) - var/range_flash = range_light + if (!corrupted) + if (!(reagents?.spark_act(check_charge, TRUE) & SPARK_ACT_DESTRUCTIVE)) + return FALSE + message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a rigged power cell explosion at [AREACOORD(loc)].") + usr?.log_message("triggered a rigged power cell explosion", LOG_GAME) + usr?.log_message("triggered a rigged power cell explosion", LOG_VICTIM, log_globally = FALSE) + qdel(src) + return TRUE + + var/range_heavy = round(sqrt(check_charge / (3.6 * rating_base))) + var/range_light = round(sqrt(check_charge / (0.9 * rating_base))) if(!range_light) - rigged = FALSE corrupt() - return + return FALSE - message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a rigged/corrupted power cell explosion at [AREACOORD(loc)].") - usr?.log_message("triggered a rigged/corrupted power cell explosion", LOG_GAME) - usr?.log_message("triggered a rigged/corrupted power cell explosion", LOG_VICTIM, log_globally = FALSE) - - explosion(src, devastation_range = range_devastation, heavy_impact_range = range_heavy, light_impact_range = range_light, flash_range = range_flash) + message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a corrupted power cell explosion at [AREACOORD(loc)].") + usr?.log_message("triggered a corrupted power cell explosion", LOG_GAME) + usr?.log_message("triggered a corrupted power cell explosion", LOG_VICTIM, log_globally = FALSE) + explosion(src, devastation_range = -1, heavy_impact_range = range_heavy, light_impact_range = range_light, flash_range = range_light) qdel(src) + return TRUE /obj/item/stock_parts/power_store/proc/corrupt(force) charge /= 2 maxcharge = max(maxcharge/2, chargerate) if (force || prob(10)) - rigged = TRUE //broken batterys are dangerous corrupted = TRUE /obj/item/stock_parts/power_store/emp_act(severity) diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index 3d50d5c1e28..a03c360e328 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -788,6 +788,34 @@ set_temperature(round(chem_temp)) handle_reactions() +/* + * Call in case of electrical current exposure, rapid heating or blunt force, things that would set off explosives and alike + * Arguments: + * * power_charge - If we were triggered from electric current, how much power was dumped into us? + * * enclosed - Is the reaction happening in an enclosed container or not? Doesn't use reagent holder's flags as it might be called on reagents "exiting" the container + * * banned_reagents - List of reagent types which we may want to have custom handling for and should avoid checking in here + */ +/datum/reagents/proc/spark_act(power_charge, enclosed, list/banned_reagents) + if (!islist(banned_reagents)) + banned_reagents = list(banned_reagents) + var/result = NONE + var/update = FALSE + for (var/datum/reagent/reagent as anything in reagent_list) + if (is_type_in_list(reagent, banned_reagents)) + continue + var/reagent_result = reagent.on_spark_act(power_charge, enclosed) + if (!reagent_result) + continue + result |= (reagent_result & SPARK_ACT_RETURNS) + if (!(reagent_result & SPARK_ACT_KEEP_REAGENT)) + reagent.volume = 0 + update = TRUE + + if (result & SPARK_ACT_CLEAR_ALL) + clear_reagents() + else if (update) + update_total() + return result //===============================Logging========================================== /// Outputs a log-friendly list of reagents based on the internal reagent_list. diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index b2bf032d208..273268f1c30 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -236,6 +236,17 @@ /datum/reagent/proc/on_mob_dead(mob/living/carbon/affected_mob, seconds_per_tick) SHOULD_CALL_PARENT(TRUE) +/* + * Called when a reagent is exposed to electric current, rapidly heated or smashed, something that would cause explosives to get easily set off + * Returning a SPARK_ACT_ flag will signal that an action has occurred as a result, for parent behavior and logging purposes + * Probably shouldn't be called from within a mob's bloodstream, unless you're ready for some very explosive results + * Arguments: + * * power_charge - If we were triggered from electric current, how much power was dumped into us? + * * enclosed - Is the reaction happening in an enclosed container or not? Doesn't use reagent holder's flags as it might be called on reagents "exiting" the container + */ +/datum/reagent/proc/on_spark_act(power_charge = 0, enclosed = TRUE) + return NONE + /** * Called after add_reagents creates a new reagent. * diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 0fec8f117c9..e55e1b33b32 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -1298,7 +1298,6 @@ /datum/reagent/fuel/expose_turf(turf/exposed_turf, reac_volume) . = ..() - if(!istype(exposed_turf) || isspaceturf(exposed_turf)) return @@ -1309,6 +1308,16 @@ if(pool) pool.burn_amount = max(min(round(reac_volume / 5), 10), 1) +/datum/reagent/fuel/on_spark_act(power_charge, enclosed) + // Doesn't go boom in open air unless we pass a REALLY high current or if we're hot enough + if (!enclosed && power_charge < STANDARD_BATTERY_VALUE && holder.chem_temp < 474) + var/turf/our_turf = get_turf(holder.my_atom) + our_turf?.hotspot_expose(holder.chem_temp * 10, volume) + return NONE + + reagent_explode(holder, volume, strengthdiv = 10, clear_holder_reagents = FALSE) + return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + /datum/reagent/space_cleaner name = "Space Cleaner" description = "A compound used to clean things. Now with 50% more sodium hypochlorite! Can be used to clean wounds, but it's not really meant for that." diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index cc45a67b64a..d2895ccb683 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -29,6 +29,10 @@ if(affected_mob.adjust_organ_loss(ORGAN_SLOT_HEART, -0.5 * metabolization_ratio * seconds_per_tick * normalise_creation_purity(), required_organ_flag = affected_organ_flags)) return UPDATE_MOB_HEALTH +/datum/reagent/nitroglycerin/on_spark_act(power_charge, enclosed) + reagent_explode(holder, volume, strengthdiv = 2, clear_holder_reagents = FALSE) + return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + /datum/reagent/stabilizing_agent name = "Stabilizing Agent" description = "Keeps unstable chemicals stable. This does not work on everything." @@ -85,6 +89,11 @@ taste_description = "air and bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/sorium/on_spark_act(power_charge, enclosed) + var/range = clamp(sqrt(volume), 1, 6) + goonchem_vortex(get_turf(holder.my_atom), 1, range) + return SPARK_ACT_NON_DESTRUCTIVE + /datum/reagent/liquid_dark_matter name = "Liquid Dark Matter" description = "Sucks everything into the detonation point." @@ -92,6 +101,11 @@ taste_description = "compressed bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/liquid_dark_matter/on_spark_act(power_charge, enclosed) + var/range = clamp(sqrt(volume / 2), 1, 6) + goonchem_vortex(get_turf(holder.my_atom), 0, range) + return SPARK_ACT_NON_DESTRUCTIVE + /datum/reagent/gunpowder name = "Gunpowder" description = "Explodes. Violently." @@ -116,10 +130,29 @@ return var/location = get_turf(holder.my_atom) var/datum/effect_system/reagents_explosion/e = new() - e.set_up(1 + round(volume/6, 1), location, 0, 0, message = 0) + e.set_up(1 + round(volume / 6, 1), location, message = FALSE) e.start(holder.my_atom) holder.clear_reagents() +/datum/reagent/gunpowder/on_spark_act(power_charge, enclosed) + // Gunpowder doesn't blow in presence of stabilizing agent but instead consumes it every time it'd get triggered + var/agent_volume = holder.get_reagent_amount(/datum/reagent/stabilizing_agent) + if (agent_volume) + var/sapped_amt = 0.1 + round(power_charge / (0.1 * STANDARD_CELL_CHARGE), 0.01) + holder.remove_reagent(/datum/reagent/stabilizing_agent, sapped_amt) + if (agent_volume > sapped_amt) + return + + if (power_charge >= STANDARD_CELL_CHARGE || holder.chem_temp >= 474) + reagent_explode(holder, volume, modifier = 5, strengthdiv = 10, clear_holder_reagents = FALSE) + return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + + holder.my_atom.visible_message(span_boldnotice("Sparks start flying around the gunpowder!")) + if (!enclosed) + do_sparks(2, TRUE, get_turf(holder.my_atom)) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reagent_explode), holder, volume, 5, 10), rand(5 SECONDS, 10 SECONDS)) + return SPARK_ACT_NON_DESTRUCTIVE // just wait a bit... + /datum/reagent/rdx name = "RDX" description = "Military grade explosive" @@ -127,6 +160,17 @@ taste_description = "salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/rdx/on_spark_act(power_charge, enclosed) + if (power_charge) + // Okay but what if we made a REALLY big boom? + var/power_coeff = log(2, power_charge / STANDARD_CELL_CHARGE) + reagent_explode(holder, volume, modifier = min(2 * power_coeff, 8), strengthdiv = 7 / min(power_coeff, 4), clear_holder_reagents = FALSE) + else if (holder.chem_temp >= 474) + reagent_explode(holder, volume, modifier = 2, strengthdiv = 7, clear_holder_reagents = FALSE) + else + reagent_explode(holder, volume, strengthdiv = 8, clear_holder_reagents = FALSE) + return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + /datum/reagent/tatp name = "TaTP" description = "Suicide grade explosive" @@ -134,6 +178,10 @@ taste_description = "death" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/tatp/on_spark_act(power_charge, enclosed) + reagent_explode(holder, volume, strengthdiv = 1.5 + rand() * 1.5, clear_holder_reagents = FALSE) + return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + /datum/reagent/flash_powder name = "Flash Powder" description = "Makes a very bright flash." @@ -141,6 +189,26 @@ taste_description = "salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/flash_powder/on_spark_act(power_charge, enclosed) + // Even weaker version of the normal flash effect + var/turf/location = get_turf(holder.my_atom) + if (!enclosed) + do_sparks(2, TRUE, location) + var/range = round(volume / 15, 1) + holder.my_atom.flash_lighting_fx(range = (range + 2)) + for(var/mob/living/victim in get_hearers_in_view(range, location)) + if(!victim.flash_act(affect_silicon = TRUE)) + continue + var/distance = get_dist(location, victim) + if(distance <= 4 || issilicon(victim)) + victim.Paralyze(max(2 SECONDS / max(1, distance), 0.5 SECONDS)) + victim.Knockdown(max(20 SECONDS / max(1, distance), 6 SECONDS)) + else + victim.adjust_dizzy_up_to(max(20 SECONDS / max(1, distance), 0.5 SECONDS), 20 SECONDS) + victim.dropItemToGround(victim.get_active_held_item()) + victim.dropItemToGround(victim.get_inactive_held_item()) + return SPARK_ACT_NON_DESTRUCTIVE + /datum/reagent/smoke_powder name = "Smoke Powder" description = "Makes a large cloud of smoke that can carry reagents." @@ -148,6 +216,23 @@ taste_description = "smoke" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/smoke_powder/on_spark_act(power_charge, enclosed) + // Can't really make a cloud of smoke if we're inside of an enclosed container + // ...unless we're a mob, in which case this is pretty cursed + if (enclosed && !ismob(holder.my_atom)) + return + var/location = get_turf(holder.my_atom) + var/datum/effect_system/fluid_spread/smoke/chem/smoke_system = new() + playsound(location, 'sound/effects/smoke.ogg', 50, TRUE, -3) + if (iscarbon(holder.my_atom)) + var/mob/living/carbon/victim = holder.my_atom + if (victim.stat != DEAD) + victim.visible_message(span_warning("[victim] starts violently coughing up smoke!")) + victim.adjust_organ_loss(ORGAN_SLOT_LUNGS, volume / 15) + smoke_system.set_up(amount = volume / 1.5, holder = holder.my_atom, location = location, carry = holder, silent = FALSE) + smoke_system.start(log = TRUE) + return SPARK_ACT_NON_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + /datum/reagent/sonic_powder name = "Sonic Powder" description = "Makes a deafening noise." @@ -155,6 +240,14 @@ taste_description = "loud noises" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED +/datum/reagent/sonic_powder/on_spark_act(power_charge, enclosed) + // Even weaker version of the normal flash effect + var/turf/location = get_turf(holder.my_atom) + var/range = round(volume / 15, 1) + for(var/mob/living/victim in get_hearers_in_view(range, location)) + victim.soundbang_act(1, 10 SECONDS, rand(0, 0.5 SECONDS)) + return SPARK_ACT_NON_DESTRUCTIVE + /datum/reagent/phlogiston name = "Phlogiston" description = "Catches you on fire and makes you ignite." @@ -166,7 +259,7 @@ /datum/reagent/phlogiston/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0) . = ..() exposed_mob.adjust_fire_stacks(1) - var/burndmg = max(0.3*exposed_mob.fire_stacks * (1 - touch_protection), 0.3) + var/burndmg = max(0.3 * exposed_mob.fire_stacks * (1 - touch_protection), 0.3) if(burndmg) exposed_mob.adjust_fire_loss(burndmg, 0) exposed_mob.ignite_mob() @@ -177,6 +270,18 @@ if(metabolizer.adjust_fire_loss(0.15 * max(metabolizer.fire_stacks, 0.15) * metabolization_ratio * seconds_per_tick, updating_health = FALSE)) return UPDATE_MOB_HEALTH +/datum/reagent/phlogiston/on_spark_act(power_charge, enclosed) + if (enclosed && !ismob(holder.my_atom)) + if (!holder.my_atom.uses_integrity) + return + // Spicy! + holder.my_atom.take_damage(volume / 3, BURN, FIRE, FALSE) + return SPARK_ACT_NON_DESTRUCTIVE | SPARK_ACT_KEEP_REAGENT + + var/turf/our_turf = get_turf(holder.my_atom) + our_turf?.hotspot_expose(holder.chem_temp * 50, volume) + return SPARK_ACT_NON_DESTRUCTIVE + /datum/reagent/napalm name = "Napalm" description = "Very flammable." @@ -201,7 +306,19 @@ /datum/reagent/napalm/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume) . = ..() if(istype(exposed_mob) && (methods & (TOUCH|VAPOR|PATCH))) - exposed_mob.adjust_fire_stacks(min(reac_volume/4, 20)) + exposed_mob.adjust_fire_stacks(min(reac_volume / 4, 20)) + +/datum/reagent/napalm/on_spark_act(power_charge, enclosed) + if (enclosed && !ismob(holder.my_atom)) + if (!holder.my_atom.uses_integrity) + return + // Spicy! + holder.my_atom.take_damage(volume / 5, BURN, FIRE, FALSE) + return SPARK_ACT_NON_DESTRUCTIVE | SPARK_ACT_KEEP_REAGENT + + var/turf/our_turf = get_turf(holder.my_atom) + our_turf?.hotspot_expose(round(holder.chem_temp * 30 * (1 + sqrt(power_charge / STANDARD_CELL_CHARGE)), 1), volume) + return SPARK_ACT_NON_DESTRUCTIVE #define CRYO_SPEED_PREFACTOR 0.4 #define CRYO_SPEED_CONSTANT 0.1 @@ -328,6 +445,11 @@ var/mob/living/carbon/human/affected_human = affected_mob affected_human.physiology.siemens_coeff *= 0.5 +/datum/reagent/teslium/on_spark_act(power_charge, enclosed) + tesla_zap(source = holder.my_atom, zap_range = round(volume / 5, 1), power = volume * 20 + power_charge, cutoff = 1 KILO JOULES, zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN | ZAP_LOW_POWER_GEN) + playsound(holder.my_atom, 'sound/machines/defib/defib_zap.ogg', 50, TRUE) + return SPARK_ACT_NON_DESTRUCTIVE + /datum/reagent/teslium/energized_jelly name = "Energized Jelly" description = "Electrically-charged jelly. Boosts jellypeople's nervous system, but only shocks other lifeforms." diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 1c49c7bd06b..0d6988abb69 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -81,6 +81,7 @@ #define LIQUID_PLASMA_BP (50+T0C) #define LIQUID_PLASMA_IG (325+T0C) +#define LIQUID_PLASMA_CHARGE_COEFF 2.7 /datum/reagent/toxin/plasma name = "Plasma" @@ -141,15 +142,30 @@ exposed_turf.atmos_spawn_air("[GAS_PLASMA]=[reac_volume];[TURF_TEMPERATURE(temp)]") return ..() -#undef LIQUID_PLASMA_BP -#undef LIQUID_PLASMA_IG -/datum/reagent/toxin/plasma/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume)//Splashing people with plasma is stronger than fuel! +// Splashing people with plasma is stronger than fuel! +/datum/reagent/toxin/plasma/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume) . = ..() if(methods & (TOUCH|VAPOR)) exposed_mob.adjust_fire_stacks(reac_volume / 5) return +/datum/reagent/toxin/plasma/on_spark_act(power_charge, enclosed) + // If we have any stabilizing agent in the mix, we need 0.2% of a standard cell value per mol of agent to be spent at once to blow + // This should allow for some more creative traps to be made with plasma + var/agent_volume = holder.get_reagent_amount(/datum/reagent/stabilizing_agent) + if (agent_volume && power_charge < agent_volume * 0.002 * STANDARD_CELL_CHARGE) + return NONE + + // Plasma explosions become stronger with higher current, and don't care about if they're enclosed or not + var/power_modifier = max(0, round(power_charge / STANDARD_CELL_CHARGE * LIQUID_PLASMA_CHARGE_COEFF, 1) - 1) + reagent_explode(holder, volume, modifier = power_modifier, strengthdiv = 5, clear_holder_reagents = FALSE) + return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL + +#undef LIQUID_PLASMA_BP +#undef LIQUID_PLASMA_IG +#undef LIQUID_PLASMA_CHARGE_COEFF + /datum/reagent/toxin/hot_ice name = "Hot Ice Slush" description = "Frozen plasma, worth its weight in gold, to the right people." diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index c6ecac2eb64..e07c14d0484 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -278,52 +278,68 @@ * * arguments: * * holder - the reagents datum that it is being used on - * * created_volume - the volume of reacting elements + * * volume - the volume of reacting elements * * modifier - a flat additive numeric to the size of the explosion - set this if you want a minimum range * * strengthdiv - the divisional factor of the explosion, a larger number means a smaller range - This is the part that modifies an explosion's range with volume (i.e. it divides it by this number) + * * clear_mob_reagents - should our mob be purged of their reagents? + * * clear_holder_reagents - should the reagent holder be fully purged? Heavily recommended to be TRUE, unless you have code handling that elsewhere down the line for whatever reason + * * flame_factor - multiplier to flame range of the explosion + * * flash_factor - multiplier to flash range of the explosion */ -/datum/chemical_reaction/proc/default_explode(datum/reagents/holder, created_volume, modifier = 0, strengthdiv = 10, clear_mob_reagents) - var/power = modifier + round(created_volume/strengthdiv, 1) - if(power > 0) - var/turf/T = get_turf(holder.my_atom) +/proc/reagent_explode(datum/reagents/holder, volume, modifier = 0, strengthdiv = 10, clear_mob_reagents = FALSE, clear_holder_reagents = TRUE, flash_factor = null, flame_factor = null) + if (QDELETED(holder)) + return + + var/power = modifier + round(volume / strengthdiv, 1) + if (power > 0) + var/turf/our_turf = get_turf(holder.my_atom) var/inside_msg - if(ismob(holder.my_atom)) - var/mob/M = holder.my_atom - inside_msg = " inside [ADMIN_LOOKUPFLW(M)]" + if (ismob(holder.my_atom)) + var/mob/owner = holder.my_atom + inside_msg = " inside [ADMIN_LOOKUPFLW(owner)]" var/lastkey = holder.my_atom.fingerprintslast //This can runtime (null.fingerprintslast) - due to plumbing? var/touch_msg = "N/A" - if(lastkey) + if (lastkey) var/mob/toucher = get_mob_by_key(lastkey) touch_msg = "[ADMIN_LOOKUPFLW(toucher)]" - if(!istype(holder.my_atom, /obj/machinery/plumbing)) //excludes standard plumbing equipment from spamming admins with this shit - message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].") - log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." ) - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(power , T, 0, 0) - e.start(holder.my_atom) - if (ismob(holder.my_atom)) - if(!clear_mob_reagents) - return - // Only clear reagents if they use a special explosive reaction to do it; it shouldn't apply - // to any explosion inside a person - holder.clear_reagents() - if(iscarbon(holder.my_atom)) - var/mob/living/carbon/victim = holder.my_atom - var/vomit_flags = MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE - // The vomiting here is for effect, not meant to help with purging - victim.vomit(vomit_flags, distance = 5) - // Not quite the same if the reaction is in their stomach; they'll throw up - // from any explosion, but it'll only make them puke up everything in their - // stomach - else if (istype(holder.my_atom, /obj/item/organ/stomach)) + if (!istype(holder.my_atom, /obj/machinery/plumbing)) //excludes standard plumbing equipment from spamming admins with this shit + message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(our_turf)][inside_msg]. Last Fingerprint: [touch_msg].") + log_game("Reagent explosion reaction occurred at [AREACOORD(our_turf)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." ) + var/datum/effect_system/reagents_explosion/explosion_system = new() + explosion_system.set_up(power, our_turf, flash_fact = flash_factor, flame_fact = flame_factor) + explosion_system.start(holder.my_atom) + + if (istype(holder.my_atom, /obj/item/organ/stomach)) var/obj/item/organ/stomach/indigestion = holder.my_atom if(power < 1) return - indigestion.owner?.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE, lost_nutrition = 150, distance = 5, purge_ratio = 1) + if (clear_mob_reagents) + indigestion.owner?.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE, lost_nutrition = 150, distance = 5, purge_ratio = 1) + if (clear_holder_reagents) + holder.clear_reagents() + return + + if (!ismob(holder.my_atom)) holder.clear_reagents() return - else + + // Not quite the same if the reaction is in their stomach; they'll throw up + // from any explosion, but it'll only make them puke up everything in their + // stomach + if (!clear_mob_reagents) + return + + // Only clear reagents if they use a special explosive reaction to do it; it shouldn't apply + // to any explosion inside a person + if (clear_holder_reagents) holder.clear_reagents() + + if (iscarbon(holder.my_atom)) + var/mob/living/carbon/victim = holder.my_atom + var/vomit_flags = MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE + // The vomiting here is for effect, not meant to help with purging + victim.vomit(vomit_flags, distance = 5) + /* *Creates a flash effect only - less expensive than explode() * diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm index ad73329cf15..f4f89f6af36 100644 --- a/code/modules/reagents/chemistry/recipes/drugs.dm +++ b/code/modules/reagents/chemistry/recipes/drugs.dm @@ -73,7 +73,7 @@ message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].") log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." ) var/datum/effect_system/reagents_explosion/e = new() - e.set_up(power, T, 0, 0) + e.set_up(power, T) e.start(holder.my_atom) holder.clear_reagents() diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 66cc645924c..9ebad8f2900 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -222,10 +222,10 @@ purity_min = 0.32 /datum/chemical_reaction/medicine/ephedrine/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) - default_explode(holder, equilibrium.reacted_vol, 0, 25) + reagent_explode(holder, equilibrium.reacted_vol, 0, 25) /datum/chemical_reaction/medicine/ephedrine/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) - default_explode(holder, equilibrium.reacted_vol, 0, 20) + reagent_explode(holder, equilibrium.reacted_vol, 0, 20) /datum/chemical_reaction/medicine/diphenhydramine results = list(/datum/reagent/medicine/diphenhydramine = 4) diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index 3e3e7d3e3e9..876bfc6d78e 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -17,30 +17,39 @@ var/clear_mob_reagents = FALSE /datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume, clear_mob_reagents) + if (!ismob(holder.my_atom)) + reagent_explode(holder, created_volume, modifier, strengthdiv, clear_mob_reagents) + return + // If an explosive reaction clears mob reagents, it should always be a minimum power - if(ismob(holder.my_atom) && clear_mob_reagents) + if(clear_mob_reagents) if(round((created_volume / strengthdiv) + modifier, 1) < 1) modifier += 1 - ((created_volume / strengthdiv) + modifier) + reagent_explode(holder, created_volume, modifier, strengthdiv, clear_mob_reagents) + return + // If this particular explosion doesn't automatically clear mob reagents as an inherent quality, // then we can still clear mob reagents with some mad science malpractice that shouldn't work but // does because omnizine is magic and also it's the future or whatever - if(ismob(holder.my_atom) && !clear_mob_reagents) - // The explosion needs to be a minimum power to clear reagents: see above - var/purge_power = round((created_volume / strengthdiv) + modifier, 1) - if(purge_power >= 1) - var/has_purging_chemical = FALSE - // They need one of the purge reagents in them - for(var/purging_chem in PURGING_REAGENTS) - if(holder.has_reagent(purging_chem)) - // We have a purging chemical - has_purging_chemical = TRUE - break - // Then we need omnizine! MAGIC! - var/has_omnizine = holder.has_reagent(/datum/reagent/medicine/omnizine) - if(has_purging_chemical && has_omnizine) - // With all this medical "science" combined, we can clear mob reagents - clear_mob_reagents = TRUE - default_explode(holder, created_volume, modifier, strengthdiv, clear_mob_reagents) + // The explosion needs to be a minimum power to clear reagents: see above + var/purge_power = round((created_volume / strengthdiv) + modifier, 1) + if(purge_power < 1) + reagent_explode(holder, created_volume, modifier, strengthdiv, clear_mob_reagents) + return + + var/has_purging_chemical = FALSE + // They need one of the purge reagents in them + for(var/purging_chem in PURGING_REAGENTS) + if(holder.has_reagent(purging_chem)) + // We have a purging chemical + has_purging_chemical = TRUE + break + // Then we need omnizine! MAGIC! + var/has_omnizine = holder.has_reagent(/datum/reagent/medicine/omnizine) + if(has_purging_chemical && has_omnizine) + // With all this medical "science" combined, we can clear mob reagents + clear_mob_reagents = TRUE + reagent_explode(holder, created_volume, modifier, strengthdiv, clear_mob_reagents) #undef PURGING_REAGENTS /datum/chemical_reaction/reagent_explosion/nitroglycerin @@ -62,7 +71,7 @@ /datum/chemical_reaction/reagent_explosion/rdx results = list(/datum/reagent/rdx= 2) - required_reagents = list(/datum/reagent/phenol = 2, /datum/reagent/toxin/acid/nitracid = 1, /datum/reagent/acetone_oxide = 1 ) + required_reagents = list(/datum/reagent/phenol = 2, /datum/reagent/toxin/acid/nitracid = 1, /datum/reagent/acetone_oxide = 1) required_catalysts = list(/datum/reagent/gold) //royal explosive required_temp = 404 strengthdiv = 8 @@ -199,7 +208,8 @@ mix_message = span_boldnotice("Sparks start flying around the gunpowder!") /datum/chemical_reaction/reagent_explosion/gunpowder_explosion/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) - addtimer(CALLBACK(src, PROC_REF(default_explode), holder, created_volume, modifier, strengthdiv), rand(5 SECONDS, 10 SECONDS)) + do_sparks(2, TRUE, get_turf(holder.my_atom)) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reagent_explode), holder, created_volume, modifier, strengthdiv), rand(5 SECONDS, 10 SECONDS)) /datum/chemical_reaction/thermite results = list(/datum/reagent/thermite = 3) @@ -391,6 +401,12 @@ S.start(log = TRUE) if(holder?.my_atom) holder.clear_reagents() + if (!iscarbon(holder?.my_atom)) + return + var/mob/living/carbon/victim = holder.my_atom + if (victim.stat != DEAD) + victim.visible_message(span_warning("[victim] starts violently coughing up smoke!")) + victim.adjust_organ_loss(ORGAN_SLOT_LUNGS, created_volume / 5) /datum/chemical_reaction/smoke_powder_smoke required_reagents = list(/datum/reagent/smoke_powder = 1) @@ -409,6 +425,14 @@ S.start(log = TRUE) if(holder?.my_atom) holder.clear_reagents() + if(holder?.my_atom) + holder.clear_reagents() + if (!iscarbon(holder?.my_atom)) + return + var/mob/living/carbon/victim = holder.my_atom + if (victim.stat != DEAD) + victim.visible_message(span_warning("[victim] starts violently coughing up smoke!")) + victim.adjust_organ_loss(ORGAN_SLOT_LUNGS, created_volume / 10) /datum/chemical_reaction/sonic_powder results = list(/datum/reagent/sonic_powder = 3) @@ -603,7 +627,7 @@ added_delay += 1.5 SECONDS if(created_volume >= 10) //10 units minimum for lightning, 40 units for secondary blast, 75 units for tertiary blast. addtimer(CALLBACK(src, PROC_REF(zappy_zappy), holder, T3), added_delay) - addtimer(CALLBACK(src, PROC_REF(default_explode), holder, created_volume, modifier, strengthdiv), added_delay) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reagent_explode), holder, created_volume, modifier, strengthdiv), added_delay) /datum/chemical_reaction/reagent_explosion/teslium_lightning/proc/zappy_zappy(datum/reagents/holder, power) var/atom/holder_atom = holder.my_atom diff --git a/code/modules/research/part_replacer.dm b/code/modules/research/part_replacer.dm index fea7c8211d0..2ac29877ead 100644 --- a/code/modules/research/part_replacer.dm +++ b/code/modules/research/part_replacer.dm @@ -90,10 +90,10 @@ if(istype(inserted_component, /obj/item/stock_parts/power_store)) var/obj/item/stock_parts/power_store/inserted_cell = inserted_component - if(inserted_cell.rigged || inserted_cell.corrupted) - message_admins("[ADMIN_LOOKUPFLW(usr)] has inserted rigged/corrupted [inserted_cell] into [src].") - usr.log_message("has inserted rigged/corrupted [inserted_cell] into [src].", LOG_GAME) - usr.log_message("inserted rigged/corrupted [inserted_cell] into [src]", LOG_ATTACK) + if(inserted_cell.corrupted) + message_admins("[ADMIN_LOOKUPFLW(usr)] has inserted corrupted [inserted_cell] into [src].") + usr.log_message("has inserted corrupted [inserted_cell] into [src].", LOG_GAME) + usr.log_message("inserted corrupted [inserted_cell] into [src]", LOG_ATTACK) return var/datum/reagents/target_holder = inserted_component.reagents