// Copyright IHY. Universal Bars plugin. #include "UniversalHUDWidget.h" #include "UniversalBarWidget.h" #include "UniversalBarsSubsystem.h" #include "UniversalBarsTheme.h" #include "Components/Image.h" #include "Components/PanelWidget.h" #include "Components/CanvasPanel.h" #include "Components/CanvasPanelSlot.h" void UUniversalHUDWidget::NativeConstruct() { Super::NativeConstruct(); ApplyTheme(); if (UUniversalBarsSubsystem* Sub = UUniversalBarsSubsystem::Get(this)) { Sub->RegisterHUD(this); } } void UUniversalHUDWidget::ApplyTheme() { if (!Theme) { return; } HungerColor = Theme->Hunger; WeightColor = Theme->Weight; ColdColor = Theme->Cold; auto StyleBar = [this](UUniversalBarWidget* Bar, FLinearColor Fill) { if (!Bar) { return; } Bar->SetBarColors(Fill, Theme->Empty, Theme->Border, Theme->Delayed); Bar->SetVector(TEXT("ShieldColor"), Theme->Shield); Bar->DamageFlashColor = Theme->DamageFlash; Bar->HealFlashColor = Theme->HealFlash; Bar->SetScalar(TEXT("CornerRadius"), Theme->CornerRadius); Bar->SetScalar(TEXT("BorderSize"), Theme->BorderSize); Bar->SetScalar(TEXT("InnerPadding"), Theme->InnerPadding); }; StyleBar(HealthBar, Theme->HealthFill); StyleBar(StaminaBar, Theme->StaminaFill); if (ChipsPanel && Theme->ChipColors.Num() > 0) { const int32 N = ChipsPanel->GetChildrenCount(); for (int32 i = 0; i < N; ++i) { if (UImage* Img = Cast(ChipsPanel->GetChildAt(i))) { Img->SetBrushTintColor(FSlateColor(Theme->ChipColors[i % Theme->ChipColors.Num()])); } } } // Tracking chips: tint by what they represent. if (ChipHeart) { ChipHeart->SetBrushTintColor(FSlateColor(Theme->HealthFill)); } if (ChipHunger) { ChipHunger->SetBrushTintColor(FSlateColor(Theme->Hunger)); } if (ChipWeight) { ChipWeight->SetBrushTintColor(FSlateColor(Theme->Weight)); } if (ChipCold) { ChipCold->SetBrushTintColor(FSlateColor(Theme->Cold)); } } void UUniversalHUDWidget::NativeDestruct() { if (UUniversalBarsSubsystem* Sub = UUniversalBarsSubsystem::Get(this)) { Sub->UnregisterHUD(this); } Super::NativeDestruct(); } void UUniversalHUDWidget::SetHealth(float Percent) { if (HealthBar) { HealthBar->SetFillPercent(Percent, true); } } void UUniversalHUDWidget::SetStamina(float Percent) { if (StaminaBar) { StaminaBar->SetFillPercent(Percent, true); } } void UUniversalHUDWidget::SetShield(float Percent) { if (HealthBar) { HealthBar->SetShieldPercent(Percent); } } void UUniversalHUDWidget::SetHunger(float Amount) { if (HealthBar) { HealthBar->SetPenaltyZone(TEXT("Hunger"), Amount, HungerColor); } } void UUniversalHUDWidget::SetWeight(float Amount) { if (HealthBar) { HealthBar->SetPenaltyZone(TEXT("Weight"), Amount, WeightColor); } } void UUniversalHUDWidget::SetCold(float Amount) { if (HealthBar) { HealthBar->SetPenaltyZone(TEXT("Cold"), Amount, ColdColor); } } void UUniversalHUDWidget::DamageHealth(float Delta) { if (HealthBar) { HealthBar->SetFillPercent(HealthBar->GetFillPercent() - FMath::Abs(Delta), true); } } void UUniversalHUDWidget::HealHealth(float Delta) { if (HealthBar) { HealthBar->SetFillPercent(HealthBar->GetFillPercent() + FMath::Abs(Delta), true); } } float UUniversalHUDWidget::GetHealth() const { return HealthBar ? HealthBar->GetFillPercent() : 0.f; } void UUniversalHUDWidget::SetDemoMode(bool bEnable) { bDemoMode = bEnable; bDemoInit = false; DemoTimer = 0.f; DemoStep = 0; } void UUniversalHUDWidget::DemoStepNow() { // Each step shows a different animation; between steps the bars ease on their own. switch (DemoStep % 10) { case 0: DamageHealth(0.25f); break; // red flash + lag tail case 1: SetStamina(0.30f); break; // stamina drop case 2: HealHealth(0.20f); break; // green flash + smooth rise case 3: SetHunger(0.18f); break; // hunger zone grows in case 4: SetWeight(0.14f); break; // weight zone grows in case 5: SetStamina(0.95f); break; // stamina refill case 6: HealHealth(0.30f); break; case 7: SetHunger(0.0f); break; // hunger zone retracts case 8: SetWeight(0.0f); break; // weight zone retracts case 9: DamageHealth(0.45f); break; // big hit -> low-HP pulse } ++DemoStep; } void UUniversalHUDWidget::PositionChips() { if (!HealthBar) { return; } // Width of the bar in local pixels — ChipCanvas is overlaid on HealthBar so // the same width maps zone-U (0..1) straight to chip X. const float W = HealthBar->GetCachedGeometry().GetLocalSize().X; if (W <= 1.f) { return; // not laid out yet this frame } auto Place = [W](UImage* Chip, float U) { if (!Chip) { return; } if (U < 0.f) { Chip->SetVisibility(ESlateVisibility::Collapsed); // zone absent return; } Chip->SetVisibility(ESlateVisibility::HitTestInvisible); if (UCanvasPanelSlot* CSlot = Cast(Chip->Slot)) { const float ChipW = CSlot->GetSize().X; // centre over the zone, sitting just above the bar (negative Y) CSlot->SetPosition(FVector2D(U * W - ChipW * 0.5f, -ChipW - 6.f)); } }; Place(ChipHeart, HealthBar->GetUsableEnd()); Place(ChipHunger, HealthBar->GetZoneCenter(TEXT("Hunger"))); Place(ChipWeight, HealthBar->GetZoneCenter(TEXT("Weight"))); Place(ChipCold, HealthBar->GetZoneCenter(TEXT("Cold"))); } void UUniversalHUDWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) { Super::NativeTick(MyGeometry, InDeltaTime); PositionChips(); // always track the bar, demo or not if (!bDemoMode) { return; } if (!bDemoInit) { bDemoInit = true; SetHealth(1.f); SetStamina(1.f); SetHunger(0.f); SetWeight(0.f); DemoTimer = 0.f; DemoStep = 0; } DemoTimer += InDeltaTime; if (DemoTimer >= DemoStepInterval) { DemoTimer = 0.f; DemoStepNow(); } }