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> _reagentButtons = new(); private Dictionary _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(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 CreateReagentButtons(string reagent, JuggernautComponent component) { var buttons = new Dictionary(); 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(); var totalReagents = 0f; var maxReagents = 250f; foreach (var reagent in _entityManager.GetComponent(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(playerUid, out var comp)) return; var selectedReagents = new Dictionary(comp.SelectedAmounts); _entityManager.System().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(uid, out var damageable)) { HealthBar.Visible = false; HealthBar.Visible = false; } else { var totalDamage = damageable.TotalDamage; var mobThresholdSystem = _entityManager.System(); 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(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(uid, out var hunger)) { HungerBar.Visible = false; HungerInfo.Visible = false; } else { HungerBar.Visible = true; HungerInfo.Visible = true; var hungerSystem = _entityManager.System(); 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(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(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 }