Skip to content

Commit

Permalink
Reaction fixes, refactoring, and flash powder retune (#7654)
Browse files Browse the repository at this point in the history
# About the pull request

This PR refactors a bit of chemistry code (var cleanup, removal of
required_other, rework required_container to do path checking), disables
mob reaction for several reactions, and retunes the flash power effect.

Flash powder now creates a chemical light in the contents of its reagent
holder (allowing it to be destroyed), has some additional logging, and
has had its duration, range, and power retuned:

![image](https://github.com/user-attachments/assets/cd4cff2a-3b5c-4760-986f-d708c8ba58f3)

# Explain why it's good for the game
This hopefully gives a bit of experimentation to using this reaction,
tones down how excessive it can be, and introduces a bit of counterplay
now that it is now destroyable.

# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>

https://youtu.be/HSCVT3V1saQ

</details>


# Changelog
:cl: Drathek
balance: emp_pulse, flash_powder, and foam reactions are no longer
possible in mobs
balance: flash_powder has been reworked to attach to its holder (so it
can be destroyed), and values retuned/capped.
refactor: Refactored the metabolize proc for chemistry reactors (removed
required_other and changed required_container to use a path check
instead)
/:cl:

---------

Co-authored-by: harryob <[email protected]>
  • Loading branch information
Drulikar and harryob authored Nov 24, 2024
1 parent 00e717a commit 54ccd98
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 72 deletions.
14 changes: 10 additions & 4 deletions code/game/objects/items/devices/flashlight.dm
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,17 @@

/obj/item/device/flashlight/flare/on/illumination/chemical/Initialize(mapload, amount)
. = ..()
light_range = floor(amount * 0.04)
if(!light_range)
if(amount < 1)
return INITIALIZE_HINT_QDEL
set_light(light_range)
fuel = amount * 5 SECONDS
var/square_amount = sqrt(amount)
// Fuel quickly ramps up to about 15.5 mins then tapers off the more volume there is (6s min)
fuel = max(((-150 / square_amount) - 2 * sqrt(amount + 2000) + 120), 0.1) MINUTES
// Range gradually ramps up from 1 to 15
light_range = max(min(square_amount - 3, 15), MINIMUM_USEFUL_LIGHT_RANGE)
// Power slowly ramps up from 1 to 5
light_power = min(0.1 * square_amount + 1, 5)
set_light(light_range, light_power)


/obj/item/device/flashlight/slime
gender = PLURAL
Expand Down
100 changes: 45 additions & 55 deletions code/modules/reagents/Chemistry-Holder.dm
Original file line number Diff line number Diff line change
Expand Up @@ -222,99 +222,89 @@
return amount

/datum/reagents/proc/metabolize(mob/M, alien, delta_time)
for(var/datum/reagent/R in reagent_list)
if(M && R && !QDELETED(R))
R.on_mob_life(M, alien, delta_time)
for(var/datum/reagent/reagent in reagent_list)
if(M && reagent && !QDELETED(reagent))
reagent.on_mob_life(M, alien, delta_time)
update_total()

/datum/reagents/proc/handle_reactions()
if(!my_atom) return
if(my_atom.flags_atom & NOREACT) return //Yup, no reactions here. No siree.
if(!my_atom)
return
if(my_atom.flags_atom & NOREACT)
return //Yup, no reactions here. No siree.

var/reaction_occurred = 0
var/reaction_occurred = FALSE
do
reaction_occurred = 0
for(var/datum/reagent/R in reagent_list) // Usually a small list
if(R.original_id) //Prevent synthesised chem variants from being mixed
for(var/datum/reagent/O in reagent_list)
if(O.id == R.id)
reaction_occurred = FALSE
for(var/datum/reagent/reagent in reagent_list) // Usually a small list
if(reagent.original_id) //Prevent synthesised chem variants from being mixed
for(var/datum/reagent/current in reagent_list)
if(current.id == reagent.id)
continue
else if(O.original_id == R.original_id || O.id == R.original_id)
else if(current.original_id == reagent.original_id || current.id == reagent.original_id)
//Merge into the original
reagent_list -= R
O.volume += R.volume
qdel(R)
reagent_list -= reagent
current.volume += reagent.volume
qdel(reagent)
break
for(var/reaction in GLOB.chemical_reactions_filtered_list[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id

if(!reaction)
continue

var/datum/chemical_reaction/C = reaction

var/total_required_reagents = length(C.required_reagents)
for(var/datum/chemical_reaction/reaction in GLOB.chemical_reactions_filtered_list[reagent.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id
var/total_required_reagents = length(reaction.required_reagents)
var/total_matching_reagents = 0
var/total_required_catalysts = 0
if(C.required_catalysts)
total_required_catalysts = length(C.required_catalysts)
if(reaction.required_catalysts)
total_required_catalysts = length(reaction.required_catalysts)
var/total_matching_catalysts= 0
var/matching_container = 0
var/matching_other = 0
var/matching_container = FALSE
var/list/multipliers = new/list()

for(var/B in C.required_reagents)
if(!has_reagent(B, C.required_reagents[B]))
for(var/required_reagent in reaction.required_reagents)
if(!has_reagent(required_reagent, reaction.required_reagents[required_reagent]))
break
total_matching_reagents++
multipliers += floor(get_reagent_amount(B) / C.required_reagents[B])
for(var/B in C.required_catalysts)
if(B == "silver" && istype(my_atom, /obj/item/reagent_container/glass/beaker/silver))
multipliers += floor(get_reagent_amount(required_reagent) / reaction.required_reagents[required_reagent])
for(var/catalyst in reaction.required_catalysts)
if(catalyst == "silver" && istype(my_atom, /obj/item/reagent_container/glass/beaker/silver))
total_matching_catalysts++
continue
if(!has_reagent(B, C.required_catalysts[B]))
if(!has_reagent(catalyst, reaction.required_catalysts[catalyst]))
break
total_matching_catalysts++

if(isliving(my_atom) && !C.mob_react) //Makes it so some chemical reactions don't occur in mobs
if(isliving(my_atom) && !reaction.mob_react) //Makes it so some chemical reactions don't occur in mobs
continue

if(!C.required_container)
matching_container = 1

else
if(my_atom.type == C.required_container)
matching_container = 1

if(!C.required_other)
matching_other = 1
if(!reaction.required_container)
matching_container = TRUE
else if(ispath(my_atom.type, reaction.required_container))
matching_container = TRUE

if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other)
if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container)
var/multiplier = min(multipliers)
var/preserved_data = null
for(var/B in C.required_reagents)
for(var/required_reagent in reaction.required_reagents)
if(!preserved_data)
preserved_data = get_data(B)
remove_reagent(B, (multiplier * C.required_reagents[B]), safety = 1)
preserved_data = get_data(required_reagent)
remove_reagent(required_reagent, (multiplier * reaction.required_reagents[required_reagent]), safety = TRUE)

var/created_volume = C.result_amount*multiplier
if(C.result)
var/created_volume = reaction.result_amount*multiplier
if(reaction.result)

multiplier = max(multiplier, 1) //this shouldnt happen ...
add_reagent(C.result, C.result_amount*multiplier)
set_data(C.result, preserved_data)
add_reagent(reaction.result, reaction.result_amount*multiplier)
set_data(reaction.result, preserved_data)

//add secondary products
for(var/S in C.secondary_results)
add_reagent(S, C.result_amount * C.secondary_results[S] * multiplier)
for(var/secondary_result in reaction.secondary_results)
add_reagent(secondary_result, reaction.result_amount * reaction.secondary_results[secondary_result] * multiplier)

var/list/seen = viewers(4, get_turf(my_atom))
for(var/mob/M in seen)
to_chat(M, SPAN_NOTICE("[icon2html(my_atom, M)] The solution begins to bubble."))

playsound(get_turf(my_atom), 'sound/effects/bubbles.ogg', 15, 1)

C.on_reaction(src, created_volume)
reaction_occurred = 1
reaction.on_reaction(src, created_volume)
reaction_occurred = TRUE
break

while(reaction_occurred)
Expand Down
18 changes: 10 additions & 8 deletions code/modules/reagents/Chemistry-Reactions.dm
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
var/list/required_reagents = new/list()
var/list/required_catalysts = new/list()

var/mob_react = TRUE //Determines if a chemical reaction can occur inside a mob
/// Determines if a chemical reaction can occur inside a mob
var/mob_react = TRUE
/// The container path required for the reaction to happen
var/required_container = null

// both vars below are currently unused
var/atom/required_container = null // the container required for the reaction to happen
var/required_other = 0 // an integer required for the reaction to happen

var/result_amount = 0 //I recommend you set the result amount to the total volume of all components.
var/secondary = 0 // set to nonzero if secondary reaction
var/list/secondary_results = list() //additional reagents produced by the reaction
/// The resulting amount: Recommended to be set to the total volume of all components
var/result_amount = 0
/// set to nonzero if secondary reaction
var/secondary = 0
/// additional reagents produced by the reaction
var/list/secondary_results = list()
var/requires_heating = 0

/datum/chemical_reaction/proc/on_reaction(datum/reagents/holder, created_volume)
Expand Down
26 changes: 21 additions & 5 deletions code/modules/reagents/chemistry_reactions/other.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
result = null
required_reagents = list("uranium" = 1, "iron" = 1) // Yes, laugh, it's the best recipe I could think of that makes a little bit of sense
result_amount = 2
mob_react = FALSE

/datum/chemical_reaction/emp_pulse/on_reaction(datum/reagents/holder, created_volume)
var/location = get_turf(holder.my_atom)
Expand Down Expand Up @@ -155,13 +156,27 @@
result = null
required_reagents = list("aluminum" = 1, "potassium" = 1, "sulfur" = 1 )
result_amount = 3
mob_react = FALSE

/datum/chemical_reaction/flash_powder/on_reaction(datum/reagents/holder, created_volume)
var/location = get_turf(holder.my_atom)
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(2, 1, location)
s.start()
new /obj/item/device/flashlight/flare/on/illumination/chemical(location, created_volume)
var/turf/location = get_turf(holder.my_atom)
var/datum/effect_system/spark_spread/sparker = new
sparker.set_up(2, 1, location)
sparker.start()
var/obj/item/device/flashlight/flare/on/illumination/chemical/light = new(holder.my_atom, created_volume)

//Admin messaging
var/area/area = get_area(location)
var/where = "[area.name]|[location.x], [location.y]"
var/whereLink = "<A HREF='?_src_=admin_holder;[HrefToken(forceGlobal = TRUE)];adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>[where]</a>"
var/data = " [created_volume] volume -> fuel: [light.fuel] range: [light.light_range] power: [light.light_power]"

if(holder.my_atom.fingerprintslast)
msg_admin_niche("[src] reaction has taken place in ([whereLink])[data]. Last associated key is [holder.my_atom.fingerprintslast].")
log_game("[src] reaction has taken place in ([where])[data]. Last associated key is [holder.my_atom.fingerprintslast].")
else
msg_admin_niche("[src] reaction has taken place in ([whereLink])[data]. No associated key.")
log_game("[src] reaction has taken place in ([where])[data]. No associated key.")

/datum/chemical_reaction/chemfire
name = "Napalm"
Expand Down Expand Up @@ -373,6 +388,7 @@
result = null
required_reagents = list("fluorosurfactant" = 1, "water" = 1)
result_amount = 2
mob_react = FALSE

/datum/chemical_reaction/foam/on_reaction(datum/reagents/holder, created_volume)
var/location = get_turf(holder.my_atom)
Expand Down

0 comments on commit 54ccd98

Please sign in to comment.