194 lines
6.5 KiB
C++
194 lines
6.5 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Blueprint/UserWidget.h"
|
|
#include "CharacterAppearanceTypes.h"
|
|
#include "CharacterPreviewActor.h"
|
|
#include "CharacterCustomizationWidget.generated.h"
|
|
|
|
class ACharacterPreviewActor;
|
|
class UButton;
|
|
class UCharacterPartsDataAsset;
|
|
class UCustomizationCategoryWidget;
|
|
class UCustomizationOptionWidget;
|
|
class UPanelWidget;
|
|
|
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCustomizationConfirmed);
|
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCustomizationCancelled);
|
|
|
|
/**
|
|
* Ce que le joueur peut regler, dans l'ordre d'affichage de la colonne.
|
|
*
|
|
* Pieces et couleurs sont dans le MEME enum : ce sont onze rangees de la meme
|
|
* colonne, et les distinguer par deux listes obligerait a dessiner deux fois la
|
|
* meme chose. La difference ne vit que dans BuildOptions, qui remplit la grille
|
|
* de vignettes ou de pastilles selon le cas.
|
|
*/
|
|
UENUM(BlueprintType)
|
|
enum class ECustomizationCategory : uint8
|
|
{
|
|
Body,
|
|
Head,
|
|
Hair,
|
|
Eyebrows,
|
|
Beard,
|
|
Mustache,
|
|
Eyes,
|
|
SkinColor,
|
|
HairColor,
|
|
EyeColor,
|
|
UnderwearColor,
|
|
|
|
Count UMETA(Hidden)
|
|
};
|
|
|
|
/**
|
|
* L'ecran de customisation : la colonne de reglages a gauche, le personnage a
|
|
* droite.
|
|
*
|
|
* Le widget ne monte rien lui-meme -- il pousse une FCharacterAppearance sur
|
|
* ACharacterPreviewActor et lit le catalogue pour savoir quoi proposer. C'est
|
|
* le meme partage des roles que partout ailleurs dans le projet : l'interface
|
|
* affiche et demande, le systeme decide.
|
|
*
|
|
* Classe Abstract : on cree un WBP_CharacterCustomization qui en herite.
|
|
*/
|
|
UCLASS(Abstract)
|
|
class EMBERWILD_API UCharacterCustomizationWidget : public UUserWidget
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UCharacterCustomizationWidget(const FObjectInitializer& ObjectInitializer);
|
|
|
|
virtual void NativeOnInitialized() override;
|
|
virtual void NativeConstruct() override;
|
|
|
|
/**
|
|
* Echap = Retour, comme le bouton.
|
|
*
|
|
* Obligatoire ici et pas seulement confortable : dans la map du menu, le
|
|
* controller ne binde AUCUNE action d'input. Tout Echap qui fonctionne passe
|
|
* donc par le NativeOnKeyDown d'un widget -- il n'y a pas d'autre chemin.
|
|
*/
|
|
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
|
|
|
|
/** Le joueur valide : l'ecran a fini, l'apparence est dans GetAppearance(). */
|
|
UPROPERTY(BlueprintAssignable, Category = "Customization")
|
|
FOnCustomizationConfirmed OnConfirmed;
|
|
|
|
/** Le joueur renonce : rien n'a ete valide, on rend la main au menu. */
|
|
UPROPERTY(BlueprintAssignable, Category = "Customization")
|
|
FOnCustomizationCancelled OnCancelled;
|
|
|
|
/**
|
|
* Branche l'ecran sur son mannequin et son point de depart.
|
|
*
|
|
* A appeler AVANT d'ajouter le widget au viewport : NativeConstruct
|
|
* reconstruit les listes, et il les batirait vides sans catalogue.
|
|
*
|
|
* Surtout PAS nommee Initialize : UUserWidget en a deja une, virtuelle, et
|
|
* une surcharge de signature differente la masquerait au lieu de la
|
|
* surcharger (C4263/C4264). Meme famille de piege que les membres nommes
|
|
* Player ou Slot dans une classe derivee du moteur.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "Customization")
|
|
void SetupCustomization(ACharacterPreviewActor* InPreview, const FCharacterAppearance& InAppearance);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Customization")
|
|
const FCharacterAppearance& GetAppearance() const { return Appearance; }
|
|
|
|
protected:
|
|
/**
|
|
* Le catalogue. Assigne-lui le meme DA_CharacterParts que BP_FpsPlayer.
|
|
*
|
|
* Sur le widget et pas pris sur le mannequin : l'ecran doit pouvoir batir
|
|
* ses listes meme si le mannequin manque encore, et un catalogue different
|
|
* des deux cotes se verrait immediatement a l'ecran.
|
|
*/
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
|
|
TObjectPtr<UCharacterPartsDataAsset> Catalog;
|
|
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
|
|
TSubclassOf<UCustomizationCategoryWidget> CategoryWidgetClass;
|
|
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
|
|
TSubclassOf<UCustomizationOptionWidget> OptionWidgetClass;
|
|
|
|
/** La colonne des categories. Un Vertical Box nomme exactement ainsi. */
|
|
UPROPERTY(meta = (BindWidget))
|
|
TObjectPtr<UPanelWidget> CategoryList;
|
|
|
|
/** La grille d'options. Un Wrap Box va tres bien. */
|
|
UPROPERTY(meta = (BindWidget))
|
|
TObjectPtr<UPanelWidget> OptionGrid;
|
|
|
|
/** Valide et lance la partie. */
|
|
UPROPERTY(meta = (BindWidget))
|
|
TObjectPtr<UButton> PlayButton;
|
|
|
|
/** Retour au menu principal, sans rien valider. */
|
|
UPROPERTY(meta = (BindWidget))
|
|
TObjectPtr<UButton> BackButton;
|
|
|
|
// ------------------------------------------------------------------
|
|
// Rotation du personnage au clic-glisser
|
|
// ------------------------------------------------------------------
|
|
|
|
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
|
virtual FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
|
virtual FReply NativeOnMouseMove(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
|
|
|
private:
|
|
/** Reconstruit la colonne de gauche. Une seule fois, les categories sont fixes. */
|
|
void BuildCategories();
|
|
|
|
/** Reconstruit la grille selon la categorie active. */
|
|
void BuildOptions();
|
|
|
|
/** Repose l'apparence sur le mannequin et rafraichit la selection affichee. */
|
|
void RefreshPreview();
|
|
|
|
/** Encadre la case qui porte le choix courant, efface les autres. */
|
|
void RefreshSelection();
|
|
|
|
/** L'index courant dans la categorie active, pour l'encadrement. */
|
|
int32 GetCurrentIndexForCategory(ECustomizationCategory Category) const;
|
|
|
|
/** Ecrit un choix dans l'apparence. Le seul endroit qui la modifie. */
|
|
void SetIndexForCategory(ECustomizationCategory Category, int32 NewIndex);
|
|
|
|
/** Combien d'options a proposer, catalogue en main. */
|
|
int32 GetOptionCountForCategory(ECustomizationCategory Category) const;
|
|
|
|
UFUNCTION()
|
|
void HandleCategoryClicked(int32 CategoryIndex);
|
|
|
|
UFUNCTION()
|
|
void HandleOptionClicked(int32 OptionIndex);
|
|
|
|
UFUNCTION()
|
|
void HandlePlayClicked();
|
|
|
|
UFUNCTION()
|
|
void HandleBackClicked();
|
|
|
|
UPROPERTY(Transient)
|
|
TObjectPtr<ACharacterPreviewActor> Preview;
|
|
|
|
UPROPERTY(Transient)
|
|
TArray<TObjectPtr<UCustomizationCategoryWidget>> CategoryWidgets;
|
|
|
|
UPROPERTY(Transient)
|
|
TArray<TObjectPtr<UCustomizationOptionWidget>> OptionWidgets;
|
|
|
|
FCharacterAppearance Appearance;
|
|
|
|
ECustomizationCategory ActiveCategory = ECustomizationCategory::Body;
|
|
|
|
/** Vrai entre l'appui et le relachement, pendant qu'on fait tourner. */
|
|
bool bDragging = false;
|
|
};
|