2024-01-28 20:38:06 -05:00
using Content.Client.Items.Systems ;
2023-06-30 18:57:28 -04:00
using Content.Shared.Chemistry ;
2023-12-26 18:32:25 -05:00
using Content.Shared.Chemistry.Components ;
2023-02-18 19:00:31 -06:00
using Content.Shared.Chemistry.Reagent ;
2024-08-02 03:18:59 -04:00
using Content.Shared.Clothing ;
using Content.Shared.Clothing.Components ;
2024-01-28 20:38:06 -05:00
using Content.Shared.Hands ;
2024-06-03 01:12:13 -04:00
using Content.Shared.Item ;
2023-03-22 23:49:42 -04:00
using Content.Shared.Rounding ;
2022-08-07 01:50:52 -07:00
using Robust.Client.GameObjects ;
2023-02-18 19:00:31 -06:00
using Robust.Shared.Prototypes ;
2022-08-07 01:50:52 -07:00
namespace Content.Client.Chemistry.Visualizers ;
public sealed class SolutionContainerVisualsSystem : VisualizerSystem < SolutionContainerVisualsComponent >
{
2023-02-18 19:00:31 -06:00
[Dependency] private readonly IPrototypeManager _prototype = default ! ;
2024-01-28 20:38:06 -05:00
[Dependency] private readonly ItemSystem _itemSystem = default ! ;
2025-05-12 16:25:28 -04:00
[Dependency] private readonly SpriteSystem _sprite = default ! ;
2023-02-18 19:00:31 -06:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < SolutionContainerVisualsComponent , MapInitEvent > ( OnMapInit ) ;
2024-01-28 20:38:06 -05:00
SubscribeLocalEvent < SolutionContainerVisualsComponent , GetInhandVisualsEvent > ( OnGetHeldVisuals ) ;
2024-08-02 03:18:59 -04:00
SubscribeLocalEvent < SolutionContainerVisualsComponent , GetEquipmentVisualsEvent > ( OnGetClothingVisuals ) ;
2023-02-18 19:00:31 -06:00
}
private void OnMapInit ( EntityUid uid , SolutionContainerVisualsComponent component , MapInitEvent args )
{
var meta = MetaData ( uid ) ;
component . InitialDescription = meta . EntityDescription ;
}
2022-08-07 01:50:52 -07:00
protected override void OnAppearanceChange ( EntityUid uid , SolutionContainerVisualsComponent component , ref AppearanceChangeEvent args )
{
2023-07-01 00:10:47 +02:00
// Check if the solution that was updated is the one set as represented
if ( ! string . IsNullOrEmpty ( component . SolutionName ) )
{
if ( AppearanceSystem . TryGetData < string > ( uid , SolutionContainerVisuals . SolutionName , out var name ,
args . Component ) & & name ! = component . SolutionName )
{
return ;
}
}
2023-02-02 17:34:53 +01:00
if ( ! AppearanceSystem . TryGetData < float > ( uid , SolutionContainerVisuals . FillFraction , out var fraction , args . Component ) )
2022-08-07 01:50:52 -07:00
return ;
if ( args . Sprite = = null )
return ;
2025-05-12 16:25:28 -04:00
if ( ! _sprite . LayerMapTryGet ( ( uid , args . Sprite ) , component . Layer , out var fillLayer , false ) )
2022-08-07 01:50:52 -07:00
return ;
2024-02-19 16:29:42 -05:00
var maxFillLevels = component . MaxFillLevels ;
var fillBaseName = component . FillBaseName ;
var changeColor = component . ChangeColor ;
var fillSprite = component . MetamorphicDefaultSprite ;
2023-01-23 19:33:11 -05:00
// Currently some solution methods such as overflowing will try to update appearance with a
// volume greater than the max volume. We'll clamp it so players don't see
// a giant error sign and error for debug.
if ( fraction > 1f )
{
2024-02-19 16:29:42 -05:00
Log . Error ( "Attempted to set solution container visuals volume ratio on " + ToPrettyString ( uid ) + " to a value greater than 1. Volume should never be greater than max volume!" ) ;
2023-01-23 19:33:11 -05:00
fraction = 1f ;
}
2023-02-18 19:00:31 -06:00
if ( component . Metamorphic )
{
2025-05-12 16:25:28 -04:00
if ( _sprite . LayerMapTryGet ( ( uid , args . Sprite ) , component . BaseLayer , out var baseLayer , false ) )
2023-02-18 19:00:31 -06:00
{
2025-05-12 16:25:28 -04:00
var hasOverlay = _sprite . LayerMapTryGet ( ( uid , args . Sprite ) , component . OverlayLayer , out var overlayLayer , false ) ;
2023-02-18 19:00:31 -06:00
if ( AppearanceSystem . TryGetData < string > ( uid , SolutionContainerVisuals . BaseOverride ,
out var baseOverride ,
args . Component ) )
{
_prototype . TryIndex < ReagentPrototype > ( baseOverride , out var reagentProto ) ;
if ( reagentProto ? . MetamorphicSprite is { } sprite )
{
2025-05-12 16:25:28 -04:00
_sprite . LayerSetSprite ( ( uid , args . Sprite ) , baseLayer , sprite ) ;
2024-02-19 16:29:42 -05:00
if ( reagentProto . MetamorphicMaxFillLevels > 0 )
{
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , fillLayer , true ) ;
2024-02-19 16:29:42 -05:00
maxFillLevels = reagentProto . MetamorphicMaxFillLevels ;
fillBaseName = reagentProto . MetamorphicFillBaseName ;
changeColor = reagentProto . MetamorphicChangeColor ;
fillSprite = sprite ;
}
else
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , fillLayer , false ) ;
2024-02-19 16:29:42 -05:00
2023-02-18 19:00:31 -06:00
if ( hasOverlay )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , overlayLayer , false ) ;
2023-02-18 19:00:31 -06:00
}
else
{
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , fillLayer , true ) ;
2023-02-18 19:00:31 -06:00
if ( hasOverlay )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , overlayLayer , true ) ;
2023-08-07 15:44:53 +12:00
if ( component . MetamorphicDefaultSprite ! = null )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetSprite ( ( uid , args . Sprite ) , baseLayer , component . MetamorphicDefaultSprite ) ;
2023-02-18 19:00:31 -06:00
}
}
}
}
2024-02-19 16:29:42 -05:00
else
{
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , fillLayer , true ) ;
2024-02-19 16:29:42 -05:00
}
2023-02-18 19:00:31 -06:00
2024-02-19 16:29:42 -05:00
var closestFillSprite = ContentHelpers . RoundToLevels ( fraction , 1 , maxFillLevels + 1 ) ;
2022-08-07 01:50:52 -07:00
if ( closestFillSprite > 0 )
{
2024-02-19 16:29:42 -05:00
if ( fillBaseName = = null )
2022-08-07 01:50:52 -07:00
return ;
2024-02-19 16:29:42 -05:00
var stateName = fillBaseName + closestFillSprite ;
if ( fillSprite ! = null )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetSprite ( ( uid , args . Sprite ) , fillLayer , fillSprite ) ;
_sprite . LayerSetRsiState ( ( uid , args . Sprite ) , fillLayer , stateName ) ;
2022-08-07 01:50:52 -07:00
2024-02-19 16:29:42 -05:00
if ( changeColor & & AppearanceSystem . TryGetData < Color > ( uid , SolutionContainerVisuals . Color , out var color , args . Component ) )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetColor ( ( uid , args . Sprite ) , fillLayer , color ) ;
2024-02-19 16:29:42 -05:00
else
2025-05-12 16:25:28 -04:00
_sprite . LayerSetColor ( ( uid , args . Sprite ) , fillLayer , Color . White ) ;
2022-08-07 01:50:52 -07:00
}
else
{
if ( component . EmptySpriteName = = null )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetVisible ( ( uid , args . Sprite ) , fillLayer , false ) ;
2022-08-07 01:50:52 -07:00
else
{
2025-05-12 16:25:28 -04:00
_sprite . LayerSetRsiState ( ( uid , args . Sprite ) , fillLayer , component . EmptySpriteName ) ;
2024-02-19 16:29:42 -05:00
if ( changeColor )
2025-05-12 16:25:28 -04:00
_sprite . LayerSetColor ( ( uid , args . Sprite ) , fillLayer , component . EmptySpriteColor ) ;
2024-02-19 16:29:42 -05:00
else
2025-05-12 16:25:28 -04:00
_sprite . LayerSetColor ( ( uid , args . Sprite ) , fillLayer , Color . White ) ;
2022-08-07 01:50:52 -07:00
}
}
2023-02-18 19:00:31 -06:00
2024-01-28 20:38:06 -05:00
// in-hand visuals
_itemSystem . VisualsChanged ( uid ) ;
}
private void OnGetHeldVisuals ( EntityUid uid , SolutionContainerVisualsComponent component , GetInhandVisualsEvent args )
{
if ( component . InHandsFillBaseName = = null )
return ;
if ( ! TryComp ( uid , out AppearanceComponent ? appearance ) )
return ;
2024-06-03 01:12:13 -04:00
if ( ! TryComp < ItemComponent > ( uid , out var item ) )
return ;
2024-01-28 20:38:06 -05:00
if ( ! AppearanceSystem . TryGetData < float > ( uid , SolutionContainerVisuals . FillFraction , out var fraction , appearance ) )
return ;
2023-02-18 19:00:31 -06:00
2024-02-19 16:29:42 -05:00
var closestFillSprite = ContentHelpers . RoundToLevels ( fraction , 1 , component . InHandsMaxFillLevels + 1 ) ;
2024-01-28 20:38:06 -05:00
if ( closestFillSprite > 0 )
{
var layer = new PrototypeLayerData ( ) ;
2024-06-03 01:12:13 -04:00
var heldPrefix = item . HeldPrefix = = null ? "inhand-" : $"{item.HeldPrefix}-inhand-" ;
var key = heldPrefix + args . Location . ToString ( ) . ToLowerInvariant ( ) + component . InHandsFillBaseName + closestFillSprite ;
2024-01-28 20:38:06 -05:00
layer . State = key ;
if ( component . ChangeColor & & AppearanceSystem . TryGetData < Color > ( uid , SolutionContainerVisuals . Color , out var color , appearance ) )
layer . Color = color ;
args . Layers . Add ( ( key , layer ) ) ;
}
2022-08-07 01:50:52 -07:00
}
2024-08-02 03:18:59 -04:00
private void OnGetClothingVisuals ( Entity < SolutionContainerVisualsComponent > ent , ref GetEquipmentVisualsEvent args )
{
if ( ent . Comp . EquippedFillBaseName = = null )
return ;
if ( ! TryComp < AppearanceComponent > ( ent , out var appearance ) )
return ;
if ( ! TryComp < ClothingComponent > ( ent , out var clothing ) )
return ;
if ( ! AppearanceSystem . TryGetData < float > ( ent , SolutionContainerVisuals . FillFraction , out var fraction , appearance ) )
return ;
var closestFillSprite = ContentHelpers . RoundToLevels ( fraction , 1 , ent . Comp . EquippedMaxFillLevels + 1 ) ;
if ( closestFillSprite > 0 )
{
var layer = new PrototypeLayerData ( ) ;
var equippedPrefix = clothing . EquippedPrefix = = null ? $"equipped-{args.Slot}" : $" {clothing.EquippedPrefix}-equipped-{args.Slot}" ;
var key = equippedPrefix + ent . Comp . EquippedFillBaseName + closestFillSprite ;
// Make sure the sprite state is valid so we don't show a big red error message
// This saves us from having to make fill level sprites for every possible slot the item could be in (including pockets).
if ( ! TryComp < SpriteComponent > ( ent , out var sprite ) | | sprite . BaseRSI = = null | | ! sprite . BaseRSI . TryGetState ( key , out _ ) )
return ;
layer . State = key ;
if ( ent . Comp . ChangeColor & & AppearanceSystem . TryGetData < Color > ( ent , SolutionContainerVisuals . Color , out var color , appearance ) )
layer . Color = color ;
args . Layers . Add ( ( key , layer ) ) ;
}
}
2022-08-07 01:50:52 -07:00
}