Skip to content

Commit

Permalink
Smart fridge (#555)
Browse files Browse the repository at this point in the history
* Переписываю с 0

* 3 попытки совмещения ужа и ежа, на этот раз полностью по предложению Артура

* Первые потуги третьей попытки совместить ужа Storage и Dtictionary Vending

* Адекватное отображение в новом интерфейсе

* Добработки работы интерфейса с Storage (черновой вариант)

* Function of getting items adjusted

* Pre-PR state

* done

* Поправил файл миграции

* Откат

* Поправлено id, добавлена локализация, добавлена ent вайл миграции

* Поправлены тэги (очень странно работает со Storage, так как предмет проверяется и по тэгу, и по компонентам, в результате предмет может вставиться, написав ошибку вайтлиста)

* Перенес функции отображения инвентаря в Shared

* Нашел, что потерял анимацию консольки, пытаюсь вернуть + заделки на сломанность и возможность отказа.

* Получилось починить анимацию, но картинка слабо отличается от не включенного.

* Прикрутил спрайты заполнения.

* Поправлена моделька

* Допили всё до финала + оставил заделку для допиливания до уровня оффов

* Убрал параметр maxSlots

* Правки под новый grid storage
  • Loading branch information
SkaldetSkaeg authored Dec 10, 2023
1 parent e25756f commit 598e8f6
Show file tree
Hide file tree
Showing 10 changed files with 768 additions and 0 deletions.
84 changes: 84 additions & 0 deletions Content.Client/SS220/SmartFridge/SmartFridgeBoundUserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt

using Content.Client.SS220.SmartFridge.UI;
using Content.Shared.SS220.SmartFridge;
using Content.Shared.VendingMachines;
using Robust.Client.UserInterface.Controls;
using System.Linq;

namespace Content.Client.SS220.SmartFridge
{
public sealed class SmartFridgeBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private SmartFridgeMenu? _menu;

[ViewVariables]
private List<VendingMachineInventoryEntry> _cachedInventory = new();

[ViewVariables]
private List<int> _cachedFilteredIndex = new();

public SmartFridgeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}

protected override void Open()
{
base.Open();

_menu = new SmartFridgeMenu { Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName };

_menu.OnClose += Close;
_menu.OnItemSelected += OnItemSelected;
_menu.OnSearchChanged += OnSearchChanged;

UpdateUI();

_menu.OpenCentered();
}

private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
{

if (_cachedInventory.Count == 0)
return;

var selectedItem = _cachedInventory.ElementAtOrDefault(_cachedFilteredIndex.ElementAtOrDefault(args.ItemIndex));

if (selectedItem == null)
return;

SendPredictedMessage(new SmartFridgeInteractWithItemEvent(selectedItem.EntityUids[0]));

UpdateUI();
}

public void UpdateUI()
{
var smartFridgeSys = EntMan.System<SharedSmartFridgeSystem>();
_cachedInventory = smartFridgeSys.GetAllInventory(Owner);
_menu?.Populate(_cachedInventory, out _cachedFilteredIndex);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;

if (_menu == null)
return;

_menu.OnItemSelected -= OnItemSelected;
_menu.OnClose -= Close;
_menu.Dispose();
}

private void OnSearchChanged(string? filter)
{
_menu?.Populate(_cachedInventory, out _cachedFilteredIndex, filter);
}
}

}
136 changes: 136 additions & 0 deletions Content.Client/SS220/SmartFridge/SmartFridgeSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt

using Content.Shared.SS220.SmartFridge;
using Robust.Client.Animations;
using Robust.Client.GameObjects;

namespace Content.Client.SS220.SmartFridge;

public sealed class SmartFridgeSystem : SharedSmartFridgeSystem
{
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<SmartFridgeComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<SmartFridgeComponent, AnimationCompletedEvent>(OnAnimationCompleted);
}

private void OnAnimationCompleted(EntityUid uid, SmartFridgeComponent component, AnimationCompletedEvent args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;

if (!TryComp<AppearanceComponent>(uid, out var appearance) ||
!_appearanceSystem.TryGetData<SmartFridgeVisualState>(uid, SmartFridgeVisuals.VisualState, out var visualState, appearance))
{
visualState = SmartFridgeVisualState.Normal;
}

UpdateAppearance(uid, visualState, component, sprite);
}

private void OnAppearanceChange(EntityUid uid, SmartFridgeComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;

if (!args.AppearanceData.TryGetValue(SmartFridgeVisuals.VisualState, out var visualStateObject) ||
visualStateObject is not SmartFridgeVisualState visualState)
{
visualState = SmartFridgeVisualState.Normal;
}

UpdateAppearance(uid, visualState, component, args.Sprite);
}

private void UpdateAppearance(EntityUid uid, SmartFridgeVisualState visualState, SmartFridgeComponent component, SpriteComponent sprite)
{
SetLayerState(SmartFridgeVisualLayers.Base, component.OffState, sprite);

switch (visualState)
{
case SmartFridgeVisualState.Normal:
SetLayerState(SmartFridgeVisualLayers.BaseUnshaded, component.NormalState, sprite);
SetLayerState(SmartFridgeVisualLayers.Screen, component.ScreenState, sprite);
break;

case SmartFridgeVisualState.Deny:
if (component.LoopDenyAnimation)
SetLayerState(SmartFridgeVisualLayers.BaseUnshaded, component.DenyState, sprite);
else
PlayAnimation(uid, SmartFridgeVisualLayers.BaseUnshaded, component.DenyState, component.DenyDelay, sprite);

SetLayerState(SmartFridgeVisualLayers.Screen, component.ScreenState, sprite);
break;

case SmartFridgeVisualState.Broken:
HideLayers(sprite);
SetLayerState(SmartFridgeVisualLayers.Base, component.BrokenState, sprite);
break;

case SmartFridgeVisualState.Off:
HideLayers(sprite);
break;
}
}

