522 lines
16 KiB
C#
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
|
|
} |