From e043a3a1f7f677a128bfdb985148b1766bd2e41c Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 21 Mar 2021 14:58:18 +0100 Subject: [PATCH] [MIRROR] Desnowflakes tank integrity (#4286) * Desnowflakes tank integrity (#56443) Makes tanks use obj_integrity instead of their own snowflaked version. Makes tanks check for exploding when they are destroyed, rather than once every process. Makes tanks always leak their gases when they are deconstructed. Removes the ability for tanks to seal themselves back up over time. Makes the bomb spawner actually produce functional bombs. Removes the extraneous syndicate bomb spawner subtype. Miscellaneous code improvements to tanks, bomb spawners, and the blastcannon. Fixes the explosives compressor doubling the power of any bomb you put in it. The changes to tank rupturing behavior shouldn't effect most tritium fueled TTVs including the 50K recipe. Toxins players don't need to worry about suddenly being incapable of getting points or refining anomaly cores. They should only really effect singlecaps, but I don't know enough about singlecaps to know what recipes I should test. I have confirmation that at least one mix is not effected by this. The self-sealing properties of tanks have been removed. I'm not sure what the purpose of it was, I have heard that it was used to enable hand-portable plasmaflooding, but I'm not familiar with the practice. As it turns out, the basic bomb spawners were broken on master! I have made the bombs they produce maxcap instead of just spring a leak. Since they maxcap by default now I have removed the syndicate subtype used to spawn the TTV produced by the cuban pete arcade game and replaced it with the normal timer subtype. Since none of the bomb spawner subtypes were used for anything else this shouldn't have any effect on the game. On a similar note, I have discovered that the maxcap recipe on the wiki stopped working at some point since it was written. I will replace it with a functioning set of instructions. Less snowflake code. Bomb spawners are actually functional now. Slightly better code. The explosives compressor accurately reflects the power of the bomb you put into it. * Desnowflakes tank integrity Co-authored-by: TemporalOroboros --- code/game/machinery/computer/arcade/arcade.dm | 2 +- .../objects/effects/spawners/bombspawner.dm | 74 ++++---- code/game/objects/items.dm | 2 +- code/game/objects/items/tanks/tanks.dm | 164 ++++++++++-------- .../projectiles/guns/misc/blastcannon.dm | 5 +- .../research/anomaly/explosive_compressor.dm | 25 ++- 6 files changed, 144 insertions(+), 128 deletions(-) diff --git a/code/game/machinery/computer/arcade/arcade.dm b/code/game/machinery/computer/arcade/arcade.dm index 74ad9b711e3..51b623e91bf 100644 --- a/code/game/machinery/computer/arcade/arcade.dm +++ b/code/game/machinery/computer/arcade/arcade.dm @@ -542,7 +542,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( playsound(loc, 'sound/arcade/win.ogg', 50, TRUE) if(obj_flags & EMAGGED) - new /obj/effect/spawner/newbomb/timer/syndicate(loc) + new /obj/effect/spawner/newbomb/timer(loc) new /obj/item/clothing/head/collectable/petehat(loc) message_admins("[ADMIN_LOOKUPFLW(usr)] has outbombed Cuban Pete and been awarded a bomb.") log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.") diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index 67b09b786fb..65f32aadcbf 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -1,67 +1,57 @@ -#define CELSIUS_TO_KELVIN(T_K) ((T_K) + T0C) - -#define OPTIMAL_TEMP_K_PLA_BURN_SCALE(PRESSURE_P,PRESSURE_O,TEMP_O) (((PRESSURE_P) * GLOB.meta_gas_info[/datum/gas/plasma][META_GAS_SPECIFIC_HEAT]) / (((PRESSURE_P) * GLOB.meta_gas_info[/datum/gas/plasma][META_GAS_SPECIFIC_HEAT] + (PRESSURE_O) * GLOB.meta_gas_info[/datum/gas/oxygen][META_GAS_SPECIFIC_HEAT]) / PLASMA_UPPER_TEMPERATURE - (PRESSURE_O) * GLOB.meta_gas_info[/datum/gas/oxygen][META_GAS_SPECIFIC_HEAT] / CELSIUS_TO_KELVIN(TEMP_O))) -#define OPTIMAL_TEMP_K_PLA_BURN_RATIO(PRESSURE_P,PRESSURE_O,TEMP_O) (CELSIUS_TO_KELVIN(TEMP_O) * PLASMA_OXYGEN_FULLBURN * (PRESSURE_P) / (PRESSURE_O)) - +/** + * Spawns a TTV. + * + */ /obj/effect/spawner/newbomb name = "bomb" icon = 'icons/hud/screen_gen.dmi' icon_state = "x" - var/temp_p = 1500 - var/temp_o = 1000 // tank temperatures - var/pressure_p = 10 * ONE_ATMOSPHERE - var/pressure_o = 10 * ONE_ATMOSPHERE //tank pressures + /// The initial temperature of the plasma tank. + var/temp_p = 1413 + /// The initial temperature of the oxygen tank. + var/temp_o = 141.3 + /// The initial pressure of the plasma tank. + var/pressure_p = TANK_LEAK_PRESSURE - 1 + /// The initial pressure of the oxygen tank. + var/pressure_o = TANK_LEAK_PRESSURE - 1 + /// The typepath of the assembly to attach to the TTV. var/assembly_type /obj/effect/spawner/newbomb/Initialize() . = ..() - var/obj/item/transfer_valve/V = new(src.loc) - var/obj/item/tank/internals/plasma/PT = new(V) - var/obj/item/tank/internals/oxygen/OT = new(V) + var/obj/item/transfer_valve/ttv = new(loc) + var/obj/item/tank/internals/plasma/plasma_tank = new(ttv) + var/obj/item/tank/internals/oxygen/oxygen_tank = new(ttv) - PT.air_contents.assert_gas(/datum/gas/plasma) - PT.air_contents.gases[/datum/gas/plasma][MOLES] = pressure_p*PT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_p)) - PT.air_contents.temperature = CELSIUS_TO_KELVIN(temp_p) + var/datum/gas_mixture/plasma_mix = plasma_tank.air_contents + plasma_mix.assert_gas(/datum/gas/plasma) + plasma_mix.gases[/datum/gas/plasma][MOLES] = pressure_p*plasma_mix.volume/(R_IDEAL_GAS_EQUATION*temp_p) + plasma_mix.temperature = temp_p - OT.air_contents.assert_gas(/datum/gas/oxygen) - OT.air_contents.gases[/datum/gas/oxygen][MOLES] = pressure_o*OT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_o)) - OT.air_contents.temperature = CELSIUS_TO_KELVIN(temp_o) + var/datum/gas_mixture/oxygen_mix = oxygen_tank.air_contents + oxygen_mix.assert_gas(/datum/gas/oxygen) + oxygen_mix.gases[/datum/gas/oxygen][MOLES] = pressure_o*oxygen_mix.volume/(R_IDEAL_GAS_EQUATION*temp_o) + oxygen_mix.temperature = temp_o - V.tank_one = PT - V.tank_two = OT - PT.master = V - OT.master = V + ttv.tank_one = plasma_tank + ttv.tank_two = oxygen_tank + plasma_tank.master = ttv + oxygen_tank.master = ttv if(assembly_type) - var/obj/item/assembly/A = new assembly_type(V) - V.attached_device = A - A.holder = V + var/obj/item/assembly/detonator = new assembly_type(ttv) + ttv.attached_device = detonator + detonator.holder = ttv - V.update_appearance() + ttv.update_appearance() return INITIALIZE_HINT_QDEL -/obj/effect/spawner/newbomb/timer/syndicate/Initialize() - temp_p = (OPTIMAL_TEMP_K_PLA_BURN_SCALE(pressure_p, pressure_o, temp_o)/2 + OPTIMAL_TEMP_K_PLA_BURN_RATIO(pressure_p, pressure_o, temp_o)/2) - T0C - . = ..() - /obj/effect/spawner/newbomb/timer assembly_type = /obj/item/assembly/timer -/obj/effect/spawner/newbomb/timer/syndicate - pressure_o = TANK_LEAK_PRESSURE - 1 - temp_o = 20 - - pressure_p = TANK_LEAK_PRESSURE - 1 - /obj/effect/spawner/newbomb/proximity assembly_type = /obj/item/assembly/prox_sensor /obj/effect/spawner/newbomb/radio assembly_type = /obj/item/assembly/signaler - - -#undef CELSIUS_TO_KELVIN - -#undef OPTIMAL_TEMP_K_PLA_BURN_SCALE -#undef OPTIMAL_TEMP_K_PLA_BURN_RATIO diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 5e0475fdc32..8cecf0c3440 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -232,7 +232,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb /obj/item/blob_act(obj/structure/blob/B) if(B && B.loc == loc) - qdel(src) + obj_destruction(MELEE) /obj/item/ComponentInitialize() . = ..() diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm index 9d70490febb..cc645e6e3e3 100644 --- a/code/game/objects/items/tanks/tanks.dm +++ b/code/game/objects/items/tanks/tanks.dm @@ -1,3 +1,12 @@ +/// How much time (in seconds) is assumed to pass while assuming air. Used to scale overpressure/overtemp damage when assuming air. +#define ASSUME_AIR_DT_FACTOR 1 + +/** + * # Gas Tank + * + * Handheld gas canisters + * Can rupture explosively if overpressurized + */ /obj/item/tank name = "tank" icon = 'icons/obj/tank.dmi' @@ -16,10 +25,15 @@ custom_materials = list(/datum/material/iron = 500) actions_types = list(/datum/action/item_action/set_internals) armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 10, BIO = 0, RAD = 0, FIRE = 80, ACID = 30) + integrity_failure = 0.5 + /// The gases this tank contains. var/datum/gas_mixture/air_contents = null - var/distribute_pressure = ONE_ATMOSPHERE - var/integrity = 3 + /// The volume of this tank. var/volume = 70 + /// Whether the tank is currently leaking. + var/leaking = FALSE + /// The pressure of the gases this tank supplies to internals. + var/distribute_pressure = ONE_ATMOSPHERE /// Icon state when in a tank holder. Null makes it incompatible with tank holder. var/tank_holder_icon_state = "holder_generic" @@ -74,7 +88,7 @@ QDEL_NULL(air_contents) STOP_PROCESSING(SSobj, src) - . = ..() + return ..() /obj/item/tank/ComponentInitialize() . = ..() @@ -111,25 +125,13 @@ . += "It feels [descriptive]." -/obj/item/tank/blob_act(obj/structure/blob/B) - if(B && B.loc == loc) - var/turf/location = get_turf(src) - if(!location) - qdel(src) - - if(air_contents) - location.assume_air(air_contents) - - qdel(src) - /obj/item/tank/deconstruct(disassembled = TRUE) - if(!disassembled) - var/turf/T = get_turf(src) - if(T) - T.assume_air(air_contents) - air_update_turf(FALSE, FALSE) - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - qdel(src) + var/turf/location = get_turf(src) + if(location) + location.assume_air(air_contents) + location.air_update_turf(FALSE, FALSE) + playsound(location, 'sound/effects/spray.ogg', 10, TRUE, -3) + return ..() /obj/item/tank/suicide_act(mob/user) var/mob/living/carbon/human/H = user @@ -146,9 +148,9 @@ /obj/item/tank/attackby(obj/item/W, mob/user, params) add_fingerprint(user) if(istype(W, /obj/item/assembly_holder)) - bomb_assemble(W,user) - else - . = ..() + bomb_assemble(W, user) + return TRUE + return ..() /obj/item/tank/ui_state(mob/user) return GLOB.hands_state @@ -213,10 +215,15 @@ /obj/item/tank/assume_air(datum/gas_mixture/giver) air_contents.merge(giver) + handle_tolerances(ASSUME_AIR_DT_FACTOR) + return TRUE - check_status() - return 1 - +/** + * Removes some volume of the tanks gases as the tanks distribution pressure. + * + * Arguments: + * - volume_to_return: The amount of volume to remove from the tank. + */ /obj/item/tank/proc/remove_air_volume(volume_to_return) if(!air_contents) return null @@ -235,59 +242,78 @@ return remove_air(moles_needed) -/obj/item/tank/process() - //Allow for reactions - air_contents.react() - check_status() - -/obj/item/tank/proc/check_status() - //Handle exploding, leaking, and rupturing of the tank - +/obj/item/tank/process(delta_time) if(!air_contents) - return 0 + return + + //Allow for reactions + air_contents.react(src) + handle_tolerances(delta_time) + if(QDELETED(src) || !leaking || !air_contents) + return + var/turf/location = get_turf(src) + if(!location) + return + var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25) + location.assume_air(leaked_gas) + location.air_update_turf(FALSE, FALSE) + +/** + * Handles the minimum and maximum pressure tolerances of the tank. + * + * Arguments: + * - delta_time: How long has passed between ticks. + */ +/obj/item/tank/proc/handle_tolerances(delta_time) + if(!air_contents) + return FALSE var/pressure = air_contents.return_pressure() var/temperature = air_contents.return_temperature() + if(temperature >= TANK_MELT_TEMPERATURE) + var/temperature_damage_ratio = (temperature - TANK_MELT_TEMPERATURE) / temperature + take_damage(max_integrity * temperature_damage_ratio * delta_time, BURN, FIRE, FALSE, NONE) + if(QDELETED(src)) + return TRUE + if(pressure >= TANK_LEAK_PRESSURE) + var/pressure_damage_ratio = (pressure - TANK_LEAK_PRESSURE) / (TANK_RUPTURE_PRESSURE - TANK_LEAK_PRESSURE) + take_damage(max_integrity * pressure_damage_ratio * delta_time, BRUTE, BOMB, FALSE, NONE) + return TRUE + +/// Handles the tank springing a leak. +/obj/item/tank/obj_break(damage_flag) + . = ..() + if(leaking) + return + + leaking = TRUE + if(obj_integrity < 0) // So we don't play the alerts while we are exploding or rupturing. + return + visible_message("[src] springs a leak!") + playsound(src, 'sound/effects/spray.ogg', 10, TRUE, -3) + +/// Handles rupturing and fragmenting +/obj/item/tank/obj_destruction(damage_flag) + if(!air_contents) + return ..() + + var/turf/location = get_turf(src) + if(!location) + return ..() + + /// Handle fragmentation + var/pressure = air_contents.return_pressure() if(pressure > TANK_FRAGMENT_PRESSURE) - if(!istype(src.loc, /obj/item/transfer_valve)) + if(!istype(loc, /obj/item/transfer_valve)) log_bomber(get_mob_by_key(fingerprintslast), "was last key to touch", src, "which ruptured explosively") //Give the gas a chance to build up more pressure through reacting air_contents.react(src) pressure = air_contents.return_pressure() var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE - var/turf/epicenter = get_turf(loc) - - explosion(epicenter, round(range*0.25), round(range*0.5), round(range), round(range*1.5)) - if(istype(src.loc, /obj/item/transfer_valve)) - qdel(src.loc) - else - qdel(src) - - else if(pressure > TANK_RUPTURE_PRESSURE || temperature > TANK_MELT_TEMPERATURE) - if(integrity <= 0) - var/turf/T = get_turf(src) - if(!T) - return - T.assume_air(air_contents) - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - qdel(src) - else - integrity-- - - else if(pressure > TANK_LEAK_PRESSURE) - if(integrity <= 0) - var/turf/T = get_turf(src) - if(!T) - return - var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25) - T.assume_air(leaked_gas) - else - integrity-- - - else if(integrity < 3) - integrity++ + explosion(location, round(range*0.25), round(range*0.5), round(range), round(range*1.5)) + return ..() /obj/item/tank/rad_act(strength) . = ..() @@ -312,3 +338,5 @@ if(gas_change) air_contents.garbage_collect() + +#undef ASSUME_AIR_DT_FACTOR diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm index 51739ea171c..52a4cb622b7 100644 --- a/code/modules/projectiles/guns/misc/blastcannon.dm +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -69,7 +69,7 @@ if(!istype(bomb_to_attach)) return ..() - if(!bomb_to_attach.tank_one || !bomb_to_attach.tank_two) + if(!bomb_to_attach.ready()) to_chat(user, "What good would an incomplete bomb do?") return FALSE if(!user.transferItemToLoc(bomb_to_attach, src)) @@ -83,7 +83,7 @@ /// Handles the bomb power calculations /obj/item/gun/blastcannon/proc/calculate_bomb() - if(!istype(bomb) || !istype(bomb.tank_one) || !istype(bomb.tank_two)) + if(!istype(bomb) || !bomb.ready()) return 0 var/datum/gas_mixture/temp = new(max(reaction_volume_mod, 0)) @@ -98,7 +98,6 @@ temp.react(src) var/pressure = temp.return_pressure() - qdel(temp) if(pressure < TANK_FRAGMENT_PRESSURE) return 0 return ((pressure - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE) diff --git a/code/modules/research/anomaly/explosive_compressor.dm b/code/modules/research/anomaly/explosive_compressor.dm index faf01f1e0c1..5b5573f0f41 100644 --- a/code/modules/research/anomaly/explosive_compressor.dm +++ b/code/modules/research/anomaly/explosive_compressor.dm @@ -114,12 +114,8 @@ /obj/machinery/research/explosive_compressor/proc/do_implosion() var/required_radius = get_required_radius(inserted_core.anomaly_type) // By now, we should be sure that we have a core, a TTV, and that the TTV has both tanks in place. - var/datum/gas_mixture/mix1 = inserted_bomb.tank_one.air_contents - var/datum/gas_mixture/mix2 = inserted_bomb.tank_two.air_contents - // Snowflaked tank explosion - var/datum/gas_mixture/mix = new(70) // Standard tank volume, 70L - mix.merge(mix1) - mix.merge(mix2) + var/datum/gas_mixture/mix = new(0) + inserted_bomb.merge_gases(mix) mix.react() if(mix.return_pressure() < TANK_FRAGMENT_PRESSURE) // They failed so miserably we're going to give them their bomb back. @@ -128,18 +124,21 @@ inserted_core.forceMove(drop_location()) inserted_core = null say("Transfer valve resulted in negligible explosive power. Items ejected.") - return + return FALSE mix.react() // build more pressure - var/pressure = mix.return_pressure() - var/range = (pressure - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE - if(range < required_radius) - inserted_bomb.forceMove(src) - say("Resultant detonation failed to produce enough implosive power to compress [inserted_core]. Core ejected.") - return + + var/range = (mix.return_pressure() - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE QDEL_NULL(inserted_bomb) // bomb goes poof + if(range < required_radius) + say("Resultant detonation failed to produce enough implosive power to compress [inserted_core]. Core ejected.") + inserted_core.forceMove(drop_location()) + inserted_core = null + return FALSE + inserted_core.create_core(drop_location(), TRUE, TRUE) inserted_core = null say("Success. Resultant detonation has theoretical range of [range]. Required radius was [required_radius]. Core production complete.") + return TRUE #undef MAX_RADIUS_REQUIRED #undef MIN_RADIUS_REQUIRED