(Feat) Add Animation Fps Charater
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "InventorySlotWidget.h"
|
||||
|
||||
#include "Components/Image.h"
|
||||
#include "Components/ProgressBar.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Components/Widget.h"
|
||||
#include "InventoryDragDropOperation.h"
|
||||
#include "ItemDataAsset.h"
|
||||
|
||||
void UInventorySlotWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Force en C++ plutot que de dependre d'un reglage du Blueprint : une case
|
||||
// non testable a la souris ne recevrait jamais NativeOnMouseButtonDown, et
|
||||
// le glisser-deposer serait mysterieusement inerte.
|
||||
// NativeConstruct s'execute APRES ConfigureAsDragVisual() pour un fantome
|
||||
// (le widget n'est construit qu'une fois ajoute a la couche de drag),
|
||||
// d'ou le test : sans lui, on remettrait le fantome en Visible et il
|
||||
// intercepterait le depot destine a la case du dessous.
|
||||
SetVisibility(bIsDragVisual ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Visible);
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::SetSelected(bool bSelected)
|
||||
{
|
||||
if (SelectionBorder)
|
||||
{
|
||||
SelectionBorder->SetVisibility(bSelected ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::ConfigureAsDragVisual()
|
||||
{
|
||||
bIsDragVisual = true;
|
||||
|
||||
// Le cadre de selection appartient a la barre, pas a l'objet.
|
||||
if (SelectionBorder)
|
||||
{
|
||||
SelectionBorder->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
|
||||
// Le fond de case appartient a la grille, pas a l'objet. Le trainer sous
|
||||
// le curseur donne l'impression de deplacer la case elle-meme.
|
||||
if (SlotBackground)
|
||||
{
|
||||
SlotBackground->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
|
||||
SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::InitSlot(UInventoryComponent* InInventory, int32 InSlotIndex)
|
||||
{
|
||||
OwningInventory = InInventory;
|
||||
SlotIndex = InSlotIndex;
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::SetQuickTransferTarget(UInventoryComponent* InTarget)
|
||||
{
|
||||
QuickTransferTarget = InTarget;
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
Super::NativeOnMouseEnter(InGeometry, InMouseEvent);
|
||||
|
||||
OnHoverChanged.Broadcast(SlotIndex, true);
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
Super::NativeOnMouseLeave(InMouseEvent);
|
||||
|
||||
OnHoverChanged.Broadcast(SlotIndex, false);
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::SetSlot(const FInventorySlot& InSlot)
|
||||
{
|
||||
CachedSlot = InSlot;
|
||||
|
||||
// Filet de securite : toute mise a jour de contenu remet la case en pleine
|
||||
// opacite, meme si un glisser s'est termine par un chemin imprevu.
|
||||
if (!bIsDragVisual)
|
||||
{
|
||||
SetRenderOpacity(1.f);
|
||||
}
|
||||
|
||||
const bool bEmpty = InSlot.IsEmpty();
|
||||
|
||||
if (ItemIcon)
|
||||
{
|
||||
// Hidden et non Collapsed : on veut que la case garde sa taille dans
|
||||
// la grille meme vide, sinon la mise en page sauterait a chaque
|
||||
// ramassage.
|
||||
if (bEmpty || !InSlot.Item->Icon)
|
||||
{
|
||||
ItemIcon->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemIcon->SetBrushFromTexture(InSlot.Item->Icon, /*bMatchSize=*/false);
|
||||
ItemIcon->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
}
|
||||
|
||||
if (QuantityText)
|
||||
{
|
||||
// On n'affiche pas "x1" : ca alourdit la grille pour rien.
|
||||
if (bEmpty || InSlot.Quantity <= 1)
|
||||
{
|
||||
QuantityText->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
QuantityText->SetText(FText::AsNumber(InSlot.Quantity));
|
||||
QuantityText->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
}
|
||||
|
||||
if (UsesBar)
|
||||
{
|
||||
// Collapsed et non Hidden : une barre d'usure sur un objet qui n'en a
|
||||
// pas ne doit meme pas reserver sa place au bas de la case.
|
||||
const int32 MaxUses = bEmpty ? 1 : FMath::Max(1, InSlot.Item->MaxUses);
|
||||
if (MaxUses <= 1)
|
||||
{
|
||||
UsesBar->SetVisibility(ESlateVisibility::Collapsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
UsesBar->SetPercent(FMath::Clamp(static_cast<float>(InSlot.RemainingUses) / static_cast<float>(MaxUses), 0.f, 1.f));
|
||||
UsesBar->SetVisibility(ESlateVisibility::HitTestInvisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FReply UInventorySlotWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
|
||||
{
|
||||
// Clic droit : transfert rapide vers l'inventaire d'en face, quand il y en
|
||||
// a un. Traite avant le glisser pour que le bouton droit ne demarre jamais
|
||||
// de DetectDrag -- ce qui laisserait une operation orpheline en cours.
|
||||
if (!CachedSlot.IsEmpty() && InMouseEvent.GetEffectingButton() == EKeys::RightMouseButton)
|
||||
{
|
||||
if (QuickTransferTarget.IsValid() && OwningInventory.IsValid() && SlotIndex != INDEX_NONE)
|
||||
{
|
||||
UInventoryComponent::QuickTransferSlot(OwningInventory.Get(), SlotIndex, QuickTransferTarget.Get());
|
||||
return FReply::Handled();
|
||||
}
|
||||
}
|
||||
|
||||
if (CachedSlot.IsEmpty() || !InMouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
|
||||
{
|
||||
return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
|
||||
}
|
||||
|
||||
// On ne demarre pas le glisser tout de suite : DetectDrag attend que la
|
||||
// souris bouge reellement. Sans ca, un simple clic serait interprete comme
|
||||
// un debut de glisser et on ne pourrait jamais faire autre chose.
|
||||
return FReply::Handled().DetectDrag(TakeWidget(), EKeys::LeftMouseButton);
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& OutOperation)
|
||||
{
|
||||
Super::NativeOnDragDetected(InGeometry, InMouseEvent, OutOperation);
|
||||
|
||||
if (CachedSlot.IsEmpty() || !OwningInventory.IsValid() || SlotIndex == INDEX_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Modificateurs au moment de la saisie : Maj pour la moitie, Ctrl pour un
|
||||
// seul. Conventions etablies du genre, le joueur les connait deja.
|
||||
int32 DraggedQuantity = CachedSlot.Quantity;
|
||||
if (InMouseEvent.IsControlDown())
|
||||
{
|
||||
DraggedQuantity = 1;
|
||||
}
|
||||
else if (InMouseEvent.IsShiftDown())
|
||||
{
|
||||
// Moitie arrondie au superieur : sur une pile de 7 on emporte 4.
|
||||
// C'est la convention de Minecraft et consorts.
|
||||
DraggedQuantity = (CachedSlot.Quantity + 1) / 2;
|
||||
}
|
||||
|
||||
UInventoryDragDropOperation* Operation = NewObject<UInventoryDragDropOperation>();
|
||||
Operation->SourceSlotIndex = SlotIndex;
|
||||
Operation->Quantity = DraggedQuantity;
|
||||
Operation->SourceInventory = OwningInventory;
|
||||
Operation->Pivot = EDragPivot::CenterCenter;
|
||||
|
||||
// Le fantome sous le curseur est une copie de cette meme case : aucun
|
||||
// widget supplementaire a maintenir, et l'apparence reste coherente.
|
||||
// Il affiche la quantite EMPORTEE, pas la pile complete.
|
||||
if (UInventorySlotWidget* Visual = CreateWidget<UInventorySlotWidget>(GetOwningPlayer(), GetClass()))
|
||||
{
|
||||
FInventorySlot VisualSlot = CachedSlot;
|
||||
VisualSlot.Quantity = DraggedQuantity;
|
||||
|
||||
Visual->SetSlot(VisualSlot);
|
||||
Visual->ConfigureAsDragVisual();
|
||||
Visual->SetRenderOpacity(DragVisualOpacity);
|
||||
Operation->DefaultDragVisual = Visual;
|
||||
}
|
||||
|
||||
// On n'estompe la case source que si la pile part en entier. Lors d'une
|
||||
// division une partie reste sur place, et la faire palir serait mentir
|
||||
// sur ce qu'elle contient encore.
|
||||
if (DraggedQuantity >= CachedSlot.Quantity)
|
||||
{
|
||||
SetRenderOpacity(DraggedSlotOpacity);
|
||||
}
|
||||
|
||||
// Les deux issues possibles remettent l'opacite : depot reussi, ou
|
||||
// abandon hors de la grille.
|
||||
Operation->OnDrop.AddDynamic(this, &UInventorySlotWidget::HandleDragFinished);
|
||||
Operation->OnDragCancelled.AddDynamic(this, &UInventorySlotWidget::HandleDragFinished);
|
||||
|
||||
OutOperation = Operation;
|
||||
}
|
||||
|
||||
void UInventorySlotWidget::HandleDragFinished(UDragDropOperation* Operation)
|
||||
{
|
||||
SetRenderOpacity(1.f);
|
||||
}
|
||||
|
||||
bool UInventorySlotWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
|
||||
{
|
||||
const UInventoryDragDropOperation* Payload = Cast<UInventoryDragDropOperation>(InOperation);
|
||||
if (!Payload || SlotIndex == INDEX_NONE)
|
||||
{
|
||||
return Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
|
||||
}
|
||||
|
||||
// TransferSlot diffuse OnInventoryChanged des DEUX cotes, donc les grilles
|
||||
// se rafraichissent seules. Elle gere indifferemment le meme inventaire et
|
||||
// deux inventaires distincts : glisser vers un coffre, vers la barre rapide
|
||||
// ou vers la case d'a cote emprunte exactement le meme chemin.
|
||||
return UInventoryComponent::TransferSlot(
|
||||
Payload->SourceInventory.Get(), Payload->SourceSlotIndex,
|
||||
OwningInventory.Get(), SlotIndex,
|
||||
Payload->Quantity);
|
||||
}
|
||||
Reference in New Issue
Block a user