2021-11-08 15:33:45 -07:00
using System.Linq ;
2021-11-11 16:10:57 -07:00
using Content.Server.Body.Components ;
2021-11-08 15:33:45 -07:00
using Content.Server.Chemistry.Components.SolutionManager ;
2021-10-29 13:40:15 +01:00
using Content.Server.Chemistry.EntitySystems ;
2021-11-22 23:51:43 -07:00
using Content.Shared.Administration.Logs ;
2022-01-04 02:17:39 -07:00
using Content.Shared.Body.Components ;
2021-09-06 15:49:44 +02:00
using Content.Shared.Chemistry.Components ;
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 ;
2021-11-10 03:11:28 -07:00
using Content.Shared.MobState.Components ;
2021-09-06 15:49:44 +02:00
using JetBrains.Annotations ;
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
{
2021-09-06 15:49:44 +02:00
[UsedImplicitly]
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 SolutionContainerSystem _solutionContainerSystem = default ! ;
[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 ! ;
2021-09-06 15:49:44 +02:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < MetabolizerComponent , ComponentInit > ( OnMetabolizerInit ) ;
2022-04-15 18:53:52 -04:00
SubscribeLocalEvent < MetabolizerComponent , ApplyMetabolicMultiplierEvent > ( OnApplyMetabolicMultiplier ) ;
2021-09-06 15:49:44 +02:00
}
private void OnMetabolizerInit ( EntityUid uid , MetabolizerComponent component , ComponentInit args )
{
2021-11-08 15:33:45 -07:00
if ( ! component . SolutionOnBody )
{
_solutionContainerSystem . EnsureSolution ( uid , component . SolutionName ) ;
}
else
{
if ( EntityManager . TryGetComponent < MechanismComponent > ( uid , out var mech ) )
{
if ( mech . Body ! = null )
{
2021-12-07 22:22:34 +11:00
_solutionContainerSystem . EnsureSolution ( ( mech . Body ) . Owner , component . SolutionName ) ;
2021-11-08 15:33:45 -07:00
}
}
}
2021-09-06 15:49:44 +02:00
}
2022-04-15 18:53:52 -04:00
private void OnApplyMetabolicMultiplier ( EntityUid uid , MetabolizerComponent component , ApplyMetabolicMultiplierEvent args )
{
if ( args . Apply )
{
component . UpdateFrequency * = args . Multiplier ;
return ;
}
component . UpdateFrequency / = args . Multiplier ;
// Reset the accumulator properly
if ( component . AccumulatedFrametime > = component . UpdateFrequency )
component . AccumulatedFrametime = component . UpdateFrequency ;
}
2021-07-31 04:50:32 -07:00
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
2021-09-28 13:35:29 +02:00
foreach ( var metab in EntityManager . EntityQuery < MetabolizerComponent > ( false ) )
2021-07-31 04:50:32 -07:00
{
metab . AccumulatedFrametime + = frameTime ;
// Only update as frequently as it should
if ( metab . AccumulatedFrametime > = metab . UpdateFrequency )
{
2021-11-08 15:33:45 -07:00
metab . AccumulatedFrametime - = metab . UpdateFrequency ;
2021-12-07 22:22:34 +11:00
TryMetabolize ( ( metab ) . Owner , metab ) ;
2021-07-31 04:50:32 -07:00
}
}
}
2021-11-08 15:33:45 -07:00
private void TryMetabolize ( EntityUid uid , MetabolizerComponent ? meta = null , MechanismComponent ? mech = null )
2021-07-31 04:50:32 -07:00
{
2021-11-08 15:33:45 -07:00
if ( ! Resolve ( uid , ref meta ) )
return ;
Resolve ( uid , ref mech , false ) ;
// First step is get the solution we actually care about
2021-09-06 15:49:44 +02:00
Solution ? solution = null ;
2021-11-08 15:33:45 -07:00
EntityUid ? solutionEntityUid = null ;
2022-02-10 17:53:10 +13:00
EntityUid ? bodyEntityUid = mech ? . Body ? . Owner ;
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
{
2021-11-08 15:33:45 -07:00
if ( mech ! = null )
2021-07-31 04:50:32 -07:00
{
2021-11-08 15:33:45 -07:00
var body = mech . Body ;
if ( body ! = null )
2021-07-31 04:50:32 -07:00
{
2021-12-07 22:22:34 +11:00
if ( ! Resolve ( ( body ) . Owner , ref manager , false ) )
2021-11-08 15:33:45 -07:00
return ;
2021-12-07 22:22:34 +11:00
_solutionContainerSystem . TryGetSolution ( ( body ) . Owner , meta . SolutionName , out solution , manager ) ;
2021-12-05 18:09:01 +01:00
solutionEntityUid = body . Owner ;
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
{
2021-11-08 15:33:45 -07:00
if ( ! Resolve ( uid , ref manager , false ) )
return ;
_solutionContainerSystem . TryGetSolution ( uid , meta . SolutionName , out solution , manager ) ;
solutionEntityUid = uid ;
2021-07-31 04:50:32 -07:00
}
2021-11-08 15:33:45 -07:00
if ( solutionEntityUid = = null | | solution = = null )
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 ;
foreach ( var reagent in list )
2021-07-31 04:50:32 -07:00
{
2021-11-08 15:33:45 -07:00
if ( ! _prototypeManager . TryIndex < ReagentPrototype > ( reagent . ReagentId , out var proto ) )
continue ;
2021-11-10 03:11:28 -07:00
FixedPoint2 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 )
_solutionContainerSystem . TryRemoveReagent ( solutionEntityUid . Value , solution , reagent . ReagentId , FixedPoint2 . New ( 1 ) ) ;
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 ;
reagents + = 1 ;
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
{
2021-11-08 15:33:45 -07:00
if ( ! proto . Metabolisms . Keys . Contains ( group . Id ) )
continue ;
var entry = proto . Metabolisms [ group . Id ] ;
// we don't remove reagent for every group, just whichever had the biggest rate
if ( entry . MetabolismRate > mostToRemove )
mostToRemove = entry . MetabolismRate ;
2022-02-10 17:53:10 +13:00
mostToRemove * = group . MetabolismRateModifier ;
mostToRemove = FixedPoint2 . Clamp ( mostToRemove , 0 , reagent . Quantity ) ;
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 ) )
{
if ( state . IsDead ( ) )
continue ;
}
2022-02-10 17:53:10 +13:00
var actualEntity = bodyEntityUid ! = null ? bodyEntityUid . Value : solutionEntityUid . Value ;
var args = new ReagentEffectArgs ( actualEntity , ( meta ) . Owner , solution , proto , mostToRemove ,
2021-11-10 03:11:28 -07:00
EntityManager , null ) ;
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 ,
2022-05-12 14:06:01 +03:00
$"Metabolism effect {effect.GetType().Name:effect} of reagent {args.Reagent.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 )
_solutionContainerSystem . TryRemoveReagent ( solutionEntityUid . Value , solution , reagent . ReagentId , mostToRemove ) ;
2021-07-31 04:50:32 -07:00
}
}
}
2022-04-15 18:53:52 -04:00
public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs
{
// The entity whose metabolism is being modified
public EntityUid Uid ;
// What the metabolism's update rate will be multiplied by
public float Multiplier ;
// Apply this multiplier or ignore / reset it?
public bool Apply ;
}
2021-07-31 04:50:32 -07:00
}