6
2025-11-07 12:32:48 +03:00

522 lines
16 KiB
C#

using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Player;
using Content.Shared.Damage;
using Content.Shared.Mobs.Systems;
using Content.Client.Message;
using Content.Shared.Mobs;
using Robust.Shared.Timing;
using Robust.Client.UserInterface;
using Robust.Client.Graphics;
using Content.Shared.StatusEffect;
using Robust.Client.UserInterface.Controls;
using Content.Shared.Damage.Components;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared._Horizon.ERTJuggernaut;
using Content.Shared.Chemistry.EntitySystems;
using System.Numerics;
namespace Content.Client._Horizon.ERTJuggernaut;
[GenerateTypedNameReferences]
public sealed partial class JuggernautWindow : DefaultWindow
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private float _updateAccumulator;
private float _rotationAccumulator;
private const float UpdateRate = 0.5f;
private const float RotationRate = 0.5f;
private Direction _currentDirection = Direction.South;
private Control _currentPage;
private Dictionary<string, Dictionary<float, Button>> _reagentButtons = new();
private Dictionary<string, BoxContainer> _buttonContainers = new();
private bool TryGetPlayerEntity(out EntityUid uid)
{
uid = default;
var entity = _playerManager.LocalSession?.AttachedEntity;
if (entity == null)
return false;
uid = entity.Value;
return true;
}
#region Initialization
public JuggernautWindow()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_currentPage = ChemMasterPage;
SetupButtons();
SetupChemMasterEvents();
UpdatePlayerSprite();
}
private void SetupButtons()
{
HealthPageButton.OnPressed += _ => SwitchToPage(HealthPage);
ChemMasterPageButton.OnPressed += _ => SwitchToPage(ChemMasterPage);
InjectButton.OnPressed += _ => SetupChemMasterEvents();
UpdateButtonStates(ChemMasterPage);
}
#endregion
#region Update Loop
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
_updateAccumulator += args.DeltaSeconds;
_rotationAccumulator += args.DeltaSeconds;
if (_updateAccumulator >= UpdateRate)
{
_updateAccumulator = 0;
if (_currentPage == ChemMasterPage)
{
UpdateChemMaster();
}
else if (_currentPage == HealthPage)
{
UpdateHealth();
}
}
if (_rotationAccumulator >= RotationRate)
{
_rotationAccumulator = 0;
RotateSprite();
}
}
private void UpdateChemMaster()
{
if (!TryGetPlayerEntity(out var uid))
return;
UpdateTotalReagents(uid);
UpdateChemMasterDisplay();
}
private void UpdateHealth()
{
if (!TryGetPlayerEntity(out var uid))
return;
UpdateHealthInfo(uid);
UpdateStaminaInfo(uid);
UpdateStatusEffects(uid);
UpdateHunger(uid);
UpdateThirst(uid);
}
#endregion
#region Page Management
private void SwitchToPage(Control newPage)
{
if (_currentPage == newPage)
return;
_currentPage.Visible = false;
newPage.Visible = true;
_currentPage = newPage;
UpdateButtonStates(newPage);
}
private void UpdateButtonStates(Control activePage)
{
HealthPageButton.Pressed = false;
ChemMasterPageButton.Pressed = false;
if (activePage == HealthPage)
{
HealthPageButton.Pressed = true;
}
else if (activePage == ChemMasterPage)
{
ChemMasterPageButton.Pressed = true;
}
}
#endregion
#region ChemMaster UI
private void UpdateChemMasterDisplay()
{
if (!TryGetPlayerEntity(out var uid) || !_entityManager.TryGetComponent<JuggernautComponent>(uid, out var component))
return;
if (ReagentsContainer.ChildCount == 0)
{
CreateChemMasterInterface(component);
}
foreach (var (reagent, available) in component.AvailableReagents)
{
if (_buttonContainers.TryGetValue(reagent, out var container))
{
var mainBox = (BoxContainer)container.Parent!;
var header = (BoxContainer)mainBox.GetChild(0);
var amountLabel = (Label)header.GetChild(1);
amountLabel.Text = available.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) + "/30.0";
var progressBar = (ProgressBar)mainBox.GetChild(1);
progressBar.Value = available;
}
}
}
private void CreateChemMasterInterface(JuggernautComponent component)
{
foreach (var (reagent, available) in component.AvailableReagents)
{
var container = new PanelContainer
{
HorizontalExpand = true,
Margin = new Thickness(0, 0, 0, 10)
};
container.PanelOverride = new StyleBoxFlat
{
BackgroundColor = Color.FromHex("#1a1a1a"),
BorderColor = Color.FromHex("#40526A"),
BorderThickness = new Thickness(1),
};
var mainBox = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
HorizontalExpand = true,
Margin = new Thickness(5)
};
var header = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
HorizontalExpand = true,
Margin = new Thickness(0, 0, 0, 5)
};
var nameLabel = new Label
{
Text = component.ReagentNames.TryGetValue(reagent, out var name) ? name : reagent,
StyleClasses = { "LabelSubText" }
};
var amountLabel = new Label
{
Text = available.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) + "/30.0",
StyleClasses = { "LabelSubText" },
HorizontalAlignment = HAlignment.Left,
HorizontalExpand = true,
Margin = new Thickness(5, 0, 0, 0)
};
header.AddChild(nameLabel);
header.AddChild(amountLabel);
var progressBar = new ProgressBar
{
MinValue = 0,
MaxValue = component.MaxReagentAmount,
Value = available,
HorizontalExpand = true,
MinHeight = 20
};
var descriptionLabel = new Label
{
Text = component.ReagentDescriptions.TryGetValue(reagent, out var description) ? description : "",
StyleClasses = { "LabelSubText" },
HorizontalExpand = true,
Margin = new Thickness(0, 5, 0, 5),
ClipText = true,
Align = Label.AlignMode.Left
};
var buttonsBox = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
HorizontalExpand = true,
Margin = new Thickness(0, 5, 0, 0)
};
_buttonContainers[reagent] = buttonsBox;
_reagentButtons[reagent] = CreateReagentButtons(reagent, component);
foreach (var button in _reagentButtons[reagent].Values)
{
buttonsBox.AddChild(button);
}
mainBox.AddChild(header);
mainBox.AddChild(progressBar);
mainBox.AddChild(descriptionLabel);
mainBox.AddChild(buttonsBox);
container.AddChild(mainBox);
ReagentsContainer.AddChild(container);
}
}
private Dictionary<float, Button> CreateReagentButtons(string reagent, JuggernautComponent component)
{
var buttons = new Dictionary<float, Button>();
var amounts = new[] { 5f, 10f, 15f, 20f, 25f, 30f };
foreach (var amount in amounts)
{
var button = new Button
{
Text = amount.ToString(),
StyleClasses = { "OpenBoth" },
MinWidth = 40,
Margin = new Thickness(2, 0)
};
button.OnPressed += args =>
{
if (button.Pressed)
{
button.Pressed = false;
component.SelectedAmounts.Remove(reagent);
}
else
{
foreach (var btn in _reagentButtons[reagent].Values)
{
btn.Pressed = false;
}
button.Pressed = true;
component.SelectedAmounts[reagent] = amount;
}
};
buttons[amount] = button;
}
return buttons;
}
private void UpdateTotalReagents(EntityUid uid)
{
var solutionSystem = _entityManager.System<SharedSolutionContainerSystem>();
var totalReagents = 0f;
var maxReagents = 250f;
foreach (var reagent in _entityManager.GetComponent<JuggernautComponent>(uid).ReagentNames.Keys)
{
totalReagents += solutionSystem.GetTotalPrototypeQuantity(uid, reagent).Float();
}
TotalReagentsLabel.Text = totalReagents.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) + "/" + maxReagents.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture);
}
private void SetupChemMasterEvents()
{
if (!TryGetPlayerEntity(out var playerUid) || !_entityManager.TryGetComponent<JuggernautComponent>(playerUid, out var comp))
return;
var selectedReagents = new Dictionary<string, float>(comp.SelectedAmounts);
_entityManager.System<JuggernautClientSystem>().SendInjectEvent(playerUid, selectedReagents);
}
#endregion
#region Health UI
public void UpdatePlayerSprite()
{
if (!TryGetPlayerEntity(out var uid))
return;
PlayerSpriteClothed.SetEntity(uid);
PlayerSpriteClothed.Scale = new Vector2(2.5f, 2.5f);
}
private void RotateSprite()
{
_currentDirection = _currentDirection switch
{
Direction.South => Direction.SouthEast,
Direction.SouthEast => Direction.East,
Direction.East => Direction.NorthEast,
Direction.NorthEast => Direction.North,
Direction.North => Direction.NorthWest,
Direction.NorthWest => Direction.West,
Direction.West => Direction.SouthWest,
Direction.SouthWest => Direction.South,
_ => Direction.South
};
PlayerSpriteClothed.OverrideDirection = _currentDirection;
}
private void UpdateHealthStatus(float currentHealth, float maxHealth)
{
var healthPercentage = currentHealth / maxHealth * 100;
var (status, color) = healthPercentage switch
{
> 66 => ("ert-juggernaut-status-normal", "#45B045"),
> 33 => ("ert-juggernaut-status-injured", "#B0B045"),
_ => ("ert-juggernaut-status-critical", "#B04545")
};
StatusValue.SetMarkup(Loc.GetString(status));
HealthBar.ForegroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex(color) };
}
private void UpdateHealthInfo(EntityUid uid)
{
if (!_entityManager.TryGetComponent<DamageableComponent>(uid, out var damageable))
{
HealthBar.Visible = false;
HealthBar.Visible = false;
}
else
{
var totalDamage = damageable.TotalDamage;
var mobThresholdSystem = _entityManager.System<MobThresholdSystem>();
var critThreshold = mobThresholdSystem.GetThresholdForState(uid, MobState.Critical);
var currentHealth = critThreshold - totalDamage;
var maxHealth = critThreshold;
HealthBar.MaxValue = maxHealth.Float();
HealthBar.Value = currentHealth.Float();
HealthInfo.SetMarkup($"[color=white]{Math.Round(currentHealth.Float(), 0)} / {Math.Round(maxHealth.Float(), 0)}[/color]");
UpdateHealthStatus(currentHealth.Float(), maxHealth.Float());
}
}
private void UpdateStaminaInfo(EntityUid uid)
{
if (!_entityManager.TryGetComponent<StaminaComponent>(uid, out var stamina))
{
StaminaBar.Visible = false;
StaminaInfo.Visible = false;
}
else
{
StaminaBar.Visible = true;
StaminaInfo.Visible = true;
var maxStamina = stamina.CritThreshold;
var currentStamina = maxStamina - stamina.StaminaDamage;
StaminaBar.MaxValue = maxStamina;
StaminaBar.Value = currentStamina;
StaminaInfo.SetMarkup($"[color=white]{Math.Round(currentStamina, 0)} / {Math.Round(maxStamina, 0)}[/color]");
var staminaPercentage = currentStamina / maxStamina * 100;
var color = staminaPercentage switch
{
>= 70 => "#4287f5",
>= 40 => "#42b0f5",
>= 20 => "#f5b042",
_ => "#f54242"
};
StaminaBar.ForegroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex(color) };
}
}
private void UpdateHunger(EntityUid uid)
{
if (!_entityManager.TryGetComponent<HungerComponent>(uid, out var hunger))
{
HungerBar.Visible = false;
HungerInfo.Visible = false;
}
else
{
HungerBar.Visible = true;
HungerInfo.Visible = true;
var hungerSystem = _entityManager.System<HungerSystem>();
var hungerValue = hungerSystem.GetHunger(hunger);
var maxHunger = hunger.Thresholds[HungerThreshold.Overfed];
HungerBar.MaxValue = maxHunger;
HungerBar.Value = hungerValue;
HungerInfo.SetMarkup($"[color=white]{Math.Round(hungerValue, 0)} / {Math.Round(maxHunger, 0)}[/color]");
}
}
private void UpdateThirst(EntityUid uid)
{
if (!_entityManager.TryGetComponent<ThirstComponent>(uid, out var thirst))
{
ThirstBar.Visible = false;
ThirstInfo.Visible = false;
}
else
{
ThirstBar.Visible = true;
ThirstInfo.Visible = true;
var thirstValue = thirst.CurrentThirst;
var maxThirst = thirst.ThirstThresholds[ThirstThreshold.OverHydrated];
ThirstBar.MaxValue = maxThirst;
ThirstBar.Value = thirstValue;
ThirstInfo.SetMarkup($"[color=white]{Math.Round(thirstValue, 0)} / {Math.Round(maxThirst, 0)}[/color]");
}
}
private void UpdateStatusEffects(EntityUid uid)
{
StatusEffectsList.RemoveAllChildren();
if (!_entityManager.TryGetComponent<StatusEffectsComponent>(uid, out var status))
{
StatusEffectsContainer.Visible = false;
return;
}
StatusEffectsContainer.Visible = true;
AddStatusEffectsToList(status);
}
private void AddStatusEffectsToList(StatusEffectsComponent status)
{
if (status.ActiveEffects.Count == 0)
{
AddStatusEffectLabel("Нет активных эффектов");
return;
}
foreach (var effect in status.ActiveEffects)
{
AddStatusEffectLabel($"- {effect.Key}");
}
}
private void AddStatusEffectLabel(string text)
{
var label = new Label
{
Text = text,
StyleClasses = { "LabelSubText" }
};
StatusEffectsList.AddChild(label);
}
#endregion
}