<commit_before>/obj/machinery/atmospherics/components/tank
	icon = 'icons/obj/atmospherics/stationary_canisters.dmi'
	icon_state = "smooth"

	name = "pressure tank"
	desc = "A large vessel containing pressurized gas."

	max_integrity = 800
	integrity_failure = 0.2
	density = TRUE
	layer = ABOVE_WINDOW_LAYER

	custom_materials = list(/datum/material/alloy/plasteel=4000, /datum/material/iron=20000)
	material_flags = MATERIAL_GREYSCALE | MATERIAL_ADD_PREFIX | MATERIAL_AFFECT_STATISTICS

	pipe_flags = PIPING_ONE_PER_TURF
	device_type = QUATERNARY
	initialize_directions = NONE
	custom_reconcilation = TRUE

	smoothing_flags = SMOOTH_CORNERS | SMOOTH_OBJ
	smoothing_groups = list(SMOOTH_GROUP_GAS_TANK)
	canSmoothWith = list(SMOOTH_GROUP_GAS_TANK)
	appearance_flags = KEEP_TOGETHER

	greyscale_config = /datum/greyscale_config/stationary_canister
	greyscale_colors = "#ffffff"

	var/image/window

	/// The volume of the gas mixture
	var/volume = 2500 //in liters
	/// The max pressure of the gas mixture before damaging the tank
	var/max_pressure = 20000
	/// The typepath of the gas this tank should be filled with.
	var/gas_type = null

	var/datum/gas_mixture/air_contents

	/// The sounds that play when the tank is breaking from overpressure
	var/static/list/breaking_sounds = list(
		'sound/effects/structure_stress/pop1.ogg',
		'sound/effects/structure_stress/pop2.ogg',
		'sound/effects/structure_stress/pop3.ogg',
	)

	/// Shared images for the knob overlay representing a side of the tank that is open to connections
	var/static/list/knob_overlays

	/// Number of crack states to fill the list with. This exists because I'm lazy and didn't want to keeping adding more things manually to the below list.
	var/crack_states_count = 10
	/// The icon states for the cracks in the tank dmi
	var/static/list/crack_states

	/// The merger id used to create/get the merger group in charge of handling tanks that share an internal gas storage
	var/merger_id = "stationary_tanks"
	/// The typecache of types which are allowed to merge internal storage
	var/static/list/merger_typecache

/obj/machinery/atmospherics/components/tank/Initialize()
	. = ..()

	if(!knob_overlays)
		knob_overlays = list()
		for(var/dir in GLOB.cardinals)
			knob_overlays["[dir]"] = image('icons/obj/atmospherics/stationary_canisters.dmi', icon_state="knob", dir=dir, layer=FLOAT_LAYER)

	if(!crack_states)
		crack_states = list()
		for(var/i in 1 to crack_states_count)
			crack_states += "crack[i]"

	if(!merger_typecache)
		merger_typecache = typecacheof(/obj/machinery/atmospherics/components/tank)

	AddComponent(/datum/component/gas_leaker, leak_rate=0.05)
	AddElement(/datum/element/volatile_gas_storage)
	AddElement(/datum/element/crackable, 'icons/obj/atmospherics/stationary_canisters.dmi', crack_states)

	RegisterSignal(src, COMSIG_MERGER_ADDING, .proc/MergerAdding)
	RegisterSignal(src, COMSIG_MERGER_REMOVING, .proc/MergerRemoving)
	RegisterSignal(src, COMSIG_ATOM_SMOOTHED_ICON, .proc/Smoothed)

	air_contents = new
	air_contents.temperature = T20C
	air_contents.volume = volume
	RefreshPressureLimit()

	if(gas_type)
		FillToPressure(gas_type)

	setPipingLayer(piping_layer)
	QUEUE_SMOOTH(src)
	QUEUE_SMOOTH_NEIGHBORS(src)

	return INITIALIZE_HINT_LATELOAD

// We late initialize here so all stationary tanks have time to set up their
// initial gas mixes and signal registrations.
/obj/machinery/atmospherics/components/tank/LateInitialize()
	. = ..()
	GetMergeGroup(merger_id, merger_typecache)

/obj/machinery/atmospherics/components/tank/Destroy()
	QUEUE_SMOOTH_NEIGHBORS(src)
	return ..()

/obj/machinery/atmospherics/components/tank/examine(mob/user, thats)
	. = ..()
	var/wrench_hint = EXAMINE_HINT("wrench")
	if(!initialize_directions)
		. += "<span class='notice'>A pipe port can be opened with a [wrench_hint].</span>"
	else
		. += "<span class='notice'>The pipe port can be moved or closed with a [wrench_hint].</span>"

