2021-11-11 16:10:57 -07:00
using Content.Server.Body.Components ;
2023-12-29 04:47:43 -08:00
using Content.Server.Chemistry.Containers.EntitySystems ;
2021-11-22 23:51:43 -07:00
using Content.Shared.Administration.Logs ;
2022-10-23 00:46:28 +02:00
using Content.Shared.Body.Organ ;
2021-09-06 15:49:44 +02:00
using Content.Shared.Chemistry.Components ;
2023-10-14 09:45:28 -07:00
using Content.Shared.Chemistry.Components.SolutionManager ;
2021-07-31 04:50:32 -07:00
using Content.Shared.Chemistry.Reagent ;
2021-11-28 14:56:53 +01:00
using Content.Shared.Database ;
2021-11-03 16:48:03 -07:00
using Content.Shared.FixedPoint ;
2023-01-13 16:57:10 -08:00
using Content.Shared.Mobs.Components ;
using Content.Shared.Mobs.Systems ;
2023-08-09 13:47:44 +10:00
using Robust.Shared.Collections ;
2021-11-08 15:33:45 -07:00
using Robust.Shared.Prototypes ;
2021-11-10 03:11:28 -07:00
using Robust.Shared.Random ;
2021-07-31 04:50:32 -07:00
2021-11-11 16:10:57 -07:00
namespace Content.Server.Body.Systems
2021-07-31 04:50:32 -07:00
{
2022-02-16 00:23:23 -07:00
public sealed class MetabolizerSystem : EntitySystem
2021-07-31 04:50:32 -07:00
{
2021-11-08 15:33:45 -07:00
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
2021-11-10 03:11:28 -07:00
[Dependency] private readonly IRobustRandom _random = default ! ;
2022-05-28 23:41:17 -07:00
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default ! ;
2023-08-09 13:47:44 +10:00
[Dependency] private readonly MobStateSystem _mobStateSystem = default ! ;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default ! ;
private EntityQuery < OrganComponent > _organQuery ;
private EntityQuery < SolutionContainerManagerComponent > _solutionQuery ;
2021-09-06 15:49:44 +02:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2023-08-09 13:47:44 +10:00
_organQuery = GetEntityQuery < OrganComponent > ( ) ;
_solutionQuery = GetEntityQuery < SolutionContainerManagerComponent > ( ) ;
2021-09-06 15:49:44 +02:00
SubscribeLocalEvent < MetabolizerComponent , ComponentInit > ( OnMetabolizerInit ) ;
2022-04-15 18:53:52 -04:00
SubscribeLocalEvent < MetabolizerComponent , ApplyMetabolicMultiplierEvent > ( OnApplyMetabolicMultiplier ) ;
2021-09-06 15:49:44 +02:00
}
2023-12-29 04:47:43 -08:00
private void OnMetabolizerInit ( Entity < MetabolizerComponent > entity , ref ComponentInit args )
2021-09-06 15:49:44 +02:00
{
2023-12-29 04:47:43 -08:00
if ( ! entity . Comp . SolutionOnBody )
2021-11-08 15:33:45 -07:00
{
2023-12-29 04:47:43 -08:00
_solutionContainerSystem . EnsureSolution ( entity . Owner , entity . Comp . SolutionName ) ;
2021-11-08 15:33:45 -07:00
}
2023-12-29 04:47:43 -08:00
else if ( _organQuery . CompOrNull ( entity ) ? . Body is { } body )
2021-11-08 15:33:45 -07:00
{
2023-12-29 04:47:43 -08:00
_solutionContainerSystem . EnsureSolution ( body , entity . Comp . SolutionName ) ;
2021-11-08 15:33:45 -07:00
}
2021-09-06 15:49:44 +02:00
}
2022-10-23 00:46:28 +02:00
private void OnApplyMetabolicMultiplier ( EntityUid uid , MetabolizerComponent component ,
ApplyMetabolicMultiplierEvent args )
2022-04-15 18:53:52 -04:00
{
if ( args . Apply )
{
component . UpdateFrequency * = args . Multiplier ;
return ;
}
2022-10-23 00:46:28 +02:00
2022-04-15 18:53:52 -04:00
component . UpdateFrequency / = args . Multiplier ;
// Reset the accumulator properly
if ( component . AccumulatedFrametime > = component . UpdateFrequency )
component . AccumulatedFrametime = component . UpdateFrequency ;
}
2022-10-23 00:46:28 +02:00
2021-07-31 04:50:32 -07:00
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
2023-08-09 13:47:44 +10:00
var metabolizers = new ValueList < ( EntityUid Uid , MetabolizerComponent Component ) > ( Count < MetabolizerComponent > ( ) ) ;
var query = EntityQueryEnumerator < MetabolizerComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var comp ) )
{
metabolizers . Add ( ( uid , comp ) ) ;
}
foreach ( var ( uid , metab ) in metabolizers )
2021-07-31 04:50:32 -07:00
{
metab . AccumulatedFrametime + = frameTime ;
// Only update as frequently as it should
2023-08-09 13:47:44 +10:00
if ( metab . AccumulatedFrametime < metab . UpdateFrequency )
continue ;
metab . AccumulatedFrametime - = metab . UpdateFrequency ;
TryMetabolize ( uid , metab ) ;
2021-07-31 04:50:32 -07:00
}
}
2023-08-09 13:47:44 +10:00
private void TryMetabolize ( EntityUid uid , MetabolizerComponent meta , OrganComponent ? organ = null )
2021-07-31 04:50:32 -07:00
{
2023-08-09 13:47:44 +10:00
_organQuery . Resolve ( uid , ref organ , false ) ;
2021-11-08 15:33:45 -07:00
// First step is get the solution we actually care about
2021-09-06 15:49:44 +02:00
Solution ? solution = null ;
2023-12-29 04:47:43 -08:00
Entity < SolutionComponent > ? soln = default ! ;
2021-11-08 15:33:45 -07:00
EntityUid ? solutionEntityUid = null ;
2022-02-10 17:53:10 +13:00
2021-11-08 15:33:45 -07:00
SolutionContainerManagerComponent ? manager = null ;
2021-07-31 04:50:32 -07:00
2021-11-08 15:33:45 -07:00
if ( meta . SolutionOnBody )
2021-07-31 04:50:32 -07:00
{
2022-10-23 00:46:28 +02:00
if ( organ ? . Body is { } body )
2021-07-31 04:50:32 -07:00
{
2023-08-09 13:47:44 +10:00
if ( ! _solutionQuery . Resolve ( body , ref manager , false ) )
2022-10-23 00:46:28 +02:00
return ;
2023-08-09 13:47:44 +10:00
2023-12-29 04:47:43 -08:00
_solutionContainerSystem . TryGetSolution ( ( body , manager ) , meta . SolutionName , out soln , out solution ) ;
2022-10-23 00:46:28 +02:00
solutionEntityUid = body ;
2021-07-31 04:50:32 -07:00
}
}
2021-11-08 15:33:45 -07:00
else
2021-07-31 04:50:32 -07:00
{
2023-08-09 13:47:44 +10:00
if ( ! _solutionQuery . Resolve ( uid , ref manager , false ) )
2021-11-08 15:33:45 -07:00
return ;
2023-08-09 13:47:44 +10:00
2023-12-29 04:47:43 -08:00
_solutionContainerSystem . TryGetSolution ( ( uid , manager ) , meta . SolutionName , out soln , out solution ) ;
2021-11-08 15:33:45 -07:00
solutionEntityUid = uid ;
2021-07-31 04:50:32 -07:00
}
2023-12-29 04:47:43 -08:00
if ( solutionEntityUid = = null | | soln is null | | solution is null | | solution . Contents . Count = = 0 )
2021-11-08 15:33:45 -07:00
return ;
2021-11-10 03:11:28 -07:00
// randomize the reagent list so we don't have any weird quirks
// like alphabetical order or insertion order mattering for processing
var list = solution . Contents . ToArray ( ) ;
_random . Shuffle ( list ) ;
int reagents = 0 ;
2023-09-05 09:55:10 +12:00
foreach ( var ( reagent , quantity ) in list )
2021-07-31 04:50:32 -07:00
{
2023-09-05 09:55:10 +12:00
if ( ! _prototypeManager . TryIndex < ReagentPrototype > ( reagent . Prototype , out var proto ) )
2021-11-08 15:33:45 -07:00
continue ;
2023-08-09 13:47:44 +10:00
var mostToRemove = FixedPoint2 . Zero ;
2021-11-08 15:33:45 -07:00
if ( proto . Metabolisms = = null )
2021-11-10 03:11:28 -07:00
{
if ( meta . RemoveEmpty )
2023-08-09 13:47:44 +10:00
{
2023-12-29 04:47:43 -08:00
solution . RemoveReagent ( reagent , FixedPoint2 . New ( 1 ) ) ;
2023-08-09 13:47:44 +10:00
}
2021-07-31 04:50:32 -07:00
continue ;
2021-11-10 03:11:28 -07:00
}
// we're done here entirely if this is true
if ( reagents > = meta . MaxReagentsProcessable )
return ;
2023-08-09 13:47:44 +10:00
2021-07-31 04:50:32 -07:00
2021-11-08 15:33:45 -07:00
// loop over all our groups and see which ones apply
2021-11-10 03:11:28 -07:00
if ( meta . MetabolismGroups = = null )
continue ;
2021-11-08 15:33:45 -07:00
foreach ( var group in meta . MetabolismGroups )
2021-07-31 04:50:32 -07:00
{
2023-12-24 23:12:22 -05:00
if ( ! proto . Metabolisms . TryGetValue ( group . Id , out var entry ) )
2021-11-08 15:33:45 -07:00
continue ;
2023-09-22 13:01:42 -07:00
var rate = entry . MetabolismRate * group . MetabolismRateModifier ;
2021-11-08 15:33:45 -07:00
2023-09-22 13:01:42 -07:00
// Remove $rate, as long as there's enough reagent there to actually remove that much
mostToRemove = FixedPoint2 . Clamp ( rate , 0 , quantity ) ;
2021-11-08 15:33:45 -07:00
2023-09-22 13:01:42 -07:00
float scale = ( float ) mostToRemove / ( float ) rate ;
2022-12-21 09:51:49 -05:00
2021-11-10 03:11:28 -07:00
// if it's possible for them to be dead, and they are,
// then we shouldn't process any effects, but should probably
// still remove reagents
if ( EntityManager . TryGetComponent < MobStateComponent > ( solutionEntityUid . Value , out var state ) )
{
2024-01-05 04:40:57 +01:00
if ( ! proto . WorksOnTheDead & & _mobStateSystem . IsDead ( solutionEntityUid . Value , state ) )
2021-11-10 03:11:28 -07:00
continue ;
}
2022-10-23 00:46:28 +02:00
var actualEntity = organ ? . Body ? ? solutionEntityUid . Value ;
2023-08-09 13:47:44 +10:00
var args = new ReagentEffectArgs ( actualEntity , uid , solution , proto , mostToRemove ,
2022-12-21 09:51:49 -05:00
EntityManager , null , scale ) ;
2021-11-10 03:11:28 -07:00
2021-11-08 15:33:45 -07:00
// do all effects, if conditions apply
foreach ( var effect in entry . Effects )
2021-07-31 04:50:32 -07:00
{
2021-11-20 16:47:53 -07:00
if ( ! effect . ShouldApply ( args , _random ) )
continue ;
2021-07-31 04:50:32 -07:00
2021-11-27 00:31:56 -07:00
if ( effect . ShouldLog )
{
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . ReagentEffect , effect . LogImpact ,
2023-05-13 15:10:32 +12:00
$"Metabolism effect {effect.GetType().Name:effect} of reagent {proto.LocalizedName:reagent} applied on entity {actualEntity:entity} at {Transform(actualEntity).Coordinates:coordinates}" ) ;
2021-11-27 00:31:56 -07:00
}
2021-11-27 01:27:47 -07:00
effect . Effect ( args ) ;
2021-11-08 15:33:45 -07:00
}
2021-07-31 04:50:32 -07:00
}
2021-11-08 15:33:45 -07:00
// remove a certain amount of reagent
if ( mostToRemove > FixedPoint2 . Zero )
2023-08-09 13:47:44 +10:00
{
2023-12-29 04:47:43 -08:00
solution . RemoveReagent ( reagent , mostToRemove ) ;
2023-09-22 13:01:42 -07:00
// We have processed a reagant, so count it towards the cap
reagents + = 1 ;
2023-08-09 13:47:44 +10:00
}
2021-07-31 04:50:32 -07:00
}
2023-12-29 04:47:43 -08:00
_solutionContainerSystem . UpdateChemicals ( soln . Value ) ;
2021-07-31 04:50:32 -07:00
}
}
2022-10-23 00:46:28 +02:00
2022-04-15 18:53:52 -04:00
public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs
{
// The entity whose metabolism is being modified
2022-10-23 00:46:28 +02:00
public EntityUid Uid ;
2022-04-15 18:53:52 -04:00
// What the metabolism's update rate will be multiplied by
2022-10-23 00:46:28 +02:00
public float Multiplier ;
2022-04-15 18:53:52 -04:00
// Apply this multiplier or ignore / reset it?
public bool Apply ;
}
2021-07-31 04:50:32 -07:00
}