(Feat) Add Storage Chests + Item Details Panel
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>
This commit is contained in:
+83
-3
@@ -115,10 +115,12 @@ qu'une compilation propre l'est vraiment.
|
||||
| HUD, input d'interface, fondus, respawn | `FpsPlayerController.h/.cpp` |
|
||||
| Interaction (trace + interface) | `Interactable.h`, `InteractionComponent`, `InteractionPromptWidget` |
|
||||
| Objets | `ItemDataAsset`, `PickupItem` |
|
||||
| Inventaire | `InventoryComponent`, `InventoryWidget`, `InventorySlotWidget`, `HotbarWidget`, `InventoryDragDropOperation` |
|
||||
| Inventaire | `InventoryComponent`, `InventoryWidget`, `InventorySlotWidget`, `HotbarWidget`, `InventoryDragDropOperation`, `ItemDetailsWidget` |
|
||||
| Survie | `SurvivalStatsComponent`, `SurvivalStatsWidget` |
|
||||
| Menu principal | `MainMenuGameMode`, `MainMenuPlayerController`, `MainMenuWidget`, `MenuCameraSpot` |
|
||||
| Fabrication | `CraftingRecipeDataAsset`, `CraftingRecipeBook`, `CraftingComponent`, `CraftingWidget`, `CraftingRecipeSlotWidget`, `CraftingIngredientRowWidget` |
|
||||
| Cuisson | `CookingStation` + bloc *Cooking* / *Fuel* de `ItemDataAsset` — **doc complète : [Docs/CookingSystem.md](../Docs/CookingSystem.md)** |
|
||||
| Coffres | `StorageContainer`, `StorageScreenWidget` + `UInventoryComponent::TransferSlot` / `TransferAllTo` |
|
||||
| Écran à onglets (inventaire / craft) | `InventoryScreenWidget` |
|
||||
| Menu de pause | `PauseMenuWidget` + la partie pause de `FpsPlayerController` |
|
||||
| Réglages | `SurvivalUserSettings`, `SettingsMenuWidget`, `SettingsTabWidget`, `SettingsGameplayWidget`, `SettingRowWidget` |
|
||||
@@ -171,10 +173,88 @@ qu'une compilation propre l'est vraiment.
|
||||
engendrer un acteur, et un établi déposerait son surplus sur sa propre table.
|
||||
- Le résultat passe par `AddItem()`, donc **exactement comme un ramassage** : piles entamées
|
||||
d'abord, puis barre rapide, puis grille.
|
||||
- **Cuisson : aucune interface, tout dans le monde.** On dépose l'objet sur le feu, on regarde
|
||||
le mesh changer. Le prompt n'affiche **que des actions réalisables** — jamais l'état du
|
||||
poste, qui se lit sur les flammes, le nombre de bûches et le mesh de la nourriture. Cuire
|
||||
n'est **pas une recette** mais une chaîne de DataAssets (cru → cuit → brûlé), donc le
|
||||
changement de visuel est gratuit. Le carburant est une **pile d'objets**, pas une jauge de
|
||||
secondes : une bûche visible = un exemplaire donné, et la capacité EST le nombre de bûches
|
||||
posées dans le Blueprint. Le feu s'allume et s'arrête seul — il brûle si et seulement s'il a
|
||||
du bois **et** du travail, la carbonisation comptant comme du travail pour que la
|
||||
sur-cuisson reste punitive. Détail et pièges : [Docs/CookingSystem.md](../Docs/CookingSystem.md).
|
||||
- **Un coffre EST un `UInventoryComponent`**, configuré par `ConfigureAsContainer()` : zéro
|
||||
barre rapide, donc `GetBackpackStartIndex()` vaut 0 et la grille affiche tout. Aucun nouveau
|
||||
modèle de données — d'où la durabilité qui traverse le coffre sans une ligne de code.
|
||||
Le déplacement est désormais **unique** : `UInventoryComponent::TransferSlot(From, i, To, j, N)`
|
||||
est statique, gère indifféremment un ou deux inventaires, et diffuse `OnInventoryChanged`
|
||||
**des deux côtés** ; `MoveItem`/`MoveItemQuantity` ne sont plus que des appels avec
|
||||
`From == To`. Oublier la seconde diffusion laisse la grille d'en face afficher un état périmé,
|
||||
ce qui se lit comme un objet qui disparaît.
|
||||
- **Le coffre n'a pas d'écran à lui : c'est un panneau de plus sur `WBP_InventoryScreen`**,
|
||||
frère du WidgetSwitcher, `Collapsed` par défaut. La raison est du ressenti pur — le sac du
|
||||
joueur ne bouge pas d'un pixel entre « j'ouvre mon sac » et « j'ouvre un coffre », alors
|
||||
qu'un écran distinct le ferait sauter à chaque fois. Ce n'est **pas** un troisième onglet non
|
||||
plus : les deux grilles doivent être visibles **ensemble**, ce qu'un WidgetSwitcher interdit.
|
||||
Techniquement c'est une **deuxième instance du même `WBP_Inventory`** — elle ne peut pas
|
||||
vivre *dans* `WBP_Inventory`, un Widget Blueprint qui se contient lui-même est une référence
|
||||
circulaire qu'UMG refuse de compiler. D'où le flag `bAutoBindToPawn` : sans lui, la grille du
|
||||
coffre s'accroche au pawn dans son `NativeConstruct` et affiche le sac des deux côtés.
|
||||
Corollaire : l'onglet Fabrication est **grisé** tant qu'un coffre est ouvert (le panneau
|
||||
resterait affiché à côté de la grille de recettes), et la touche de craft rend le coffre.
|
||||
- **Clic droit = envoyer en face**, via `AddItem()` donc exactement comme un ramassage. La
|
||||
destination est **poussée** par l'écran (`SetQuickTransferTarget`), jamais devinée : hors de
|
||||
l'écran de coffre il n'existe aucun « autre côté », et deviner ferait disparaître des objets.
|
||||
« Tout ranger » part de `GetBackpackStartIndex()` : la barre rapide porte les outils, les
|
||||
ranger d'office serait une punition déguisée en confort.
|
||||
- **Le coffre ne décide de rien.** Il appelle `OpenStorage(this)` ; c'est le contrôleur qui
|
||||
referme le couvercle — par Échap, par la mort, ou parce que le joueur s'est éloigné
|
||||
(`MaxStorageDistance`, surveillé par un timer à 0,25 s, jamais un Tick). Un coffre qui
|
||||
déciderait seul finirait ouvert dans la moitié de ces cas.
|
||||
- **`LidPivot`**, un `SceneComponent` vide entre la caisse et le couvercle : le pivot de
|
||||
`SM_Chest_Top` est celui du pack, presque jamais la charnière. Sans lui le couvercle tourne
|
||||
autour de son centre et traverse la caisse. Le coffre ne tick **que** pendant l'animation.
|
||||
- **Toute la collision du coffre tient dans une `UBoxComponent`, les meshes n'en portent
|
||||
aucune.** C'est la seule exception à la règle « le mesh EST la surface d'interaction » posée
|
||||
par `ACraftingStation`, et elle vient de ce qu'un coffre est en **deux morceaux dont un
|
||||
pivote** : compter sur leurs collisions imposerait que le pack en fournisse deux correctes,
|
||||
et laisserait surtout un trou dans la surface visable dès que le couvercle se lève. La boîte
|
||||
est fille de la **caisse**, jamais du pivot — le coffre se vise donc au même endroit ouvert
|
||||
et fermé. Elle s'ajuste seule au volume des deux meshes dans `OnConstruction`
|
||||
(`bAutoFitInteractionBox`), donc changer de variante de coffre ne demande aucun retaillage.
|
||||
- **La barre rapide se masque sur l'onglet Fabrication.** `UInventoryScreenWidget` se contente
|
||||
de diffuser `OnTabChanged` ; c'est le contrôleur qui décide, via une unique
|
||||
`UpdateHotbarVisibility()`. Un seul point de décision, sinon fermer l'écran depuis l'onglet
|
||||
craft laisse la barre cachée pour le reste de la partie.
|
||||
- **Le détail de l'objet survolé vit *dans* `WBP_Inventory`**, à droite de la grille, et non sur
|
||||
`WBP_InventoryScreen` : c'est un panneau de la grille, il doit la suivre — l'onglet
|
||||
Fabrication l'emporte donc avec la page, sans une ligne de code. Contenu volontairement
|
||||
réduit : icône, nom, trait de séparation, description, barre d'usure. Pas de fond, la largeur
|
||||
est tenue par un Size Box — sans lui, un panneau vide ferait sauter la grille latéralement.
|
||||
`UItemDetailsWidget` reçoit un couple **(inventaire, index)**, jamais une copie du
|
||||
`FInventorySlot` : une charge consommée sous le curseur doit se voir, d'où son abonnement à
|
||||
`OnInventoryChanged`. La grille masque `ContentRoot` en bloc quand rien n'est survolé, sinon
|
||||
le trait de séparation resterait seul à l'écran.
|
||||
- **Aucune case à cocher pour ce panneau : c'est l'écran qui l'allume et l'éteint**
|
||||
(`SetItemDetailsEnabled`). `WBP_Inventory` étant instancié **deux fois** sur l'écran de coffre,
|
||||
un réglage par instance serait un bug qui attend qu'on oublie de le décocher. La grille de
|
||||
coffre n'en a donc jamais, et le sac perd le sien tant qu'un coffre est ouvert — trois
|
||||
colonnes seraient illisibles. `HideStorage()` étant appelé dès `NativeConstruct`, l'état par
|
||||
défaut est reposé à chaque construction plutôt que hérité de la Designer.
|
||||
- **Une case ne connaît plus sa grille, elle diffuse `OnHoverChanged`.** La barre rapide pose
|
||||
les mêmes `WBP_InventorySlot` sans être un `UInventoryWidget` : lui passer `nullptr` comme
|
||||
parent était acceptable tant que personne n'écoutait le survol. La barre relaie vers le
|
||||
**contrôleur**, seul à posséder à la fois le HUD et l'écran — un widget de HUD n'a pas à
|
||||
connaître l'écran d'inventaire. Le panneau efface donc **la case qu'on quitte**
|
||||
(`ClearSlot(inventaire, index)`), jamais « le panneau » : passer d'une case de la barre à une
|
||||
case de la grille émet *entre dans B* puis *sort de A*, et un effacement inconditionnel
|
||||
viderait ce qui vient d'être affiché.
|
||||
- **`NativeOnMouseLeave` n'arrive jamais quand un widget s'efface sous le curseur.** C'est
|
||||
exactement ce qui se passe en fermant un coffre, d'où le passage obligé par
|
||||
`SetHoveredSlot()` dans `ClearSlotWidgets()` et `BindToInventory()` — cette dernière annonce
|
||||
la sortie **avant** de changer d'inventaire, sinon on ne sait plus quel affichage périmer.
|
||||
|
||||
Le détail du système de craft — API, câblage éditeur, dépannage — est dans
|
||||
[`Docs/Crafting.md`](../Docs/Crafting.md).
|
||||
|
||||
### Pièges UMG rencontrés sur l'écran de craft
|
||||
|
||||
@@ -215,8 +295,8 @@ qu'une compilation propre l'est vraiment.
|
||||
|
||||
## État actuel et suite
|
||||
|
||||
Boucle complète : récolter → **fabriquer** → ranger → consommer → survivre → mourir →
|
||||
réapparaître. Autour : menu principal, menu de pause, et un écran de réglages dont **seul
|
||||
Boucle complète : récolter → **fabriquer** → **cuire** → ranger → consommer → survivre →
|
||||
mourir → réapparaître. Autour : menu principal, menu de pause, et un écran de réglages dont **seul
|
||||
l'onglet « Jeu & Contrôles » est rempli**.
|
||||
|
||||
Le craft est en place de bout en bout : `DA_RecipeBook`, onglets Inventaire / Fabrication,
|
||||
|
||||
Reference in New Issue
Block a user