2023-07-08 14:08:32 +10:00
using System.Numerics ;
2022-08-29 22:31:27 -04:00
using Content.Server.Body.Components ;
2024-03-12 01:59:21 +04:00
using Content.Server.Botany.Components ;
2023-08-28 16:53:24 -07:00
using Content.Server.Fluids.EntitySystems ;
2023-01-11 17:03:18 -05:00
using Content.Server.Materials ;
2023-08-28 16:53:24 -07:00
using Content.Server.Power.Components ;
using Content.Shared.Administration.Logs ;
using Content.Shared.Audio ;
using Content.Shared.CCVar ;
using Content.Shared.Chemistry.Components ;
2023-10-11 10:41:11 +11:00
using Content.Shared.Climbing.Events ;
2023-08-28 16:53:24 -07:00
using Content.Shared.Construction.Components ;
using Content.Shared.Database ;
2023-02-24 19:01:25 -05:00
using Content.Shared.DoAfter ;
2022-12-24 23:28:21 -05:00
using Content.Shared.Humanoid ;
2023-08-28 16:53:24 -07:00
using Content.Shared.Interaction ;
2022-10-04 07:08:46 -04:00
using Content.Shared.Interaction.Events ;
2023-08-28 16:53:24 -07:00
using Content.Shared.Jittering ;
using Content.Shared.Medical ;
2023-08-30 21:46:11 -07:00
using Content.Shared.Mind ;
2024-03-12 01:59:21 +04:00
using Content.Shared.Materials ;
2023-01-13 16:57:10 -08:00
using Content.Shared.Mobs.Components ;
using Content.Shared.Mobs.Systems ;
2023-08-28 16:53:24 -07:00
using Content.Shared.Nutrition.Components ;
2022-10-04 07:08:46 -04:00
using Content.Shared.Popups ;
2023-08-28 16:53:24 -07:00
using Content.Shared.Throwing ;
2022-08-29 22:31:27 -04:00
using Robust.Server.Player ;
2023-11-27 22:12:34 +11:00
using Robust.Shared.Audio.Systems ;
2023-08-28 16:53:24 -07:00
using Robust.Shared.Configuration ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Components ;
2024-03-12 01:59:21 +04:00
using Robust.Shared.Prototypes ;
2023-08-28 16:53:24 -07:00
using Robust.Shared.Random ;
2022-08-29 22:31:27 -04:00
namespace Content.Server.Medical.BiomassReclaimer
{
public sealed class BiomassReclaimerSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _configManager = default ! ;
[Dependency] private readonly MobStateSystem _mobState = default ! ;
[Dependency] private readonly SharedJitteringSystem _jitteringSystem = default ! ;
[Dependency] private readonly SharedAudioSystem _sharedAudioSystem = default ! ;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default ! ;
2022-10-04 07:08:46 -04:00
[Dependency] private readonly SharedPopupSystem _popup = default ! ;
2023-04-10 15:37:03 +10:00
[Dependency] private readonly PuddleSystem _puddleSystem = default ! ;
2022-08-29 22:31:27 -04:00
[Dependency] private readonly ThrowingSystem _throwing = default ! ;
[Dependency] private readonly IRobustRandom _robustRandom = default ! ;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default ! ;
2023-04-03 13:13:48 +12:00
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default ! ;
2022-08-29 22:31:27 -04:00
[Dependency] private readonly IPlayerManager _playerManager = default ! ;
2023-01-11 17:03:18 -05:00
[Dependency] private readonly MaterialStorageSystem _material = default ! ;
2023-08-30 21:46:11 -07:00
[Dependency] private readonly SharedMindSystem _minds = default ! ;
2022-08-29 22:31:27 -04:00
2024-03-12 01:59:21 +04:00
[ValidatePrototypeId<MaterialPrototype>]
public const string BiomassPrototype = "Biomass" ;
2022-08-29 22:31:27 -04:00
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
2023-10-19 12:34:31 -07:00
var query = EntityQueryEnumerator < ActiveBiomassReclaimerComponent , BiomassReclaimerComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var _ , out var reclaimer ) )
2022-08-29 22:31:27 -04:00
{
2022-10-04 03:55:15 +02:00
reclaimer . ProcessingTimer - = frameTime ;
reclaimer . RandomMessTimer - = frameTime ;
2022-08-29 22:31:27 -04:00
2022-10-04 03:55:15 +02:00
if ( reclaimer . RandomMessTimer < = 0 )
2022-08-29 22:31:27 -04:00
{
2022-10-04 03:55:15 +02:00
if ( _robustRandom . Prob ( 0.2f ) & & reclaimer . BloodReagent is not null )
2022-08-29 22:31:27 -04:00
{
2022-10-04 03:55:15 +02:00
Solution blood = new ( ) ;
blood . AddReagent ( reclaimer . BloodReagent , 50 ) ;
2023-10-19 12:34:31 -07:00
_puddleSystem . TrySpillAt ( uid , blood , out _ ) ;
2022-08-29 22:31:27 -04:00
}
2022-10-04 03:55:15 +02:00
if ( _robustRandom . Prob ( 0.03f ) & & reclaimer . SpawnedEntities . Count > 0 )
{
2023-10-19 12:34:31 -07:00
var thrown = Spawn ( _robustRandom . Pick ( reclaimer . SpawnedEntities ) . PrototypeId , Transform ( uid ) . Coordinates ) ;
2023-07-08 14:08:32 +10:00
var direction = new Vector2 ( _robustRandom . Next ( - 30 , 30 ) , _robustRandom . Next ( - 30 , 30 ) ) ;
2022-10-04 03:55:15 +02:00
_throwing . TryThrow ( thrown , direction , _robustRandom . Next ( 1 , 10 ) ) ;
}
reclaimer . RandomMessTimer + = ( float ) reclaimer . RandomMessInterval . TotalSeconds ;
2022-08-29 22:31:27 -04:00
}
2022-10-04 03:55:15 +02:00
if ( reclaimer . ProcessingTimer > 0 )
2022-08-29 22:31:27 -04:00
{
continue ;
}
2024-03-12 01:59:21 +04:00
var actualYield = ( int ) ( reclaimer . CurrentExpectedYield ) ; // can only have integer biomass
reclaimer . CurrentExpectedYield = reclaimer . CurrentExpectedYield - actualYield ; // store non-integer leftovers
_material . SpawnMultipleFromMaterial ( actualYield , BiomassPrototype , Transform ( uid ) . Coordinates ) ;
2022-08-29 22:31:27 -04:00
2022-10-04 03:55:15 +02:00
reclaimer . BloodReagent = null ;
2022-08-29 22:31:27 -04:00
reclaimer . SpawnedEntities . Clear ( ) ;
2023-10-19 12:34:31 -07:00
RemCompDeferred < ActiveBiomassReclaimerComponent > ( uid ) ;
2022-08-29 22:31:27 -04:00
}
}
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < ActiveBiomassReclaimerComponent , ComponentInit > ( OnInit ) ;
SubscribeLocalEvent < ActiveBiomassReclaimerComponent , ComponentShutdown > ( OnShutdown ) ;
SubscribeLocalEvent < ActiveBiomassReclaimerComponent , UnanchorAttemptEvent > ( OnUnanchorAttempt ) ;
SubscribeLocalEvent < BiomassReclaimerComponent , AfterInteractUsingEvent > ( OnAfterInteractUsing ) ;
SubscribeLocalEvent < BiomassReclaimerComponent , ClimbedOnEvent > ( OnClimbedOn ) ;
2022-10-23 00:54:19 +03:00
SubscribeLocalEvent < BiomassReclaimerComponent , PowerChangedEvent > ( OnPowerChanged ) ;
2022-10-04 07:08:46 -04:00
SubscribeLocalEvent < BiomassReclaimerComponent , SuicideEvent > ( OnSuicide ) ;
2023-04-03 13:13:48 +12:00
SubscribeLocalEvent < BiomassReclaimerComponent , ReclaimerDoAfterEvent > ( OnDoAfter ) ;
2022-08-29 22:31:27 -04:00
}
2023-10-19 12:34:31 -07:00
private void OnSuicide ( Entity < BiomassReclaimerComponent > ent , ref SuicideEvent args )
2022-10-04 07:08:46 -04:00
{
if ( args . Handled )
return ;
2022-11-04 04:27:47 +01:00
2023-10-19 12:34:31 -07:00
if ( HasComp < ActiveBiomassReclaimerComponent > ( ent ) )
2022-10-04 07:08:46 -04:00
return ;
2023-10-19 12:34:31 -07:00
if ( TryComp < ApcPowerReceiverComponent > ( ent , out var power ) & & ! power . Powered )
2022-10-04 07:08:46 -04:00
return ;
2023-10-19 12:34:31 -07:00
_popup . PopupEntity ( Loc . GetString ( "biomass-reclaimer-suicide-others" , ( "victim" , args . Victim ) ) , ent , PopupType . LargeCaution ) ;
StartProcessing ( args . Victim , ent ) ;
2022-10-04 07:08:46 -04:00
args . SetHandled ( SuicideKind . Blunt ) ;
}
2022-08-29 22:31:27 -04:00
private void OnInit ( EntityUid uid , ActiveBiomassReclaimerComponent component , ComponentInit args )
{
_jitteringSystem . AddJitter ( uid , - 10 , 100 ) ;
2022-11-22 13:49:48 +13:00
_sharedAudioSystem . PlayPvs ( "/Audio/Machines/reclaimer_startup.ogg" , uid ) ;
2022-08-29 22:31:27 -04:00
_ambientSoundSystem . SetAmbience ( uid , true ) ;
}
private void OnShutdown ( EntityUid uid , ActiveBiomassReclaimerComponent component , ComponentShutdown args )
{
RemComp < JitteringComponent > ( uid ) ;
_ambientSoundSystem . SetAmbience ( uid , false ) ;
}
2022-10-23 00:54:19 +03:00
private void OnPowerChanged ( EntityUid uid , BiomassReclaimerComponent component , ref PowerChangedEvent args )
{
if ( args . Powered )
{
if ( component . ProcessingTimer > 0 )
EnsureComp < ActiveBiomassReclaimerComponent > ( uid ) ;
}
else
2023-10-19 12:34:31 -07:00
RemComp < ActiveBiomassReclaimerComponent > ( uid ) ;
2022-10-23 00:54:19 +03:00
}
2022-08-29 22:31:27 -04:00
private void OnUnanchorAttempt ( EntityUid uid , ActiveBiomassReclaimerComponent component , UnanchorAttemptEvent args )
{
args . Cancel ( ) ;
}
2023-10-19 12:34:31 -07:00
private void OnAfterInteractUsing ( Entity < BiomassReclaimerComponent > reclaimer , ref AfterInteractUsingEvent args )
2022-08-29 22:31:27 -04:00
{
2023-02-24 19:01:25 -05:00
if ( ! args . CanReach | | args . Target = = null )
2022-08-29 22:31:27 -04:00
return ;
2024-03-12 01:59:21 +04:00
if ( ! CanGib ( reclaimer , args . Used ) )
return ;
if ( ! TryComp < PhysicsComponent > ( args . Used , out var physics ) )
2022-12-18 20:10:37 -05:00
return ;
2024-03-12 01:59:21 +04:00
var delay = reclaimer . Comp . BaseInsertionDelay * physics . FixturesMass ;
_doAfterSystem . TryStartDoAfter ( new DoAfterArgs ( EntityManager , args . User , delay , new ReclaimerDoAfterEvent ( ) , reclaimer , target : args . Target , used : args . Used )
2022-08-29 22:31:27 -04:00
{
2024-03-19 12:09:00 +02:00
NeedHand = true ,
BreakOnMove = true
2022-12-18 20:10:37 -05:00
} ) ;
2022-08-29 22:31:27 -04:00
}
2023-10-19 12:34:31 -07:00
private void OnClimbedOn ( Entity < BiomassReclaimerComponent > reclaimer , ref ClimbedOnEvent args )
2022-08-29 22:31:27 -04:00
{
2023-10-19 12:34:31 -07:00
if ( ! CanGib ( reclaimer , args . Climber ) )
2022-08-29 22:31:27 -04:00
{
2023-07-08 14:08:32 +10:00
var direction = new Vector2 ( _robustRandom . Next ( - 2 , 2 ) , _robustRandom . Next ( - 2 , 2 ) ) ;
2022-08-29 22:31:27 -04:00
_throwing . TryThrow ( args . Climber , direction , 0.5f ) ;
return ;
}
2023-10-19 12:34:31 -07:00
_adminLogger . Add ( LogType . Action , LogImpact . Extreme , $"{ToPrettyString(args.Instigator):player} used a biomass reclaimer to gib {ToPrettyString(args.Climber):target} in {ToPrettyString(reclaimer):reclaimer}" ) ;
2022-08-29 22:31:27 -04:00
2023-10-19 12:34:31 -07:00
StartProcessing ( args . Climber , reclaimer ) ;
2022-08-29 22:31:27 -04:00
}
2023-10-19 12:34:31 -07:00
private void OnDoAfter ( Entity < BiomassReclaimerComponent > reclaimer , ref ReclaimerDoAfterEvent args )
2022-08-29 22:31:27 -04:00
{
2024-03-12 01:59:21 +04:00
if ( args . Handled | | args . Cancelled )
return ;
if ( args . Args . Used = = null | | args . Args . Target = = null | | ! HasComp < BiomassReclaimerComponent > ( args . Args . Target . Value ) )
2022-08-29 22:31:27 -04:00
return ;
2023-10-19 12:34:31 -07:00
_adminLogger . Add ( LogType . Action , LogImpact . Extreme , $"{ToPrettyString(args.Args.User):player} used a biomass reclaimer to gib {ToPrettyString(args.Args.Target.Value):target} in {ToPrettyString(reclaimer):reclaimer}" ) ;
2024-03-12 01:59:21 +04:00
StartProcessing ( args . Args . Used . Value , reclaimer ) ;
2022-08-29 22:31:27 -04:00
2023-02-24 19:01:25 -05:00
args . Handled = true ;
2022-08-29 22:31:27 -04:00
}
2022-10-04 03:55:15 +02:00
2023-10-19 12:34:31 -07:00
private void StartProcessing ( EntityUid toProcess , Entity < BiomassReclaimerComponent > ent , PhysicsComponent ? physics = null )
2022-08-29 22:31:27 -04:00
{
2022-10-04 03:55:15 +02:00
if ( ! Resolve ( toProcess , ref physics ) )
return ;
2023-10-19 12:34:31 -07:00
var component = ent . Comp ;
AddComp < ActiveBiomassReclaimerComponent > ( ent ) ;
2022-08-29 22:31:27 -04:00
if ( TryComp < BloodstreamComponent > ( toProcess , out var stream ) )
{
component . BloodReagent = stream . BloodReagent ;
}
2023-02-14 00:29:34 +11:00
if ( TryComp < ButcherableComponent > ( toProcess , out var butcherableComponent ) )
2022-08-29 22:31:27 -04:00
{
component . SpawnedEntities = butcherableComponent . SpawnedEntities ;
}
2024-03-12 01:59:21 +04:00
var expectedYield = physics . FixturesMass * component . YieldPerUnitMass ;
if ( HasComp < ProduceComponent > ( toProcess ) )
expectedYield * = component . ProduceYieldMultiplier ;
component . CurrentExpectedYield + = expectedYield ;
2022-10-04 03:55:15 +02:00
component . ProcessingTimer = physics . FixturesMass * component . ProcessingTimePerUnitMass ;
2024-03-12 01:59:21 +04:00
2022-10-04 03:55:15 +02:00
QueueDel ( toProcess ) ;
2022-08-29 22:31:27 -04:00
}
2023-10-19 12:34:31 -07:00
private bool CanGib ( Entity < BiomassReclaimerComponent > reclaimer , EntityUid dragged )
2022-08-29 22:31:27 -04:00
{
2023-10-19 12:34:31 -07:00
if ( HasComp < ActiveBiomassReclaimerComponent > ( reclaimer ) )
2022-08-29 22:31:27 -04:00
return false ;
2024-03-12 01:59:21 +04:00
bool isPlant = HasComp < ProduceComponent > ( dragged ) ;
if ( ! isPlant & & ! HasComp < MobStateComponent > ( dragged ) )
2022-08-29 22:31:27 -04:00
return false ;
2023-10-19 12:34:31 -07:00
if ( ! Transform ( reclaimer ) . Anchored )
2022-08-29 22:31:27 -04:00
return false ;
2023-10-19 12:34:31 -07:00
if ( TryComp < ApcPowerReceiverComponent > ( reclaimer , out var power ) & & ! power . Powered )
2022-08-29 22:31:27 -04:00
return false ;
2024-03-12 01:59:21 +04:00
if ( ! isPlant & & reclaimer . Comp . SafetyEnabled & & ! _mobState . IsDead ( dragged ) )
2022-08-29 22:31:27 -04:00
return false ;
// Reject souled bodies in easy mode.
2022-09-27 04:00:30 -04:00
if ( _configManager . GetCVar ( CCVars . BiomassEasyMode ) & &
2023-01-24 13:38:19 +13:00
HasComp < HumanoidAppearanceComponent > ( dragged ) & &
2023-08-28 16:53:24 -07:00
_minds . TryGetMind ( dragged , out _ , out var mind ) )
2022-09-27 04:00:30 -04:00
{
2023-08-28 16:53:24 -07:00
if ( mind . UserId ! = null & & _playerManager . TryGetSessionById ( mind . UserId . Value , out _ ) )
2022-09-27 04:00:30 -04:00
return false ;
}
2022-08-29 22:31:27 -04:00
return true ;
}
}
}