Import initial du projet Survival (UE 5.8)

Boucle de jeu complète : récolte, inventaire, consommation, stats de survie,
mort et respawn. Gameplay en C++, Blueprints réservés au câblage d'assets.

Assets binaires (.uasset, .umap, textures, audio) suivis via Git LFS.
Binaries/, Intermediate/, Saved/ et DerivedDataCache/ sont ignorés :
régénérés au build, ils pèsent 3 Go pour 1,3 Go de contenu utile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-28 11:29:07 +02:00
commit ed7c7fd991
2626 changed files with 14833 additions and 0 deletions
@@ -0,0 +1,212 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventoryWidget.h"
#include "Components/UniformGridPanel.h"
#include "Components/UniformGridSlot.h"
#include "FpsPlayer.h"
#include "GameFramework/PlayerController.h"
#include "InventoryComponent.h"
#include "InventoryDragDropOperation.h"
#include "InventorySlotWidget.h"
void UInventoryWidget::NativeConstruct()
{
Super::NativeConstruct();
BindToOwningPawn();
}
void UInventoryWidget::BindToOwningPawn()
{
UInventoryComponent* NewInventory = ResolveInventory();
if (BoundInventory.Get() == NewInventory)
{
return;
}
if (BoundInventory.IsValid())
{
BoundInventory->OnInventoryChanged.RemoveDynamic(this, &UInventoryWidget::HandleInventoryChanged);
}
BoundInventory = NewInventory;
if (BoundInventory.IsValid())
{
BoundInventory->OnInventoryChanged.AddDynamic(this, &UInventoryWidget::HandleInventoryChanged);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("UInventoryWidget : aucun UInventoryComponent trouve sur le pawn possede."));
}
// Les cases gardent l'ancien composant en memoire : on force leur
// recreation en vidant la grille.
for (UInventorySlotWidget* SlotWidget : SlotWidgets)
{
if (SlotWidget)
{
SlotWidget->RemoveFromParent();
}
}
SlotWidgets.Reset();
Refresh();
}
void UInventoryWidget::NativeDestruct()
{
if (BoundInventory.IsValid())
{
BoundInventory->OnInventoryChanged.RemoveDynamic(this, &UInventoryWidget::HandleInventoryChanged);
}
BoundInventory.Reset();
Super::NativeDestruct();
}
UInventoryComponent* UInventoryWidget::ResolveInventory() const
{
const APlayerController* OwningController = GetOwningPlayer();
AFpsPlayer* Player = OwningController ? Cast<AFpsPlayer>(OwningController->GetPawn()) : nullptr;
return Player ? Player->GetInventoryComponent() : nullptr;
}
void UInventoryWidget::HandleInventoryChanged()
{
Refresh();
}
void UInventoryWidget::NotifySlotHovered(int32 Index, bool bHovered)
{
if (bHovered)
{
HoveredSlotIndex = Index;
}
else if (HoveredSlotIndex == Index)
{
// Le test d'egalite evite d'effacer le survol quand les evenements
// arrivent dans l'ordre "entre dans B" puis "sort de A".
HoveredSlotIndex = INDEX_NONE;
}
}
bool UInventoryWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
// On n'arrive ici que si AUCUNE case n'a traite le depot : les widgets
// enfants sont consultes en premier et arretent la propagation.
const UInventoryDragDropOperation* Payload = Cast<UInventoryDragDropOperation>(InOperation);
if (!Payload || Payload->SourceSlotIndex == INDEX_NONE)
{
return true;
}
// Lache DANS la grille mais entre deux cases (l'espacement) : on absorbe.
// Jeter un objet au sol parce que le joueur a rate une case de trois
// pixels serait impardonnable dans un jeu de survie.
if (SlotGrid && SlotGrid->GetCachedGeometry().IsUnderLocation(InDragDropEvent.GetScreenSpacePosition()))
{
return true;
}
// Lache hors de la grille : on jette au sol, comme dans Valheim ou Raft.
if (const APlayerController* OwningController = GetOwningPlayer())
{
if (AFpsPlayer* Player = Cast<AFpsPlayer>(OwningController->GetPawn()))
{
Player->DropSlot(Payload->SourceSlotIndex, Payload->Quantity);
}
}
return true;
}
void UInventoryWidget::Refresh()
{
if (!SlotGrid)
{
UE_LOG(LogTemp, Warning, TEXT("UInventoryWidget::Refresh : SlotGrid est nul. Le widget nomme 'SlotGrid' manque dans le Blueprint."));
return;
}
if (!BoundInventory.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("UInventoryWidget::Refresh : aucun inventaire lie."));
return;
}
const TArray<FInventorySlot>& Slots = BoundInventory->GetSlots();
// La grille commence APRES la barre rapide : les deux conteneurs sont
// independants, un objet dans la barre n'apparait pas ici.
const int32 Offset = BoundInventory->GetBackpackStartIndex();
const int32 GridCount = FMath::Max(0, Slots.Num() - Offset);
// Le nombre de slots peut changer en cours de partie (sac equipe).
// On ne reconstruit que si la taille a bouge : recreer toutes les cases
// a chaque ramassage serait du gaspillage pur.
RebuildGrid(GridCount);
for (int32 Index = 0; Index < SlotWidgets.Num(); ++Index)
{
const int32 Absolute = Offset + Index;
if (SlotWidgets[Index] && Slots.IsValidIndex(Absolute))
{
SlotWidgets[Index]->SetSlot(Slots[Absolute]);
}
}
}
void UInventoryWidget::RebuildGrid(int32 DesiredCount)
{
if (SlotWidgets.Num() == DesiredCount)
{
return;
}
if (!SlotWidgetClass)
{
UE_LOG(LogTemp, Warning, TEXT("UInventoryWidget : SlotWidgetClass n'est pas assignee, la grille restera vide."));
return;
}
// Trop de cases : on retire le surplus par la fin.
while (SlotWidgets.Num() > DesiredCount)
{
const int32 Last = SlotWidgets.Num() - 1;
if (SlotWidgets[Last])
{
SlotWidgets[Last]->RemoveFromParent();
}
SlotWidgets.RemoveAt(Last);
}
// Pas assez : on en cree, en les placant en ligne / colonne.
const int32 Columns = FMath::Max(1, ColumnCount);
while (SlotWidgets.Num() < DesiredCount)
{
UInventorySlotWidget* NewSlot = CreateWidget<UInventorySlotWidget>(this, SlotWidgetClass);
if (!NewSlot)
{
// Arrive typiquement quand le Widget Blueprint de case ne compile
// plus, apres un changement de BindWidget cote C++ par exemple.
UE_LOG(LogTemp, Error, TEXT("UInventoryWidget : impossible de creer une case a partir de %s. Recompile ce Widget Blueprint."),
*GetNameSafe(SlotWidgetClass));
break;
}
const int32 Index = SlotWidgets.Num();
// On donne l'index ABSOLU dans le tableau, pas la position dans la
// grille. C'est ce qui rend le glisser entre la barre et la grille
// gratuit : les deux manipulent les memes index.
const int32 Absolute = BoundInventory->GetBackpackStartIndex() + Index;
NewSlot->InitSlot(BoundInventory.Get(), Absolute, this);
SlotGrid->AddChildToUniformGrid(NewSlot, Index / Columns, Index % Columns);
SlotWidgets.Add(NewSlot);
}
}