/obj/machinery/atmospherics/components/tank/set_custom_materials(list/materials, multiplier)
	. = ..()
	RefreshPressureLimit()

/// Recalculates pressure based on the current max integrity compared to original
/obj/machinery/atmospherics/components/tank/proc/RefreshPressureLimit()
	var/max_pressure_multiplier = max_integrity / initial(max_integrity)
	max_pressure = max_pressure_multiplier * initial(max_pressure)

/// Fills the tank to the maximum safe pressure.
/// Safety margin is a multiplier for the cap for the purpose of this proc so it doesn't have to be filled completely.
/obj/machinery/atmospherics/components/tank/proc/FillToPressure(gastype, safety_margin=0.5)
	var/pressure_limit = max_pressure * safety_margin

	var/moles_to_add = (pressure_limit * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
	air_contents.assert_gas(gastype)
	air_contents.gases[gastype][MOLES] += moles_to_add
	air_contents.archive()
	update_parents()

/obj/machinery/atmospherics/components/tank/process_atmos()
	if(air_contents.react(src))
		update_parents()

	if(air_contents.return_pressure() > max_pressure)
		take_damage(0.1, BRUTE, sound_effect=FALSE)
		if(prob(40))
			playsound(src, pick(breaking_sounds), 30, vary=TRUE)

	RefreshWindow()

///////////////////////////////////////////////////////////////////
// Pipenet stuff

/obj/machinery/atmospherics/components/tank/returnAirsForReconcilation(datum/pipeline/requester)
	. = ..()
	if(!air_contents)
		return
	. += air_contents

/obj/machinery/atmospherics/components/tank/returnPipenetsForReconcilation(datum/pipeline/requester)
	. = ..()
	var/datum/merger/merge_group = GetMergeGroup(merger_id, merger_typecache)
	for(var/obj/machinery/atmospherics/components/tank/tank as anything in merge_group.members)
		. += tank.parents

/obj/machinery/atmospherics/components/tank/proc/ToggleSidePort(new_dir)
	if(initialize_directions & new_dir)
		initialize_directions &= ~new_dir
	else
		initialize_directions |= new_dir<commit_msg>Makes tanks display their max pressure and only return their analysed contents once (#59946)<commit_after>/obj/machinery/atmospherics/components/tank
	icon = 'icons/obj/atmospherics/stationary_canisters.dmi'
	icon_state = "smooth"

	name = "pressure tank"
	desc = "A large vessel containing pressurized gas."

	max_integrity = 800
	integrity_failure = 0.2
	density = TRUE
	layer = ABOVE_WINDOW_LAYER

	custom_materials = list(/datum/material/alloy/plasteel=4000, /datum/material/iron=20000)
	material_flags = MATERIAL_GREYSCALE | MATERIAL_ADD_PREFIX | MATERIAL_AFFECT_STATISTICS

	pipe_flags = PIPING_ONE_PER_TURF
	device_type = QUATERNARY
	initialize_directions = NONE
	custom_reconcilation = TRUE

	smoothing_flags = SMOOTH_CORNERS | SMOOTH_OBJ
	smoothing_groups = list(SMOOTH_GROUP_GAS_TANK)
	canSmoothWith = list(SMOOTH_GROUP_GAS_TANK)
	appearance_flags = KEEP_TOGETHER

	greyscale_config = /datum/greyscale_config/stationary_canister
	greyscale_colors = "#ffffff"

	var/image/window

	/// The volume of the gas mixture
	var/volume = 2500 //in liters
	/// The max pressure of the gas mixture before damaging the tank
	var/max_pressure = 20000
	/// The typepath of the gas this tank should be filled with.
	var/gas_type = null

	var/datum/gas_mixture/air_contents

	/// The sounds that play when the tank is breaking from overpressure
	var/static/list/breaking_sounds = list(
		'sound/effects/structure_stress/pop1.ogg',
		'sound/effects/structure_stress/pop2.ogg',
		'sound/effects/structure_stress/pop3.ogg',
	)

	/// Shared images for the knob overlay representing a side of the tank that is open to connections
	var/static/list/knob_overlays

	/// Number of crack states to fill the list with. This exists because I'm lazy and didn't want to keeping adding more things manually to the below list.
	var/crack_states_count = 10
	/// The icon states for the cracks in the tank dmi
	var/static/list/crack_states

	/// The merger id used to create/get the merger group in charge of handling tanks that share an internal gas storage
	var/merger_id = "stationary_tanks"
	/// The typecache of types which are allowed to merge internal storage
	var/static/list/merger_typecache

/obj/machinery/atmospherics/components/tank/Initialize()
	. = ..()

	if(!knob_overlays)
		knob_overlays = list()
		for(var/dir in GLOB.cardinals)
			knob_overlays["[dir]"] = image('icons/obj/atmospherics/stationary_canisters.dmi', icon_state="knob", dir=dir, layer=FLOAT_LAYER)

	if(!crack_states)
		crack_states = list()
		for(var/i in 1 to crack_states_count)
			crack_states += "crack[i]"

	if(!merger_typecache)
		merger_typecache = typecacheof(/obj/machinery/atmospherics/components/tank)

	AddComponent(/datum/component/gas_leaker, leak_rate=0.05)
	AddElement(/datum/element/volatile_gas_storage)
	AddElement(/datum/element/crackable, 'icons/obj/atmospherics/stationary_canisters.dmi', crack_states)

	RegisterSignal(src, COMSIG_MERGER_ADDING, .proc/MergerAdding)
	RegisterSignal(src, COMSIG_MERGER_REMOVING, .proc/MergerRemoving)
	RegisterSignal(src, COMSIG_ATOM_SMOOTHED_ICON, .proc/Smoothed)

	air_contents = new
	air_contents.temperature = T20C
	air_contents.volume = volume
	RefreshPressureLimit()

	if(gas_type)
		FillToPressure(gas_type)

	setPipingLayer(piping_layer)
	QUEUE_SMOOTH(src)
	QUEUE_SMOOTH_NEIGHBORS(src)

	return INITIALIZE_HINT_LATELOAD

// We late initialize here so all stationary tanks have time to set up their
// initial gas mixes and signal registrations.
/obj/machinery/atmospherics/components/tank/LateInitialize()
	. = ..()
	GetMergeGroup(merger_id, merger_typecache)

/obj/machinery/atmospherics/components/tank/Destroy()
	QUEUE_SMOOTH_NEIGHBORS(src)
	return ..()

/obj/machinery/atmospherics/components/tank/examine(mob/user, thats)
	. = ..()
	var/wrench_hint = EXAMINE_HINT("wrench")
	if(!initialize_directions)
		. += "<span class='notice'>A pipe port can be opened with a [wrench_hint].</span>"
	else
		. += "<span class='notice'>The pipe port can be moved or closed with a [wrench_hint].</span>"
	. += "<span class='notice'>A holographic sticker on it says that its maximum safe pressure is: [siunit_pressure(max_pressure, 0)].</span>"

/obj/machinery/atmospherics/components/tank/set_custom_materials(list/materials, multiplier)
	. = ..()
	RefreshPressureLimit()

/// Recalculates pressure based on the current max integrity compared to original
/obj/machinery/atmospherics/components/tank/proc/RefreshPressureLimit()
	var/max_pressure_multiplier = max_integrity / initial(max_integrity)
	max_pressure = max_pressure_multiplier * initial(max_pressure)

/// Fills the tank to the maximum safe pressure.
/// Safety margin is a multiplier for the cap for the purpose of this proc so it doesn't have to be filled completely.
/obj/machinery/atmospherics/components/tank/proc/FillToPressure(gastype, safety_margin=0.5)
	var/pressure_limit = max_pressure * safety_margin

	var/moles_to_add = (pressure_limit * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
	air_contents.assert_gas(gastype)
	air_contents.gases[gastype][MOLES] += moles_to_add
	air_contents.archive()
	update_parents()

/obj/machinery/atmospherics/components/tank/process_atmos()
	if(air_contents.react(src))
		update_parents()

	if(air_contents.return_pressure() > max_pressure)
		take_damage(0.1, BRUTE, sound_effect=FALSE)
		if(prob(40))
			playsound(src, pick(breaking_sounds), 30, vary=TRUE)

	RefreshWindow()

///////////////////////////////////////////////////////////////////
// Pipenet stuff

/obj/machinery/atmospherics/components/tank/return_analyzable_air()
	return air_contents

/obj/machinery/atmospherics/components/tank/returnAirsForReconcilation(datum/pipeline/requester)
	. = ..()
	if(!air_contents)
		return
	. += air_contents

/obj/machinery/atmospherics/components/tank/returnPipenetsForReconcilation(datum/pipeline/requester)
	. = ..()
	var/datum/merger/merge_group = GetMergeGroup(merger_id, merger_typecache)
	for(var/obj/machinery/atmospherics/components/tank/tank as anything in merge_group.members)
		. += tank.parents

/obj/machinery/atmospherics/components/tank/proc/ToggleSidePort(new_dir)
	if(initialize_directions & new_dir)
		initialize_directions &= ~new_dir
	else
		initialize_directions |= new_dir
{'fraction_non_alphanumeric': 0.11038053751952001, 'fraction_numerical': 0.007397057614859866, 'mean_word_length': 4.872586872586872, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5040312', 'n_tokens_mistral': 4494, 'n_tokens_neox': 4187, 'n_words': 848}