diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index 80413080462..5652653b79d 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -310,3 +310,9 @@ /// 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) + +// spark_flags values +/// We're reacting in an enclosed container +#define SPARK_ACT_ENCLOSED (1 << 0) +/// We're in a large container or something, so decrease the power of bootleg explosives like welding fuel +#define SPARK_ACT_WEAKEN_COMMON (1 << 1) diff --git a/code/datums/elements/food/dunkable.dm b/code/datums/elements/food/dunkable.dm index fa2a9cbbc1c..5491b5ca8da 100644 --- a/code/datums/elements/food/dunkable.dm +++ b/code/datums/elements/food/dunkable.dm @@ -19,16 +19,16 @@ /datum/element/dunkable/proc/get_dunked(obj/item/source, mob/user, atom/target, params) SIGNAL_HANDLER - if(target.reagents?.flags & DUNKABLE) // container should be a valid target for dunking - if(!target.is_drainable()) - to_chat(user, span_warning("[target] is unable to be dunked in!")) - return ITEM_INTERACT_BLOCKING - if(target.reagents.trans_to(source, dunk_amount, transferred_by = user)) //if reagents were transferred, show the message - to_chat(user, span_notice("You dunk \the [target] into \the [target].")) - return ITEM_INTERACT_SUCCESS - if(!target.reagents.total_volume) - to_chat(user, span_warning("[target] is empty!")) - else - to_chat(user, span_warning("[source] is full!")) + if(!target.is_dunkable()) // container should be a valid target for dunking + return NONE + if(!target.is_drainable()) + to_chat(user, span_warning("[target] is unable to be dunked in!")) return ITEM_INTERACT_BLOCKING - return NONE + if(target.reagents.trans_to(source, dunk_amount, transferred_by = user)) //if reagents were transferred, show the message + to_chat(user, span_notice("You dunk \the [target] into \the [target].")) + return ITEM_INTERACT_SUCCESS + if(!target.reagents.total_volume) + to_chat(user, span_warning("[target] is empty!")) + else + to_chat(user, span_warning("[source] is full!")) + return ITEM_INTERACT_BLOCKING diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index a5a22ec5347..cdaa2ea434c 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -410,6 +410,10 @@ /atom/proc/is_drainable() return reagents && (reagents.flags & DRAINABLE) +/// Can we dunk stuff into this container? +/atom/proc/is_dunkable() + return reagents && (reagents.flags & DUNKABLE) + /** Handles exposing this atom to a list of reagents. * * Sends COMSIG_ATOM_EXPOSE_REAGENTS diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index 7e008a750d3..03033b0f1d2 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -426,7 +426,7 @@ 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) + if(reagents?.spark_act(0, NONE, banned_reagents = /datum/reagent/flash_powder) & SPARK_ACT_DESTRUCTIVE) usr?.log_message("lit a rigged cigarette", LOG_VICTIM) qdel(src) return diff --git a/code/game/objects/items/lighter.dm b/code/game/objects/items/lighter.dm index 1bf94097f32..60a7bf5271e 100644 --- a/code/game/objects/items/lighter.dm +++ b/code/game/objects/items/lighter.dm @@ -245,7 +245,7 @@ if(!lit) return FALSE - if (reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) + if (reagents.spark_act(0, SPARK_ACT_ENCLOSED, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) qdel(src) return FALSE diff --git a/code/game/objects/items/tools/engineering/weldingtool.dm b/code/game/objects/items/tools/engineering/weldingtool.dm index ec2412ca835..a82218e845d 100644 --- a/code/game/objects/items/tools/engineering/weldingtool.dm +++ b/code/game/objects/items/tools/engineering/weldingtool.dm @@ -184,7 +184,7 @@ user.log_message("set [key_name(attacked_mob)] on fire with [src].", LOG_ATTACK) /obj/item/weldingtool/attack_self(mob/user) - if(reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) + if(reagents.spark_act(0, SPARK_ACT_ENCLOSED, 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) qdel(src) diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 017dbf49d6e..0be336ff584 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -593,6 +593,7 @@ Striking a noncultist, however, will tear their flesh."} lefthand_file = 'icons/mob/inhands/items/drinks_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi' list_reagents = list(/datum/reagent/fuel/unholywater = 50) + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/unholywater/Initialize(mapload) . = ..() diff --git a/code/modules/antagonists/heretic/items/eldritch_flask.dm b/code/modules/antagonists/heretic/items/eldritch_flask.dm index 02adc82d9fa..5ef62fa410b 100644 --- a/code/modules/antagonists/heretic/items/eldritch_flask.dm +++ b/code/modules/antagonists/heretic/items/eldritch_flask.dm @@ -6,6 +6,7 @@ icon = 'icons/obj/antags/eldritch.dmi' icon_state = "eldritch_flask" list_reagents = list(/datum/reagent/eldritch = 50) + can_lid = FALSE // Unique bottle that lets you instantly draw blood from a victim /obj/item/reagent_containers/cup/phylactery diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 74f2f748f1b..e9c57626bc6 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -125,7 +125,7 @@ /obj/item/assembly_holder/dropped(mob/user) . = ..() for(var/obj/item/assembly/assembly as anything in assemblies) - assembly.dropped() + assembly.dropped(user) /obj/item/assembly_holder/attack_hand(mob/living/user, list/modifiers)//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess . = ..() diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 11e037bf7ad..96404d02b22 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -38,14 +38,16 @@ if(location) location.hotspot_expose(heat, EXPOSED_VOLUME) if(holder) - SEND_SIGNAL(holder.loc, COMSIG_IGNITER_ACTIVATE) + SEND_SIGNAL(holder.loc, COMSIG_IGNITER_ACTIVATE, src) if(QDELETED(src)) return TRUE sparks.start() return TRUE /obj/item/assembly/igniter/attack_self(mob/user) - activate() + // Don't trigger when interacting with assemblies + if (!holder) + activate() add_fingerprint(user) /obj/item/assembly/igniter/ignition_effect(atom/A, mob/user) @@ -59,7 +61,7 @@ desc = "A small electronic device able to chill their surroundings." icon_state = "freezer" custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2.5, /datum/material/glass=SMALL_MATERIAL_AMOUNT * 3) - heat = 200 + heat = MIN_FREEZE_TEMP /obj/item/assembly/igniter/condenser/activate() . = ..() @@ -68,9 +70,8 @@ var/turf/location = get_turf(loc) if(location) var/datum/gas_mixture/enviro = location.return_air() - enviro.temperature = clamp(min(ROOM_TEMP, enviro.temperature*0.85),MIN_FREEZE_TEMP,MAX_FREEZE_TEMP) + enviro.temperature = clamp(min(ROOM_TEMP, enviro.temperature * 0.85), MIN_FREEZE_TEMP, MAX_FREEZE_TEMP) location.air_update_turf(FALSE, FALSE) - sparks.start() #undef EXPOSED_VOLUME #undef ROOM_TEMP diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index cbb50412f8d..68c10d9c6c4 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -240,7 +240,7 @@ color_set = color if(reagents || !is_full_charge()) START_PROCESSING(SSmachines, src) - if (reagents?.spark_act(active_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE) + if (reagents?.spark_act(active_power_usage, SPARK_ACT_ENCLOSED) & SPARK_ACT_DESTRUCTIVE) message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.") qdel(src) return @@ -279,7 +279,7 @@ 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) + if (reagents?.spark_act(idle_power_usage, SPARK_ACT_ENCLOSED) & SPARK_ACT_DESTRUCTIVE) message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.") qdel(src) return diff --git a/code/modules/power/power_store.dm b/code/modules/power/power_store.dm index 22539a5ed28..63b7a9ec8e1 100644 --- a/code/modules/power/power_store.dm +++ b/code/modules/power/power_store.dm @@ -206,7 +206,7 @@ return FALSE if (!corrupted) - if (!(reagents?.spark_act(check_charge, TRUE) & SPARK_ACT_DESTRUCTIVE)) + if (!(reagents?.spark_act(check_charge, SPARK_ACT_ENCLOSED) & 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) diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index a03c360e328..78e9c1bce79 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -792,10 +792,10 @@ * 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 + * * spark_flags - Set of flags describing the interaction * * 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) +/datum/reagents/proc/spark_act(power_charge, spark_flags, list/banned_reagents) if (!islist(banned_reagents)) banned_reagents = list(banned_reagents) var/result = NONE @@ -803,7 +803,7 @@ 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) + var/reagent_result = reagent.on_spark_act(power_charge, spark_flags) if (!reagent_result) continue result |= (reagent_result & SPARK_ACT_RETURNS) diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 273268f1c30..8a2dcaa0e53 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -242,9 +242,9 @@ * 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 + * * spark_flags - Flags specific to the interaction, is it in an enclosed space, should we nerf common reagents, etc. */ -/datum/reagent/proc/on_spark_act(power_charge = 0, enclosed = TRUE) +/datum/reagent/proc/on_spark_act(power_charge = 0, spark_flags = NONE) return NONE /** diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 70685934419..64609737852 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -1308,14 +1308,18 @@ if(pool) pool.burn_amount = max(min(round(reac_volume / 5), 10), 1) -/datum/reagent/fuel/on_spark_act(power_charge, enclosed) +/datum/reagent/fuel/on_spark_act(power_charge, spark_flags) // 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) + if (!(spark_flags & SPARK_ACT_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) + var/strengthdiv = 10 + if (spark_flags & SPARK_ACT_WEAKEN_COMMON) + strengthdiv *= 3 // Noticeably weaker than waterpot, at least put some effort in, cmon + + reagent_explode(holder, volume, strengthdiv = strengthdiv, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/space_cleaner diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index d2895ccb683..46c8e7bd112 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -29,8 +29,8 @@ 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) +/datum/reagent/nitroglycerin/on_spark_act(power_charge, spark_flags) + reagent_explode(holder, volume, strengthdiv = 2, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/stabilizing_agent @@ -89,7 +89,7 @@ taste_description = "air and bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/sorium/on_spark_act(power_charge, enclosed) +/datum/reagent/sorium/on_spark_act(power_charge, spark_flags) var/range = clamp(sqrt(volume), 1, 6) goonchem_vortex(get_turf(holder.my_atom), 1, range) return SPARK_ACT_NON_DESTRUCTIVE @@ -101,7 +101,7 @@ taste_description = "compressed bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/liquid_dark_matter/on_spark_act(power_charge, enclosed) +/datum/reagent/liquid_dark_matter/on_spark_act(power_charge, spark_flags) var/range = clamp(sqrt(volume / 2), 1, 6) goonchem_vortex(get_turf(holder.my_atom), 0, range) return SPARK_ACT_NON_DESTRUCTIVE @@ -134,7 +134,7 @@ e.start(holder.my_atom) holder.clear_reagents() -/datum/reagent/gunpowder/on_spark_act(power_charge, enclosed) +/datum/reagent/gunpowder/on_spark_act(power_charge, spark_flags) // 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) @@ -144,11 +144,11 @@ return if (power_charge >= STANDARD_CELL_CHARGE || holder.chem_temp >= 474) - reagent_explode(holder, volume, modifier = 5, strengthdiv = 10, clear_holder_reagents = FALSE) + reagent_explode(holder, volume, modifier = 5, strengthdiv = 10, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL holder.my_atom.visible_message(span_boldnotice("Sparks start flying around the gunpowder!")) - if (!enclosed) + if (!(spark_flags & SPARK_ACT_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... @@ -160,15 +160,15 @@ taste_description = "salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/rdx/on_spark_act(power_charge, enclosed) +/datum/reagent/rdx/on_spark_act(power_charge, spark_flags) 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) + reagent_explode(holder, volume, modifier = min(2 * power_coeff, 8), strengthdiv = 7 / min(power_coeff, 4), clear_holder_reagents = FALSE, flame_factor = 1) else if (holder.chem_temp >= 474) - reagent_explode(holder, volume, modifier = 2, strengthdiv = 7, clear_holder_reagents = FALSE) + reagent_explode(holder, volume, modifier = 2, strengthdiv = 7, clear_holder_reagents = FALSE, flame_factor = 1) else - reagent_explode(holder, volume, strengthdiv = 8, clear_holder_reagents = FALSE) + reagent_explode(holder, volume, strengthdiv = 8, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/tatp @@ -178,8 +178,8 @@ 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) +/datum/reagent/tatp/on_spark_act(power_charge, spark_flags) + reagent_explode(holder, volume, strengthdiv = 1.5 + rand() * 1.5, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/flash_powder @@ -189,11 +189,14 @@ taste_description = "salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/flash_powder/on_spark_act(power_charge, enclosed) +/datum/reagent/flash_powder/on_spark_act(power_charge, spark_flags) // Even weaker version of the normal flash effect var/turf/location = get_turf(holder.my_atom) - if (!enclosed) + if (!(spark_flags & SPARK_ACT_ENCLOSED)) do_sparks(2, TRUE, location) + else if (!(holder.flags & TRANSPARENT) || (!isturf(holder.my_atom.loc) && !ismob(holder.my_atom.loc))) // nope + return SPARK_ACT_NON_DESTRUCTIVE + 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)) @@ -216,10 +219,10 @@ taste_description = "smoke" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/smoke_powder/on_spark_act(power_charge, enclosed) +/datum/reagent/smoke_powder/on_spark_act(power_charge, spark_flags) // 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)) + if ((spark_flags & SPARK_ACT_ENCLOSED) && !ismob(holder.my_atom)) return var/location = get_turf(holder.my_atom) var/datum/effect_system/fluid_spread/smoke/chem/smoke_system = new() @@ -240,7 +243,7 @@ taste_description = "loud noises" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/sonic_powder/on_spark_act(power_charge, enclosed) +/datum/reagent/sonic_powder/on_spark_act(power_charge, spark_flags) // Even weaker version of the normal flash effect var/turf/location = get_turf(holder.my_atom) var/range = round(volume / 15, 1) @@ -270,8 +273,8 @@ 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)) +/datum/reagent/phlogiston/on_spark_act(power_charge, spark_flags) + if ((spark_flags & SPARK_ACT_ENCLOSED) && !ismob(holder.my_atom)) if (!holder.my_atom.uses_integrity) return // Spicy! @@ -308,8 +311,8 @@ if(istype(exposed_mob) && (methods & (TOUCH|VAPOR|PATCH))) 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)) +/datum/reagent/napalm/on_spark_act(power_charge, spark_flags) + if ((spark_flags & SPARK_ACT_ENCLOSED) && !ismob(holder.my_atom)) if (!holder.my_atom.uses_integrity) return // Spicy! @@ -445,7 +448,7 @@ 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) +/datum/reagent/teslium/on_spark_act(power_charge, spark_flags) 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 diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 944fcac731f..9c3b52054cc 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -82,6 +82,7 @@ #define LIQUID_PLASMA_BP (50+T0C) #define LIQUID_PLASMA_IG (325+T0C) #define LIQUID_PLASMA_CHARGE_COEFF 2.7 +#define LIQUID_PLASMA_VOLUME_POWER_CAP 7 /datum/reagent/toxin/plasma name = "Plasma" @@ -150,7 +151,16 @@ exposed_mob.adjust_fire_stacks(reac_volume / 5) return -/datum/reagent/toxin/plasma/on_spark_act(power_charge, enclosed) +/datum/reagent/toxin/plasma/on_spark_act(power_charge, spark_flags) + // Tape up your plasma IEDs + if ((spark_flags & SPARK_ACT_WEAKEN_COMMON) && !(spark_flags & SPARK_ACT_ENCLOSED)) + if(holder.chem_temp < LIQUID_PLASMA_BP) + return NONE + var/turf/holder_turf = get_turf(holder) + if (holder_turf) + holder_turf.atmos_spawn_air("[GAS_PLASMA]=[volume];[TURF_TEMPERATURE(holder.chem_temp)]") + return SPARK_ACT_NON_DESTRUCTIVE + // 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) @@ -158,13 +168,21 @@ 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) + var/power_modifier = max(0, round(sqrt(power_charge / STANDARD_CELL_CHARGE) * LIQUID_PLASMA_CHARGE_COEFF, 1) - 1) + var/strengthdiv = 5 + if (spark_flags & SPARK_ACT_WEAKEN_COMMON) + strengthdiv *= 3 // Stronger than waterpot, weaker than methbombs + var/current_limit = round(volume / LIQUID_PLASMA_VOLUME_POWER_CAP, 1) + // High current can only get you so far before you get a sharp dropoff + if (power_modifier > current_limit) + power_modifier = round(current_limit + log(power_modifier - current_limit + 1), 1) + reagent_explode(holder, volume, modifier = power_modifier, strengthdiv = strengthdiv, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL #undef LIQUID_PLASMA_BP #undef LIQUID_PLASMA_IG #undef LIQUID_PLASMA_CHARGE_COEFF +#undef LIQUID_PLASMA_VOLUME_POWER_CAP /datum/reagent/toxin/hot_ice name = "Hot Ice Slush" diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index 74f12ac7f9a..f0895a21b39 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -11,31 +11,53 @@ righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi' reagent_container_liquid_sound = SFX_DEFAULT_LIQUID_SLOSH - ///Like Edible's food type, what kind of drink is this? + /// Like Edible's food type, what kind of drink is this? var/drink_type = NONE - ///The last time we have checked for taste. + /// The last time we have checked for taste. var/last_check_time - ///How much we drink at once, shot glasses drink more. + /// How much we drink at once, shot glasses drink more. var/gulp_size = 5 - ///Whether the 'bottle' is made of glass or not so that milk cartons dont shatter when someone gets hit by it. + /// Whether the 'bottle' is made of glass or not so that milk cartons dont shatter when someone gets hit by it. var/isGlass = FALSE - ///What kind of chem transfer method does this cup use. Defaults to INGEST + /// What kind of chem transfer method does this cup use. Defaults to INGEST var/reagent_consumption_method = INGEST - ///What sound does our consumption play on consuming from the container? + /// What sound does our consumption play on consuming from the container? var/consumption_sound = 'sound/items/drink.ogg' - ///Whether to allow heating up the contents with a source of flame. + /// Whether to allow heating up the contents with a source of flame. var/heatable = TRUE + /// Can we put a lid on this container? + var/can_lid = FALSE + /// Does this container have a lid on right now? + var/has_lid = FALSE + /// Assembly attached to our lid + var/obj/item/assembly_holder/lid_assembly = null + /// Power cell duct-taped to the side of the beaker + var/obj/item/stock_parts/power_store/cell/attached_cell = null + /// Have we added wiring to the cell? + var/cell_wired = FALSE + /// Visual y-offset for the assembly on our lid + var/assembly_pixel_y = 0 /obj/item/reagent_containers/cup/Initialize(mapload, vol) . = ..() if(heatable) AddElement(/datum/element/reagents_item_heatable) +/obj/item/reagent_containers/cup/Destroy(force) + QDEL_NULL(lid_assembly) + QDEL_NULL(attached_cell) + return ..() + /obj/item/reagent_containers/cup/examine(mob/user) . = ..() if(drink_type) var/list/types = bitfield_to_list(drink_type, FOOD_FLAGS) . += span_notice("The label says it contains [LOWER_TEXT(english_list(types))] ingredients.") + if(can_lid) + if(has_lid) + . += span_notice("Its sealed with a bright orange rubber lid[!isnull(lid_assembly) ? "with an assembly attached ontop of it" : ""].") + else + . += span_notice("It can be sealed with a lid using [EXAMINE_HINT("Alt-Click")].") /** * Checks if the mob actually liked drinking this cup. @@ -141,8 +163,58 @@ return NONE /obj/item/reagent_containers/cup/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(has_lid && istype(tool, /obj/item/assembly_holder)) + if (lid_assembly) + to_chat(user, span_warning("[src]'s lid already has an assembly attached to it!")) + return ITEM_INTERACT_BLOCKING + + if (attach_assembly(tool, user)) + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + + if (lid_assembly && istype(tool, /obj/item/stock_parts/power_store/cell)) + if (attached_cell) + to_chat(user, span_warning("[src] already has \a [attached_cell] attached to it!")) + return ITEM_INTERACT_BLOCKING + + if (isnull(locate(/obj/item/assembly/igniter) in lid_assembly)) + to_chat(user, span_warning("[lid_assembly] doesn't have an igniter to connect [src] to!")) + return ITEM_INTERACT_BLOCKING + + if (!user.transferItemToLoc(tool, src)) + to_chat(user, span_warning("[tool] is stuck to your hand!")) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You attach [tool] underneath [src]'s lid.")) + add_fingerprint(user) + log_bomber(user, "attached [tool.name] to", src) + attached_cell = tool + attached_cell.pixel_y = 0 + attached_cell.pixel_z = -4 + update_appearance() + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + return ITEM_INTERACT_SUCCESS + + if (attached_cell && istype(tool, /obj/item/stack/cable_coil)) + if (cell_wired) + to_chat(user, span_warning("[attached_cell] is already wired to [lid_assembly]!")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/cable_coil/cable = tool + if (!cable.use(5)) + to_chat(user, span_warning("You need at least 5 cable pieces to wire [attached_cell]!")) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You wire [attached_cell] to [lid_assembly].")) + add_fingerprint(user) + cell_wired = TRUE + update_appearance() + return ITEM_INTERACT_SUCCESS + if(!is_open_container()) return NONE + if(istype(tool, /obj/item/food/egg)) //breaking eggs if(reagents.holder_full()) to_chat(user, span_notice("[src] is full.")) @@ -172,6 +244,125 @@ /obj/item/reagent_containers/cup/proc/on_cup_reset() drink_type = NONE +/obj/item/reagent_containers/cup/update_overlays() + . = ..() + if (has_lid) + . += mutable_appearance(icon, "[icon_state]_lid") + if (lid_assembly) + . += lid_assembly + if (attached_cell) + . += attached_cell + if (cell_wired) + . += mutable_appearance('icons/obj/machines/cell_charger.dmi', "ccharger-[attached_cell.connector_type]-on") + +// For player convinience, assume that the lids are rubber and can be pierced with a syringe +/obj/item/reagent_containers/cup/is_refillable() + return ..() && !has_lid + +/obj/item/reagent_containers/cup/is_drainable() + return ..() && !has_lid + +/obj/item/reagent_containers/cup/is_dunkable() + return ..() && !has_lid + +/obj/item/reagent_containers/cup/attack_self(mob/user) + if (!lid_assembly) + return ..() + lid_assembly.attack_self(user) + return TRUE + +/obj/item/reagent_containers/cup/click_alt(mob/user) + if (!can_lid) + return NONE + + if (cell_wired) + balloon_alert(user, "cut the wiring first!") + return CLICK_ACTION_BLOCKING + + if (attached_cell) + var/obj/item/our_cell = attached_cell + // Exited() automatically clears it + our_cell.forceMove(drop_location()) + user.put_in_hands(our_cell) + balloon_alert(user, "cell detached") + update_appearance() + return CLICK_ACTION_SUCCESS + + if (lid_assembly) + var/obj/item/our_assembly = lid_assembly + our_assembly.forceMove(drop_location()) + user.put_in_hands(our_assembly) + balloon_alert(user, "assembly detached") + update_appearance() + return CLICK_ACTION_SUCCESS + + has_lid = !has_lid + update_appearance() + balloon_alert(user, "lid [has_lid ? "sealed" : "unsealed"]") + if (has_lid) + add_container_flags(SEALED_CONTAINER) + else + reset_container_flags() + return CLICK_ACTION_SUCCESS + +/obj/item/reagent_containers/cup/wirecutter_act(mob/living/user, obj/item/tool) + if (user.combat_mode || !cell_wired) + return NONE + + new /obj/item/stack/cable_coil(drop_location(), 5) + cell_wired = FALSE + update_appearance() + balloon_alert(user, "wiring cut") + tool.play_tool_sound(src, 50) + return ITEM_INTERACT_SUCCESS + +/obj/item/reagent_containers/cup/proc/attach_assembly(obj/item/assembly_holder/assembly, mob/living/user) + if (!user.transferItemToLoc(assembly, src)) + to_chat(user, span_warning("[assembly] is stuck to your hand!")) + return FALSE + + to_chat(user, span_notice("You attach [assembly] to [src]'s lid.")) + add_fingerprint(user) + lid_assembly = assembly + lid_assembly.master = src + lid_assembly.pixel_y = 0 + lid_assembly.pixel_z = assembly_pixel_y + lid_assembly.on_attach() + RegisterSignal(src, COMSIG_IGNITER_ACTIVATE, PROC_REF(on_igniter_activate)) + log_bomber(user, "attached [lid_assembly.name] to", src) + update_appearance() + return TRUE + +/obj/item/reagent_containers/cup/Exited(atom/movable/gone, direction) + . = ..() + if (gone == lid_assembly) + lid_assembly = null + UnregisterSignal(src, COMSIG_IGNITER_ACTIVATE) + update_appearance() + else if (gone == attached_cell) + attached_cell = null + cell_wired = FALSE + update_appearance() + +/obj/item/reagent_containers/cup/proc/on_igniter_activate(datum/source, obj/item/assembly/igniter/igniter) + SIGNAL_HANDLER + // We've got an attached cell wired up, so we'll try to spend all of its current first + if (attached_cell && cell_wired) + var/power_spent = attached_cell.use(attached_cell.charge()) + // Power cell was rigged + if (QDELETED(src)) + return + + // We'll be nerfing plasma and welding fuel as they're very easy to get and make for boring bombs + if (power_spent > 0 && (reagents.spark_act(power_spent, SPARK_ACT_ENCLOSED | SPARK_ACT_WEAKEN_COMMON) & SPARK_ACT_DESTRUCTIVE)) + qdel(src) + return + + // Igniters heat, condensers chill + var/igniter_temp = igniter.get_temperature() + if (igniter_temp > 0) + reagents.expose_temperature(igniter_temp) + /obj/item/reagent_containers/cup/beaker name = "beaker" desc = "A beaker. It can hold up to 50 units." @@ -186,6 +377,8 @@ pickup_sound = 'sound/items/handling/beaker_pickup.ogg' drop_sound = 'sound/items/handling/beaker_place.ogg' sound_vary = TRUE + can_lid = TRUE + assembly_pixel_y = 4 /obj/item/reagent_containers/cup/beaker/Initialize(mapload) . = ..() @@ -199,6 +392,7 @@ desc = "A jar for honey. It can hold up to 50 units of sweet delight." icon = 'icons/obj/medical/chemical.dmi' icon_state = "vapour" + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/large name = "large beaker" @@ -209,6 +403,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100) fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + assembly_pixel_y = 8 /obj/item/reagent_containers/cup/beaker/plastic name = "x-large beaker" @@ -220,6 +415,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,60,120) fill_icon_thresholds = list(0, 1, 10, 20, 40, 60, 80, 100) + assembly_pixel_y = 8 /obj/item/reagent_containers/cup/beaker/meta name = "metamaterial beaker" @@ -231,6 +427,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,60,120,180) fill_icon_thresholds = list(0, 1, 10, 25, 35, 50, 60, 80, 100) + assembly_pixel_y = 10 /obj/item/reagent_containers/cup/beaker/noreact name = "cryostasis beaker" @@ -242,6 +439,7 @@ initial_reagent_flags = OPENCONTAINER | NO_REACT volume = 50 amount_per_transfer_from_this = 10 + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/bluespace name = "bluespace beaker" @@ -254,6 +452,7 @@ volume = 300 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100,300) + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/meta/omnizine list_reagents = list(/datum/reagent/medicine/omnizine = 180) @@ -528,3 +727,8 @@ possible_transfer_amounts = list(5, 10, 15, 30) volume = 30 fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + can_lid = TRUE + +/obj/item/reagent_containers/cup/tube/attach_assembly(obj/item/assembly_holder/assembly, mob/living/user) + to_chat(user, span_warning("[src]'s lid is too small to fit [assembly]!")) + return FALSE diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 9557ef3a9f7..5a8e8bc5a7d 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -11,6 +11,8 @@ possible_transfer_amounts = list(5, 10, 15, 25, 50) volume = 50 fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + can_lid = TRUE + assembly_pixel_y = 4 /obj/item/reagent_containers/cup/bottle/Initialize(mapload) . = ..() @@ -505,6 +507,7 @@ fill_icon_thresholds = list(0, 20, 40, 60, 80, 100) possible_transfer_amounts = list(5, 10) amount_per_transfer_from_this = 5 + can_lid = FALSE /obj/item/reagent_containers/cup/bottle/syrup_bottle/Initialize(mapload) . = ..() diff --git a/code/modules/reagents/reagent_containers/cups/organ_jar.dm b/code/modules/reagents/reagent_containers/cups/organ_jar.dm index 52754f5836a..b71b048de28 100644 --- a/code/modules/reagents/reagent_containers/cups/organ_jar.dm +++ b/code/modules/reagents/reagent_containers/cups/organ_jar.dm @@ -12,6 +12,7 @@ possible_transfer_amounts = list(20, 40, 60, 120) fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) w_class = WEIGHT_CLASS_SMALL // Organs are small by default, so the jar should be at least small as well + can_lid = FALSE /// The organ that is currently inside the jar var/obj/item/organ/held_organ = null /// Whether the jar is filled to capacity with formaldehyde, preserving any organ inside diff --git a/code/modules/reagents/reagent_containers/cups/soda.dm b/code/modules/reagents/reagent_containers/cups/soda.dm index 569d40d33cd..40ab56e6093 100644 --- a/code/modules/reagents/reagent_containers/cups/soda.dm +++ b/code/modules/reagents/reagent_containers/cups/soda.dm @@ -22,6 +22,12 @@ throwforce = 12 // set to 0 upon being opened. Have you ever been domed by a soda can? Those things fucking hurt /// If the can hasn't been opened yet, this is the measure of how fizzed up it is from being shaken or thrown around. When opened, this is rolled as a percentage chance to burst var/fizziness = 0 + /// Have we been sealed with tape? And if so, what color is it? + var/tape_color = null + /// Color of our fuse, if any + var/fuse_color = null + /// Timer for our explosion + var/fuse_timer = null /obj/item/reagent_containers/cup/soda_cans/Initialize(mapload, vol) . = ..() @@ -39,7 +45,7 @@ H.visible_message(span_warning("[H] is trying to take a big sip from [src]... The can is empty!")) return SHAME if(!is_drainable()) - open_soda() + open_soda(H) sleep(1 SECONDS) H.visible_message(span_suicide("[H] takes a big sip from [src]! It looks like [H.p_theyre()] trying to commit suicide!")) playsound(H,'sound/items/drink.ogg', 80, TRUE) @@ -67,24 +73,24 @@ return TOXLOSS /obj/item/reagent_containers/cup/soda_cans/interact_with_atom(atom/target, mob/living/user, list/modifiers) - if(iscarbon(target) && !reagents.total_volume && user.combat_mode && user.zone_selected == BODY_ZONE_HEAD) - if(target == user) - user.visible_message( - span_warning("[user] crushes the can of [src] on [user.p_their()] forehead!"), - span_notice("You crush the can of [src] on your forehead."), - ) - else - user.visible_message( - span_warning("[user] crushes the can of [src] on [target]'s forehead!"), - span_notice("You crush the can of [src] on [target]'s forehead."), - ) - playsound(src, 'sound/items/weapons/pierce.ogg', rand(10, 50), TRUE) - var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(target.drop_location()) - crushed_can.icon_state = icon_state - qdel(src) - return ITEM_INTERACT_SUCCESS + if(!iscarbon(target) || reagents.total_volume || !user.combat_mode || user.zone_selected != BODY_ZONE_HEAD) + return ..() - return ..() + if(target == user) + user.visible_message( + span_warning("[user] crushes the can of [src] on [user.p_their()] forehead!"), + span_notice("You crush the can of [src] on your forehead."), + ) + else + user.visible_message( + span_warning("[user] crushes the can of [src] on [target]'s forehead!"), + span_notice("You crush the can of [src] on [target]'s forehead."), + ) + playsound(src, 'sound/items/weapons/pierce.ogg', rand(10, 50), TRUE) + var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(target.drop_location()) + crushed_can.icon_state = icon_state + qdel(src) + return ITEM_INTERACT_SUCCESS /obj/item/reagent_containers/cup/soda_cans/bullet_act(obj/projectile/proj) . = ..() @@ -99,6 +105,14 @@ qdel(src) /obj/item/reagent_containers/cup/soda_cans/proc/open_soda(mob/user) + if(tape_color) + to_chat(user, "You rip off the tape covering [src]'s hole.") + playsound(user, 'sound/items/duct_tape/duct_tape_rip.ogg', 50, TRUE) + tape_color = null + add_container_flags(OPENCONTAINER) + update_appearance() + return + if(prob(fizziness)) user.visible_message(span_danger("[user] opens [src], and is suddenly sprayed by the fizzing contents!"), span_danger("You pull back the tab of [src], and are suddenly sprayed with a torrent of liquid! Ahhh!!")) burst_soda(user) @@ -135,9 +149,120 @@ reagents.clear_reagents() throwforce = 0 +/obj/item/reagent_containers/cup/soda_cans/wirecutter_act(mob/living/user, obj/item/tool) + if (!fuse_color) + return NONE + to_chat(user, span_notice("You snip [src]'s fuse off.")) + tool.play_tool_sound(src, 50) + add_fingerprint(user) + fuse_color = null + if (!isnull(fuse_timer)) + deltimer(fuse_timer) + fuse_timer = null + log_bomber(user, "has disarmed", src) + if (heatable) + AddElement(/datum/element/reagents_item_heatable) + update_appearance() + +/obj/item/reagent_containers/cup/soda_cans/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (istype(tool, /obj/item/stack/cable_coil)) + if (fuse_color) + to_chat(user, span_warning("[src] already has a fuse attached to it!")) + return ITEM_INTERACT_BLOCKING + + if (tape_color) + to_chat(user, span_warning("[src]'s hole is covered up with tape!")) + return ITEM_INTERACT_BLOCKING + + if (!is_drainable()) + to_chat(user, span_warning("[src] hasn't been opened yet!")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/cable_coil/coil = tool + var/coil_color = GLOB.cable_colors[coil.cable_color] + add_fingerprint(user) + if (!coil.use(1)) + return ITEM_INTERACT_BLOCKING + + fuse_color = coil_color + // Heating replaced with lighting the fuse + RemoveElement(/datum/element/reagents_item_heatable) + to_chat(user, span_notice("You attach a fuse to [src].")) + log_bomber(user, "attached a fuse to", src) + update_appearance() + return ITEM_INTERACT_SUCCESS + + if (istype(tool, /obj/item/stack/sticky_tape)) + if (tape_color) + to_chat(user, span_warning("[src]'s hole is already covered up with tape!")) + return ITEM_INTERACT_BLOCKING + + if (!is_drainable()) + to_chat(user, span_warning("[src] hasn't been opened yet!")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/sticky_tape/tape = tool + var/list/tape_colors = SSgreyscale.ParseColorString(tape.greyscale_colors) + add_fingerprint(user) + if (!tape.use(1)) + return ITEM_INTERACT_BLOCKING + + tape_color = tape_colors[1] + to_chat(user, span_notice("You wrap [src] up in [tape].")) + reset_container_flags() + update_appearance() + return ITEM_INTERACT_SUCCESS + + if (!fuse_color || tool.get_temperature() < FIRE_MINIMUM_TEMPERATURE_TO_EXIST) + return ..() + + if (fuse_timer) + to_chat(user, span_warning("[src] is already lit!")) + return ITEM_INTERACT_BLOCKING + + add_fingerprint(user) + log_bomber(user, "has primed a rigged", src) + to_chat(user, span_warning("You light [src]'s fuse!")) + fuse_timer = addtimer(CALLBACK(src, PROC_REF(try_detonate)), rand(2 SECONDS, 4 SECONDS)) + update_appearance() + return ITEM_INTERACT_SUCCESS + +/obj/item/reagent_containers/cup/soda_cans/proc/try_detonate() + var/spark_flags = SPARK_ACT_WEAKEN_COMMON + if (tape_color) + spark_flags |= SPARK_ACT_ENCLOSED + + playsound(src, 'sound/effects/sparks/sparks1.ogg', 50, TRUE) + if (reagents.spark_act(0, spark_flags) & SPARK_ACT_DESTRUCTIVE) + qdel(src) + return + + // Was a dud + fuse_color = null + tape_color = null + add_container_flags(OPENCONTAINER) + if (heatable) + AddElement(/datum/element/reagents_item_heatable) + update_appearance() + +/obj/item/reagent_containers/cup/soda_cans/update_overlays() + . = ..() + if (fuse_color) + var/mutable_appearance/fuse_overlay = mutable_appearance('icons/obj/weapons/grenade.dmi', "improvised_grenade_fuse") + fuse_overlay.color = fuse_color + . += fuse_overlay + + if (tape_color) + var/mutable_appearance/tape_overlay = mutable_appearance('icons/obj/weapons/grenade.dmi', "improvised_grenade_tape") + tape_overlay.color = tape_color + . += tape_overlay + + if (fuse_timer) + . += mutable_appearance('icons/obj/weapons/grenade.dmi', "improvised_grenade_active") + /obj/item/reagent_containers/cup/soda_cans/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) . = ..() - if(. || is_open_container() || !reagents.total_volume) // if it was caught, already opened, or has nothing in it + if(. || is_open_container() || !reagents.total_volume || tape_color) // if it was caught, already opened, or has nothing in it return fizziness += SODA_FIZZINESS_THROWN @@ -152,9 +277,14 @@ QDEL_IN(src, 1 SECONDS) // give it a second so it can still be logged for the throw impact /obj/item/reagent_containers/cup/soda_cans/attack_self(mob/user) + if(fuse_timer) + balloon_alert(user, "the fuse is on fire!") + return + if(!is_drainable()) open_soda(user) return + return ..() /obj/item/reagent_containers/cup/soda_cans/attack_self_secondary(mob/user) diff --git a/icons/obj/medical/chemical.dmi b/icons/obj/medical/chemical.dmi index 0bc3c3fd075..8f3267c07d4 100644 Binary files a/icons/obj/medical/chemical.dmi and b/icons/obj/medical/chemical.dmi differ diff --git a/icons/obj/weapons/grenade.dmi b/icons/obj/weapons/grenade.dmi index 415166f9c40..a70529cd60d 100644 Binary files a/icons/obj/weapons/grenade.dmi and b/icons/obj/weapons/grenade.dmi differ