2024-08-14 16:04:00 +03:00
using System.Numerics ;
2024-08-10 22:31:32 +03:00
using System.Text ;
using Content.Server.Nutrition.Components ;
using Content.Shared.Chemistry.EntitySystems ;
using Content.Shared.Interaction ;
2024-08-14 16:04:00 +03:00
using Content.Shared.Mobs.Systems ;
2024-09-08 09:22:27 +03:00
using Content.Shared.Nutrition ;
2024-08-10 22:31:32 +03:00
using Content.Shared.Nutrition.Components ;
using Content.Shared.Nutrition.EntitySystems ;
2024-09-08 09:22:27 +03:00
using Content.Shared.Nutrition.Prototypes ;
2024-08-10 22:31:32 +03:00
using Content.Shared.Popups ;
2024-08-14 15:47:03 -04:00
using Content.Shared.Tag ;
2024-09-08 09:22:27 +03:00
using Robust.Server.GameObjects ;
using Robust.Shared.Prototypes ;
2024-08-14 16:04:00 +03:00
using Robust.Shared.Random ;
2024-08-10 22:31:32 +03:00
namespace Content.Server.Nutrition.EntitySystems ;
public sealed class FoodSequenceSystem : SharedFoodSequenceSystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default ! ;
[Dependency] private readonly SharedPopupSystem _popup = default ! ;
[Dependency] private readonly MetaDataSystem _metaData = default ! ;
2024-08-14 16:04:00 +03:00
[Dependency] private readonly MobStateSystem _mobState = default ! ;
2024-08-14 15:47:03 -04:00
[Dependency] private readonly TagSystem _tag = default ! ;
2024-08-14 16:04:00 +03:00
[Dependency] private readonly IRobustRandom _random = default ! ;
2024-09-08 09:22:27 +03:00
[Dependency] private readonly IPrototypeManager _proto = default ! ;
[Dependency] private readonly TransformSystem _transform = default ! ;
2024-08-10 22:31:32 +03:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < FoodSequenceStartPointComponent , InteractUsingEvent > ( OnInteractUsing ) ;
2024-09-08 09:22:27 +03:00
SubscribeLocalEvent < FoodMetamorphableByAddingComponent , FoodSequenceIngredientAddedEvent > ( OnIngredientAdded ) ;
2024-08-10 22:31:32 +03:00
}
private void OnInteractUsing ( Entity < FoodSequenceStartPointComponent > ent , ref InteractUsingEvent args )
{
if ( TryComp < FoodSequenceElementComponent > ( args . Used , out var sequenceElement ) )
TryAddFoodElement ( ent , ( args . Used , sequenceElement ) , args . User ) ;
}
2024-09-08 09:22:27 +03:00
private void OnIngredientAdded ( Entity < FoodMetamorphableByAddingComponent > ent , ref FoodSequenceIngredientAddedEvent args )
{
if ( ! TryComp < FoodSequenceStartPointComponent > ( args . Start , out var start ) )
return ;
if ( ! _proto . TryIndex ( args . Proto , out var elementProto ) )
return ;
if ( ! ent . Comp . OnlyFinal | | elementProto . Final | | start . FoodLayers . Count = = start . MaxLayers )
{
TryMetamorph ( ( ent , start ) ) ;
}
}
private bool TryMetamorph ( Entity < FoodSequenceStartPointComponent > start )
2024-08-10 22:31:32 +03:00
{
2024-09-08 09:22:27 +03:00
List < MetamorphRecipePrototype > availableRecipes = new ( ) ;
foreach ( var recipe in _proto . EnumeratePrototypes < MetamorphRecipePrototype > ( ) )
2024-08-10 22:31:32 +03:00
{
2024-09-08 09:22:27 +03:00
if ( recipe . Key ! = start . Comp . Key )
continue ;
bool allowed = true ;
foreach ( var rule in recipe . Rules )
2024-08-10 22:31:32 +03:00
{
2024-09-08 09:22:27 +03:00
if ( ! rule . Check ( _proto , EntityManager , start , start . Comp . FoodLayers ) )
{
allowed = false ;
break ;
}
2024-08-10 22:31:32 +03:00
}
2024-09-08 09:22:27 +03:00
if ( allowed )
availableRecipes . Add ( recipe ) ;
2024-08-10 22:31:32 +03:00
}
2024-09-08 09:22:27 +03:00
if ( availableRecipes . Count < = 0 )
return true ;
Metamorf ( start , _random . Pick ( availableRecipes ) ) ; //In general, if there's more than one recipe, the yml-guys screwed up. Maybe some kind of unit test is needed.
QueueDel ( start ) ;
return true ;
}
private void Metamorf ( Entity < FoodSequenceStartPointComponent > start , MetamorphRecipePrototype recipe )
{
var result = SpawnAtPosition ( recipe . Result , Transform ( start ) . Coordinates ) ;
//Try putting in container
_transform . DropNextTo ( result , ( start , Transform ( start ) ) ) ;
if ( ! _solutionContainer . TryGetSolution ( result , start . Comp . Solution , out var resultSoln , out var resultSolution ) )
return ;
if ( ! _solutionContainer . TryGetSolution ( start . Owner , start . Comp . Solution , out var startSoln , out var startSolution ) )
return ;
_solutionContainer . RemoveAllSolution ( resultSoln . Value ) ; //Remove all YML reagents
resultSoln . Value . Comp . Solution . MaxVolume = startSoln . Value . Comp . Solution . MaxVolume ;
_solutionContainer . TryAddSolution ( resultSoln . Value , startSolution ) ;
MergeFlavorProfiles ( start , result ) ;
MergeTrash ( start , result ) ;
MergeTags ( start , result ) ;
}
private bool TryAddFoodElement ( Entity < FoodSequenceStartPointComponent > start , Entity < FoodSequenceElementComponent > element , EntityUid ? user = null )
{
// we can't add a live mouse to a burger.
if ( ! TryComp < FoodComponent > ( element , out var elementFood ) )
return false ;
if ( elementFood . RequireDead & & _mobState . IsAlive ( element ) )
2024-08-10 22:31:32 +03:00
return false ;
2024-09-08 09:22:27 +03:00
//looking for a suitable FoodSequence prototype
2024-09-10 16:08:04 +03:00
if ( ! element . Comp . Entries . TryGetValue ( start . Comp . Key , out var elementProto ) )
return false ;
2024-09-08 09:22:27 +03:00
if ( ! _proto . TryIndex ( elementProto , out var elementIndexed ) )
return false ;
2024-08-14 16:04:00 +03:00
2024-08-10 22:31:32 +03:00
//if we run out of space, we can still put in one last, final finishing element.
2024-09-08 09:22:27 +03:00
if ( start . Comp . FoodLayers . Count > = start . Comp . MaxLayers & & ! elementIndexed . Final | | start . Comp . Finished )
2024-08-10 22:31:32 +03:00
{
if ( user is not null )
_popup . PopupEntity ( Loc . GetString ( "food-sequence-no-space" ) , start , user . Value ) ;
return false ;
}
2024-09-08 09:22:27 +03:00
//Generate new visual layer
var flip = start . Comp . AllowHorizontalFlip & & _random . Prob ( 0.5f ) ;
var layer = new FoodSequenceVisualLayer ( elementIndexed ,
_random . Pick ( elementIndexed . Sprites ) ,
2024-09-13 16:02:54 +02:00
new Vector2 ( flip ? - elementIndexed . Scale . X : elementIndexed . Scale . X , elementIndexed . Scale . Y ) ,
2024-09-08 09:22:27 +03:00
new Vector2 (
_random . NextFloat ( start . Comp . MinLayerOffset . X , start . Comp . MaxLayerOffset . X ) ,
_random . NextFloat ( start . Comp . MinLayerOffset . Y , start . Comp . MaxLayerOffset . Y ) )
) ;
start . Comp . FoodLayers . Add ( layer ) ;
2024-08-14 16:04:00 +03:00
Dirty ( start ) ;
2024-09-08 09:22:27 +03:00
if ( elementIndexed . Final )
2024-08-10 22:31:32 +03:00
start . Comp . Finished = true ;
UpdateFoodName ( start ) ;
MergeFoodSolutions ( start , element ) ;
MergeFlavorProfiles ( start , element ) ;
MergeTrash ( start , element ) ;
2024-08-14 15:47:03 -04:00
MergeTags ( start , element ) ;
2024-09-08 09:22:27 +03:00
var ev = new FoodSequenceIngredientAddedEvent ( start , element , elementProto , user ) ;
RaiseLocalEvent ( start , ev ) ;
2024-08-10 22:31:32 +03:00
QueueDel ( element ) ;
return true ;
}
private void UpdateFoodName ( Entity < FoodSequenceStartPointComponent > start )
{
if ( start . Comp . NameGeneration is null )
return ;
var content = new StringBuilder ( ) ;
var separator = "" ;
if ( start . Comp . ContentSeparator is not null )
separator = Loc . GetString ( start . Comp . ContentSeparator ) ;
2024-09-08 09:22:27 +03:00
HashSet < ProtoId < FoodSequenceElementPrototype > > existedContentNames = new ( ) ;
2024-08-10 22:31:32 +03:00
foreach ( var layer in start . Comp . FoodLayers )
{
2024-09-08 09:22:27 +03:00
if ( ! existedContentNames . Contains ( layer . Proto ) )
existedContentNames . Add ( layer . Proto ) ;
2024-08-14 16:04:00 +03:00
}
var nameCounter = 1 ;
2024-09-08 09:22:27 +03:00
foreach ( var proto in existedContentNames )
2024-08-14 16:04:00 +03:00
{
2024-09-08 09:22:27 +03:00
if ( ! _proto . TryIndex ( proto , out var protoIndexed ) )
continue ;
if ( protoIndexed . Name is null )
continue ;
content . Append ( Loc . GetString ( protoIndexed . Name . Value ) ) ;
2024-08-10 22:31:32 +03:00
2024-08-14 16:04:00 +03:00
if ( nameCounter < existedContentNames . Count )
content . Append ( separator ) ;
nameCounter + + ;
2024-08-10 22:31:32 +03:00
}
var newName = Loc . GetString ( start . Comp . NameGeneration . Value ,
( "prefix" , start . Comp . NamePrefix is not null ? Loc . GetString ( start . Comp . NamePrefix ) : "" ) ,
( "content" , content ) ,
( "suffix" , start . Comp . NameSuffix is not null ? Loc . GetString ( start . Comp . NameSuffix ) : "" ) ) ;
_metaData . SetEntityName ( start , newName ) ;
}
2024-09-08 09:22:27 +03:00
private void MergeFoodSolutions ( EntityUid start , EntityUid element )
2024-08-10 22:31:32 +03:00
{
2024-09-08 09:22:27 +03:00
if ( ! TryComp < FoodComponent > ( start , out var startFood ) )
return ;
if ( ! TryComp < FoodComponent > ( element , out var elementFood ) )
return ;
if ( ! _solutionContainer . TryGetSolution ( start , startFood . Solution , out var startSolutionEntity , out var startSolution ) )
2024-08-10 22:31:32 +03:00
return ;
2024-09-08 09:22:27 +03:00
if ( ! _solutionContainer . TryGetSolution ( element , elementFood . Solution , out _ , out var elementSolution ) )
2024-08-10 22:31:32 +03:00
return ;
startSolution . MaxVolume + = elementSolution . MaxVolume ;
_solutionContainer . TryAddSolution ( startSolutionEntity . Value , elementSolution ) ;
}
2024-09-08 09:22:27 +03:00
private void MergeFlavorProfiles ( EntityUid start , EntityUid element )
2024-08-10 22:31:32 +03:00
{
if ( ! TryComp < FlavorProfileComponent > ( start , out var startProfile ) )
return ;
if ( ! TryComp < FlavorProfileComponent > ( element , out var elementProfile ) )
return ;
foreach ( var flavor in elementProfile . Flavors )
{
if ( startProfile ! = null & & ! startProfile . Flavors . Contains ( flavor ) )
startProfile . Flavors . Add ( flavor ) ;
}
}
2024-09-08 09:22:27 +03:00
private void MergeTrash ( EntityUid start , EntityUid element )
2024-08-10 22:31:32 +03:00
{
if ( ! TryComp < FoodComponent > ( start , out var startFood ) )
return ;
if ( ! TryComp < FoodComponent > ( element , out var elementFood ) )
return ;
foreach ( var trash in elementFood . Trash )
{
startFood . Trash . Add ( trash ) ;
}
}
2024-08-14 15:47:03 -04:00
2024-09-08 09:22:27 +03:00
private void MergeTags ( EntityUid start , EntityUid element )
2024-08-14 15:47:03 -04:00
{
if ( ! TryComp < TagComponent > ( element , out var elementTags ) )
return ;
2024-09-08 09:22:27 +03:00
EnsureComp < TagComponent > ( start ) ;
2024-08-14 15:47:03 -04:00
2024-09-08 09:22:27 +03:00
_tag . TryAddTags ( start , elementTags . Tags ) ;
2024-08-14 15:47:03 -04:00
}
2024-08-10 22:31:32 +03:00
}