private static void SetLayerState(SmartFridgeVisualLayers layer, string? state, SpriteComponent sprite)
{
if (string.IsNullOrEmpty(state))
return;

sprite.LayerSetVisible(layer, true);
sprite.LayerSetAutoAnimated(layer, true);
sprite.LayerSetState(layer, state);
}

private void PlayAnimation(EntityUid uid, SmartFridgeVisualLayers layer, string? state, float animationTime, SpriteComponent sprite)
{
if (string.IsNullOrEmpty(state))
return;

if (!_animationPlayer.HasRunningAnimation(uid, state))
{
var animation = GetAnimation(layer, state, animationTime);
sprite.LayerSetVisible(layer, true);
_animationPlayer.Play(uid, animation, state);
}
}

private static Animation GetAnimation(SmartFridgeVisualLayers layer, string state, float animationTime)
{
return new Animation
{
Length = TimeSpan.FromSeconds(animationTime),
AnimationTracks =
{
new AnimationTrackSpriteFlick
{
LayerKey = layer,
KeyFrames =
{
new AnimationTrackSpriteFlick.KeyFrame(state, 0f)
}
}
}
};
}

private static void HideLayers(SpriteComponent sprite)
{
HideLayer(SmartFridgeVisualLayers.BaseUnshaded, sprite);
HideLayer(SmartFridgeVisualLayers.Screen, sprite);
}

private static void HideLayer(SmartFridgeVisualLayers layer, SpriteComponent sprite)
{
if (!sprite.LayerMapTryGet(layer, out var actualLayer))
return;

sprite.LayerSetVisible(actualLayer, false);
}
}
11 changes: 11 additions & 0 deletions Content.Client/SS220/SmartFridge/UI/SmartFridgeMenu.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt -->

<DefaultWindow xmlns="https://spacestation14.io">
<BoxContainer Orientation="Vertical">
<LineEdit Name="SearchBar" PlaceHolder="{Loc 'vending-machine-component-search-filter'}" HorizontalExpand="True" Margin ="0 4" Access="Public"/>
<ItemList Name="SmartFridgeContents"
SizeFlagsStretchRatio="8"
VerticalExpand="True">
</ItemList>
</BoxContainer>
</DefaultWindow>
112 changes: 112 additions & 0 deletions Content.Client/SS220/SmartFridge/UI/SmartFridgeMenu.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt

using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Content.Shared.VendingMachines;

namespace Content.Client.SS220.SmartFridge.UI
{
[GenerateTypedNameReferences]
public sealed partial class SmartFridgeMenu : DefaultWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;

public event Action<ItemList.ItemListSelectedEventArgs>? OnItemSelected;
public event Action<string>? OnSearchChanged;

public SmartFridgeMenu()
{
MinSize = new Vector2(250, 150); // Corvax-Resize
SetSize = new Vector2(450, 150); // Corvax-Resize
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);

SearchBar.OnTextChanged += _ =>
{
OnSearchChanged?.Invoke(SearchBar.Text);
};

SmartFridgeContents.OnItemSelected += args =>
{
OnItemSelected?.Invoke(args);
};
}

/// <summary>
/// Populates the list of available items on the vending machine interface
/// and sets icons based on their prototypes
/// </summary>
public void Populate(List<VendingMachineInventoryEntry> inventory, out List<int> filteredInventory, string? filter = null)
{

filteredInventory = new();

if (inventory.Count == 0)
{
SmartFridgeContents.Clear();
var outOfStockText = Loc.GetString("vending-machine-component-try-eject-out-of-stock");
SmartFridgeContents.AddItem(outOfStockText);
SetSizeAfterUpdate(outOfStockText.Length, SmartFridgeContents.Count);
return;
}

while (inventory.Count != SmartFridgeContents.Count)
{
if (inventory.Count > SmartFridgeContents.Count)
SmartFridgeContents.AddItem(string.Empty);
else
SmartFridgeContents.RemoveAt(SmartFridgeContents.Count - 1);
}

var longestEntry = string.Empty;
var spriteSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>();

var filterCount = 0;
for (var i = 0; i < inventory.Count; i++)
{
var entry = inventory[i];
var smartFridgeItem = SmartFridgeContents[i - filterCount];
smartFridgeItem.Text = string.Empty;
smartFridgeItem.Icon = null;

var itemName = entry.ID;
Texture? icon = null;
if (_prototypeManager.TryIndex<EntityPrototype>(entry.ID, out var prototype))
{
itemName = prototype.Name;
icon = spriteSystem.GetPrototypeIcon(prototype).Default;
}

// search filter
if (!string.IsNullOrEmpty(filter) &&
!itemName.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant()))
{
SmartFridgeContents.Remove(smartFridgeItem);
filterCount++;
continue;
}

if (itemName.Length > longestEntry.Length)
longestEntry = itemName;

smartFridgeItem.Text = $"{itemName} [{entry.Amount}]";
smartFridgeItem.Icon = icon;
filteredInventory.Add(i);
}

SetSizeAfterUpdate(longestEntry.Length, inventory.Count);
}

private void SetSizeAfterUpdate(int longestEntryLength, int contentCount)
{
SetSize = new Vector2(Math.Clamp((longestEntryLength + 2) * 12, 250, 300),
Math.Clamp(contentCount * 50, 150, 350));
}
}
}
Loading

0 comments on commit 598e8f6

Please sign in to comment.