// Fill out your copyright notice in the Description page of Project Settings. #include "InventoryScreenWidget.h" #include "Components/Button.h" #include "Components/TextBlock.h" #include "Components/WidgetSwitcher.h" #include "CraftingWidget.h" #include "FpsPlayer.h" #include "GameFramework/PlayerController.h" #include "InventoryComponent.h" #include "InventoryDragDropOperation.h" #include "InventoryWidget.h" #include "StorageContainer.h" void UInventoryScreenWidget::NativeConstruct() { Super::NativeConstruct(); if (InventoryTabButton) { InventoryTabButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleInventoryTabClicked); } if (CraftingTabButton) { CraftingTabButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleCraftingTabClicked); } if (StoreAllButton) { StoreAllButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleStoreAllClicked); } if (TakeAllButton) { TakeAllButton->OnClicked.AddDynamic(this, &UInventoryScreenWidget::HandleTakeAllClicked); } // La grille de coffre n'affiche jamais de detail : c'est le meme // WBP_Inventory que le sac, elle en porte donc un exemplaire qu'on coupe une // fois pour toutes ici plutot que de compter sur une case a cocher. if (StorageGrid) { StorageGrid->SetItemDetailsEnabled(false); } // Le panneau de coffre ne se montre qu'a l'ouverture d'un coffre. On le // masque en C++ plutot que de dependre de ce qui a ete regle dans la // Designer : un panneau laisse Visible par erreur afficherait une grille // vide en permanence a cote du sac. HideStorage(); ShowInventoryTab(); } void UInventoryScreenWidget::BindToOwningPawn() { if (InventoryPage) { InventoryPage->BindToOwningPawn(); } if (CraftingPage) { CraftingPage->BindToOwningPawn(); } } bool UInventoryScreenWidget::IsInventoryTabActive() const { return TabSwitcher && InventoryPage && TabSwitcher->GetActiveWidget() == InventoryPage; } int32 UInventoryScreenWidget::GetHoveredSlotIndex() const { if (!InventoryPage || !IsInventoryTabActive()) { return INDEX_NONE; } return InventoryPage->GetHoveredSlotIndex(); } bool UInventoryScreenWidget::GetHoveredSlot(UInventoryComponent*& OutInventory, int32& OutIndex) const { // Le coffre en premier : c'est le panneau le plus a droite, donc celui que // le curseur survole quand les deux sont affiches. if (bStorageVisible && StorageGrid && StorageGrid->GetHoveredSlot(OutInventory, OutIndex)) { return true; } if (InventoryPage && IsInventoryTabActive() && InventoryPage->GetHoveredSlot(OutInventory, OutIndex)) { return true; } OutInventory = nullptr; OutIndex = INDEX_NONE; return false; } void UInventoryScreenWidget::NotifyExternalSlotHovered(UInventoryComponent* Inventory, int32 SlotIndex, bool bHovered) { // Rien a faire sous l'onglet Fabrication : le panneau est parti avec la // page, et la barre rapide y est de toute facon masquee. if (InventoryPage && IsInventoryTabActive()) { InventoryPage->NotifyExternalSlotHovered(Inventory, SlotIndex, bHovered); } } UInventoryComponent* UInventoryScreenWidget::GetPlayerInventory() const { const APlayerController* OwningController = GetOwningPlayer(); AFpsPlayer* PlayerPawn = OwningController ? Cast(OwningController->GetPawn()) : nullptr; return PlayerPawn ? PlayerPawn->GetInventoryComponent() : nullptr; } void UInventoryScreenWidget::ShowStorage(AStorageContainer* Container) { UInventoryComponent* Contents = Container ? Container->GetStorage() : nullptr; if (!Contents) { UE_LOG(LogTemp, Warning, TEXT("UInventoryScreenWidget::ShowStorage : coffre nul ou sans inventaire.")); return; } if (!StoragePanel || !StorageGrid) { UE_LOG(LogTemp, Warning, TEXT("UInventoryScreenWidget : 'StoragePanel' ou 'StorageGrid' manque dans WBP_InventoryScreen, le coffre ne s'affichera pas.")); return; } BoundStorage = Contents; bStorageVisible = true; StorageGrid->SetColumnCount(Container->GetColumnCount()); StorageGrid->BindToInventory(Contents); // Le clic droit envoie d'un cote a l'autre. Le pawn a pu changer depuis la // derniere ouverture (respawn), d'ou la relecture plutot qu'une valeur // gardee en cache. StorageGrid->SetQuickTransferTarget(GetPlayerInventory()); if (InventoryPage) { InventoryPage->SetQuickTransferTarget(Contents); // Le detail disparait en mode coffre : l'ecran passe deja a deux grilles // cote a cote, une troisieme colonne le rendrait illisible. InventoryPage->SetItemDetailsEnabled(false); } if (StorageTitle) { StorageTitle->SetText(Container->GetDisplayName()); } StoragePanel->SetVisibility(ESlateVisibility::Visible); // Un coffre ouvert sous l'onglet Fabrication n'aurait aucun sens : on force // l'onglet Inventaire, et ShowPage grise le bouton Fabrication tant que // bStorageVisible est vrai. ShowInventoryTab(); } void UInventoryScreenWidget::HideStorage() { BoundStorage.Reset(); bStorageVisible = false; // On coupe les transferts rapides AVANT de debrancher : une case qui garde // un coffre en memoire y enverrait encore des objets au prochain clic droit, // panneau ferme. if (InventoryPage) { InventoryPage->SetQuickTransferTarget(nullptr); // Le sac retrouve son panneau. Repose a chaque fermeture, y compris a la // construction : c'est l'etat par defaut de l'ecran, il ne doit dependre // ni de la Designer ni de ce qu'a laisse la session precedente. InventoryPage->SetItemDetailsEnabled(true); } if (StorageGrid) { StorageGrid->SetQuickTransferTarget(nullptr); StorageGrid->BindToInventory(nullptr); } if (StoragePanel) { StoragePanel->SetVisibility(ESlateVisibility::Collapsed); } // Rend la main a l'onglet Fabrication, que ShowPage avait grise. if (CraftingTabButton) { CraftingTabButton->SetIsEnabled(IsInventoryTabActive()); } } void UInventoryScreenWidget::HandleStoreAllClicked() { UInventoryComponent* PlayerInventory = GetPlayerInventory(); if (!PlayerInventory || !BoundStorage.IsValid()) { return; } // A partir de la grille seulement : la barre rapide porte les outils qu'on // vient d'utiliser, et les ranger a chaque retour d'expedition serait une // punition deguisee en confort. PlayerInventory->TransferAllTo(BoundStorage.Get(), PlayerInventory->GetBackpackStartIndex()); } void UInventoryScreenWidget::HandleTakeAllClicked() { UInventoryComponent* PlayerInventory = GetPlayerInventory(); if (!PlayerInventory || !BoundStorage.IsValid()) { return; } // Depuis l'index 0 : un coffre n'a pas de barre rapide. Ce qui ne rentre // pas dans le sac reste dans le coffre. BoundStorage->TransferAllTo(PlayerInventory, 0); } void UInventoryScreenWidget::ShowInventoryTab() { ShowPage(InventoryPage); } void UInventoryScreenWidget::ShowCraftingTab() { ShowPage(CraftingPage); } void UInventoryScreenWidget::HandleInventoryTabClicked() { ShowPage(InventoryPage); } void UInventoryScreenWidget::HandleCraftingTabClicked() { ShowPage(CraftingPage); } void UInventoryScreenWidget::ShowPage(UWidget* Page) { if (!TabSwitcher || !Page) { return; } TabSwitcher->SetActiveWidget(Page); // Le bouton de l'onglet courant est desactive : il sert d'indicateur de // position autant que de garde-fou contre un clic sans effet. const bool bInventoryActive = (Page == InventoryPage); if (InventoryTabButton) { InventoryTabButton->SetIsEnabled(!bInventoryActive); } if (CraftingTabButton) { // Grise aussi tant qu'un coffre est ouvert : ses ressources ne comptent // pas encore dans les recettes, et un clic dont le resultat serait // incomprehensible vaut mieux etre rendu impossible. CraftingTabButton->SetIsEnabled(bInventoryActive && !bStorageVisible); } OnTabChanged.Broadcast(bInventoryActive); } bool UInventoryScreenWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) { Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation); // On n'arrive ici que si ni une case ni la page d'inventaire n'ont traite le // depot : les enfants sont consultes en premier et arretent la propagation. const UInventoryDragDropOperation* Payload = Cast(InOperation); if (!Payload || Payload->SourceSlotIndex == INDEX_NONE) { return true; } // Depuis l'inventaire SOURCE et non celui du joueur : sortir une pile du // coffre en la lachant dans le vide doit la retirer du coffre. if (const APlayerController* OwningController = GetOwningPlayer()) { if (AFpsPlayer* PlayerPawn = Cast(OwningController->GetPawn())) { PlayerPawn->DropFromInventory(Payload->SourceInventory.Get(), Payload->SourceSlotIndex, Payload->Quantity); } } return true; }