41d6e6a7f3
Coffres : AStorageContainer, un UInventoryComponent configure en conteneur, affiche comme panneau de WBP_InventoryScreen plutot que dans un ecran a lui. Deplacement unifie par UInventoryComponent::TransferSlot / TransferAllTo. Panneau de detail : UItemDetailsWidget, pose DANS WBP_Inventory a droite de la grille. Icone, nom, separation, description, barre d'usure. Il recoit un couple (inventaire, index) et s'abonne a OnInventoryChanged, donc une charge consommee sous le curseur se voit. Une case ne connait plus sa grille : elle diffuse OnHoverChanged. La barre rapide s'y abonne aussi et relaie par le PlayerController, seul a posseder a la fois le HUD et l'ecran. Le panneau efface la case qu'on QUITTE et non lui-meme, sinon passer de la barre a la grille viderait ce qui vient d'etre affiche. L'ecran allume et eteint le panneau (SetItemDetailsEnabled) : WBP_Inventory etant instancie deux fois en mode coffre, une case a cocher par instance serait un bug qui attend qu'on oublie de la decocher. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
240 lines
8.9 KiB
C++
240 lines
8.9 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "Interactable.h"
|
|
#include "StorageContainer.generated.h"
|
|
|
|
class UBoxComponent;
|
|
class UInventoryComponent;
|
|
class UItemDataAsset;
|
|
class USoundBase;
|
|
class UStaticMeshComponent;
|
|
|
|
/**
|
|
* Contenu initial d'un coffre pose dans le niveau.
|
|
*
|
|
* De la donnee de level design, pas du gameplay : un coffre de depart, une
|
|
* cache a trouver. Verse une seule fois au BeginPlay, jamais relu ensuite.
|
|
*/
|
|
USTRUCT(BlueprintType)
|
|
struct FStorageStartingItem
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TObjectPtr<UItemDataAsset> Item = nullptr;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage", meta = (ClampMin = "1"))
|
|
int32 Quantity = 1;
|
|
};
|
|
|
|
/**
|
|
* Coffre pose dans le monde.
|
|
*
|
|
* Il ne contient AUCUN modele de donnees a lui : son contenu est un
|
|
* UInventoryComponent, exactement le meme que celui du joueur, simplement
|
|
* configure sans barre rapide. C'est ce qui rend le glisser-deposer entre le
|
|
* sac et le coffre gratuit -- ce sont les memes cases, le meme FInventorySlot,
|
|
* et donc la durabilite d'une hache traverse le coffre sans une ligne de code.
|
|
*
|
|
* Toute la collision tient dans UNE boite, et les deux meshes n'en portent
|
|
* aucune. C'est la difference avec ACraftingStation, qui se contente de son
|
|
* mesh : un coffre est en deux morceaux dont l'un PIVOTE. Se reposer sur leurs
|
|
* collisions imposerait que le pack en fournisse deux correctes, et laisserait
|
|
* surtout un trou dans la surface visable des que le couvercle se leve --
|
|
* viser le haut du coffre ouvert ne toucherait plus rien.
|
|
*
|
|
* La boite est fille de la caisse et ne bouge donc jamais : le coffre se vise
|
|
* exactement pareil ouvert ou ferme. Elle bloque aussi le joueur physiquement,
|
|
* ce qui evite d'avoir a se demander si le mesh du pack sait le faire.
|
|
*
|
|
* Aucun Tick au repos : il ne s'active que le temps d'animer le couvercle, et
|
|
* se rendort des que l'angle est atteint.
|
|
*/
|
|
UCLASS()
|
|
class SURVIVAL_PROJET_API AStorageContainer : public AActor, public IInteractable
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
AStorageContainer();
|
|
|
|
virtual void Tick(float DeltaTime) override;
|
|
|
|
// --- IInteractable ---
|
|
virtual FText GetInteractionPrompt_Implementation(AActor* Interactor) const override;
|
|
virtual void Interact_Implementation(AActor* Interactor) override;
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Storage")
|
|
UInventoryComponent* GetStorage() const { return Storage; }
|
|
|
|
/** Nom affiche en titre de l'ecran de coffre. */
|
|
UFUNCTION(BlueprintPure, Category = "Storage")
|
|
FText GetDisplayName() const { return DisplayName; }
|
|
|
|
/** Cases par ligne souhaitees pour la grille de l'ecran. */
|
|
UFUNCTION(BlueprintPure, Category = "Storage")
|
|
int32 GetColumnCount() const { return ColumnCount; }
|
|
|
|
/**
|
|
* Ouvre ou ferme le couvercle.
|
|
*
|
|
* Appele par le controller, pas par le coffre lui-meme : c'est lui qui sait
|
|
* quand l'ecran se referme, que ce soit par Echap, par la mort du joueur ou
|
|
* parce qu'il s'est trop eloigne. Un coffre qui deciderait seul finirait
|
|
* ouvert dans la moitie de ces cas.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "Storage")
|
|
void SetOpen(bool bInOpen);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Storage")
|
|
bool IsOpen() const { return bOpen; }
|
|
|
|
protected:
|
|
/**
|
|
* Dimensionne le composant AVANT que son InitializeComponent() fige la
|
|
* taille par defaut. PostInitializeComponents serait deja trop tard, et le
|
|
* constructeur trop tot -- il ne connait pas les valeurs reglees sur
|
|
* l'instance posee dans le niveau.
|
|
*/
|
|
virtual void PreInitializeComponents() override;
|
|
|
|
virtual void BeginPlay() override;
|
|
|
|
/** Redimensionne la boite quand un mesh change dans le Blueprint. */
|
|
virtual void OnConstruction(const FTransform& Transform) override;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Composants
|
|
// ------------------------------------------------------------------
|
|
|
|
/** La caisse : SM_Chest_Bottom. Racine, purement visuelle. */
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TObjectPtr<UStaticMeshComponent> BaseMesh;
|
|
|
|
/**
|
|
* L'unique collision du coffre : ce que le trace d'interaction vient
|
|
* toucher, et ce qui empeche le joueur de traverser.
|
|
*
|
|
* Fille de la caisse et non du pivot : elle ne suit surtout pas le
|
|
* couvercle. Un coffre ouvert doit se viser au meme endroit qu'un coffre
|
|
* ferme, sinon le prompt disparait au moment precis ou l'on s'en sert.
|
|
*/
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TObjectPtr<UBoxComponent> InteractionBox;
|
|
|
|
/**
|
|
* La charniere. Place-la a la souris sur l'arete arriere du coffre.
|
|
*
|
|
* Ce composant vide n'est pas un luxe : le pivot de SM_Chest_Top est celui
|
|
* que le pack lui a donne, presque jamais la charniere. Faire tourner le
|
|
* mesh directement le ferait pivoter autour de son centre et traverser la
|
|
* caisse. Ici on tourne le pivot, et le mesh suit avec son decalage.
|
|
*/
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TObjectPtr<USceneComponent> LidPivot;
|
|
|
|
/** Le couvercle : SM_Chest_Top, enfant du pivot. */
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TObjectPtr<UStaticMeshComponent> LidMesh;
|
|
|
|
/** Le contenu. Un inventaire ordinaire, sans barre rapide. */
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TObjectPtr<UInventoryComponent> Storage;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Reglages
|
|
// ------------------------------------------------------------------
|
|
|
|
/**
|
|
* Nombre de cases. Reglable PAR INSTANCE : le meme Blueprint donne un petit
|
|
* coffre de 12 et un grand de 30 selon celui qu'on pose.
|
|
*/
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage", meta = (ClampMin = "1", ClampMax = "60"))
|
|
int32 SlotCount = 20;
|
|
|
|
/** Cases par ligne dans l'ecran. Purement visuel. */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage", meta = (ClampMin = "1"))
|
|
int32 ColumnCount = 6;
|
|
|
|
/** Nom affiche en titre de l'ecran. */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
FText DisplayName;
|
|
|
|
/** Texte affiche quand le joueur vise le coffre. */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
FText InteractionPrompt;
|
|
|
|
/** Verse dans le coffre au demarrage. Laisse vide pour un coffre a remplir. */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage")
|
|
TArray<FStorageStartingItem> StartingItems;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Collision
|
|
// ------------------------------------------------------------------
|
|
|
|
/**
|
|
* La boite epouse toute seule les deux meshes, couvercle ferme.
|
|
*
|
|
* Coche par defaut : changer SM_Chest_v1 pour le v3 doit suffire, sans
|
|
* avoir a retailler une boite a la main derriere. Decoche-le seulement si
|
|
* tu veux une zone volontairement differente du volume du coffre.
|
|
*/
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Collision")
|
|
bool bAutoFitInteractionBox = true;
|
|
|
|
/**
|
|
* Marge ajoutee autour des meshes par l'ajustement automatique.
|
|
*
|
|
* Garde-la petite. Le trace d'interaction est deja un balayage spherique
|
|
* tolerant : gonfler la boite ne rendrait pas le coffre plus facile a viser,
|
|
* ca l'empecherait juste de se coller a un mur.
|
|
*/
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Collision", meta = (ClampMin = "0", Units = "cm"))
|
|
float InteractionBoxPadding = 0.f;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Couvercle
|
|
// ------------------------------------------------------------------
|
|
|
|
/**
|
|
* Rotation RELATIVE du pivot couvercle ouvert. Le repos vaut toujours zero.
|
|
*
|
|
* Un FRotator complet et pas un simple angle : selon l'orientation que tu
|
|
* donnes au pivot, la charniere peut tomber sur le pitch, le yaw ou le roll.
|
|
* Regle les trois a l'oeil dans le viewport plutot que d'aller comprendre
|
|
* quel axe local du mesh pointe ou.
|
|
*/
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Lid")
|
|
FRotator OpenRotation = FRotator(-100.f, 0.f, 0.f);
|
|
|
|
/** Duree de l'ouverture, en secondes. */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Lid", meta = (ClampMin = "0.05", Units = "s"))
|
|
float LidDuration = 0.35f;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Sons. Facultatifs : un champ vide ne joue rien.
|
|
// ------------------------------------------------------------------
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Sound")
|
|
TObjectPtr<USoundBase> OpenSound;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Storage|Sound")
|
|
TObjectPtr<USoundBase> CloseSound;
|
|
|
|
private:
|
|
/** Recopie LidAlpha dans la rotation du pivot. */
|
|
void ApplyLidRotation();
|
|
|
|
/** Cale la boite sur le volume combine des deux meshes, couvercle ferme. */
|
|
void FitInteractionBox();
|
|
|
|
bool bOpen = false;
|
|
|
|
/** 0 = ferme, 1 = ouvert. Interpole, jamais pose directement. */
|
|
float LidAlpha = 0.f;
|
|
};
|