diff --git a/code/__DEFINES/reagents/reagent.dm b/code/__DEFINES/reagents/reagent.dm index 834f06b97eeb..a4050c3d658c 100644 --- a/code/__DEFINES/reagents/reagent.dm +++ b/code/__DEFINES/reagents/reagent.dm @@ -18,3 +18,13 @@ DEFINE_SHARED_BITFIELD(reagent_guidebook_flags, list( BITFIELD(REAGENT_GUIDEBOOK_UNLISTED), BITFIELD(REAGENT_GUIDEBOOK_HIDDEN), )) + +//* flags for /datum/reagent/var/reagent_filter_flags *// + +/// the default flag that reagents have by default. +#define REAGENT_FILTER_GENERIC (1<<0) + +/// **disallow** from common medical machinery, like sleepers. +/// +/// * high-end reagents filtration / systems should be able to hit this +#define REAGENT_FILTER_NO_COMMON_BIOANALYSIS (1<<23) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 080b7d55293b..14d54e5a7a68 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -178,6 +178,9 @@ idle_power_usage = 15 active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. + /// filter flags we use for dialysis + var/dialysis_reagent_filter_flags = ~REAGENT_FILTER_NO_COMMON_BIOANALYSIS + /obj/machinery/sleeper/Initialize(mapload) . = ..() beaker = new /obj/item/reagent_containers/glass/beaker/large(src) @@ -243,21 +246,32 @@ if(filtering > 0) if(beaker) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - var/pumped = 0 - for(var/datum/reagent/x in occupant.reagents.reagent_list) - occupant.reagents.trans_to_obj(beaker, 3) - pumped++ - if(ishuman(occupant)) - occupant.vessel.trans_to_obj(beaker, pumped + 1) + // filter 3 units per chem-type inside them, or remaining volume, whichever is lesser + // we will also pump out 1/3 of that volume as their blood. + var/remaining_beaker_volume_for_dialysis = beaker.reagents.maximum_volume - beaker.reagents.total_volume + var/filtered_volume = occupant.reagents?.filter_to_holder( + beaker.reagents, + min( + remaining_beaker_volume_for_dialysis * (3 / 4), + length(occupant.reagents.reagent_list) * 3, + ), + dialysis_reagent_filter_flags, + ) + occupant.vessel.trans_to_holder(beaker.reagents, filtered_volume * (1 / 3)) else toggle_filter() if(pumping > 0) if(beaker) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - for(var/datum/reagent/x in occupant.ingested.reagent_list) - occupant.ingested.trans_to_obj(beaker, 3) + // filter 3 units per chem-type inside them, or remaining volume, whichever is lesser + occupant.ingested?.filter_to_holder( + beaker.reagents, + min( + beaker.reagents.maximum_volume - beaker.reagents.total_volume, + length(occupant.ingested.reagent_list) * 3, + ), + dialysis_reagent_filter_flags, + ) else toggle_pump() diff --git a/code/modules/reagents/chemistry/reagent.dm b/code/modules/reagents/chemistry/reagent.dm index a84786770768..2b8dc3122323 100644 --- a/code/modules/reagents/chemistry/reagent.dm +++ b/code/modules/reagents/chemistry/reagent.dm @@ -17,6 +17,14 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) /// reagent flags - see [code/__DEFINES/reagents/flags.dm] var/reagent_flags = NONE + //* Filtering *// + /// reagent filter flags - dynamic flags used for simulations of filtration/identification/detection + /// + /// * used for a lot of things + /// * REAGENT_FILTER_GENERIC is a default because this allows us to have a single 'flags' on filter, + /// instead of a 'include flags' and 'exclude flags'. + var/reagent_filter_flags = REAGENT_FILTER_GENERIC + //* Identity /// our name - visible from guidebooks and to admins var/name = "Reagent" diff --git a/code/modules/reagents/chemistry/reagent_holder.dm b/code/modules/reagents/chemistry/reagent_holder.dm index f054e2c452e4..7ee0d420823b 100644 --- a/code/modules/reagents/chemistry/reagent_holder.dm +++ b/code/modules/reagents/chemistry/reagent_holder.dm @@ -539,6 +539,49 @@ R.on_update (A) update_total() +//* Filtering *// + +/** + * Filters chemicals by `reagent_filter_flags` + * + * @params + * * transfer_to - where to transfer to + * * amount - volume limit + * * flags - only these flags are allowed + */ +/datum/reagent_holder/proc/filter_to_holder(datum/reagent_holder/transfer_to, amount = INFINITY, flags) + if(amount <= 0) + return + var/list/filtering_ids = list() + for(var/datum/reagent/reagent in reagent_list) + if(!(reagent.reagent_filter_flags & flags)) + continue + filtering_ids += reagent.id + return transfer_to_holder(transfer_to, filtering_ids, amount) + +/** + * Filters chemicals by `reagent_filter_flags` + * + * @params + * * amount - volume limit + * * flags - only these flags are allowed + */ +/datum/reagent_holder/proc/filter_to_void(amount = INFINITY, flags) + if(amount <= 0) + return + var/total_filterable = 0 + var/list/datum/reagent/filtering = list() + for(var/datum/reagent/reagent in reagent_list) + if(!(reagent.reagent_filter_flags & flags)) + continue + total_filterable += reagent.volume + filtering += reagent + var/ratio = amount / total_filterable + for(var/datum/reagent/to_filter in filtering) + remove_reagent(to_filter.id, to_filter.volume * ratio, TRUE) + reconsider_reactions() + return min(amount, total_filterable) + //* Queries *// /** @@ -620,7 +663,7 @@ if(!total_volume) return if(!reagents) - var/ratio = min(1, (target.maximum_volume - target.total_volume) / total_volume) + var/ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_volume) . = total_volume * ratio if(!copy) for(var/datum/reagent/R as anything in reagent_list) @@ -646,7 +689,7 @@ reagents_transferring += R if(!total_transferable) return 0 - var/ratio = min(1, (target.maximum_volume - target.total_volume) / total_transferable) + var/ratio = min(1, min(amount, target.maximum_volume - target.total_volume) / total_transferable) . = total_transferable * ratio if(!copy) for(var/datum/reagent/R as anything in reagents_transferring)