// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "CharacterAppearanceTypes.h" #include "CharacterPartsDataAsset.generated.h" class UAnimInstance; class UMaterialInterface; class USkeletalMesh; class UTexture2D; /** * Tout ce qui compose une silhouette : ses meshes de corps, son AnimBP, et les * pieces disponibles pour chaque categorie. */ USTRUCT(BlueprintType) struct FCharacterBodySet { GENERATED_BODY() /** La vignette du bouton de silhouette, meme role que celle d'une piece. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Body") TObjectPtr Icon; /** * Les morceaux du corps, dans l'ordre. Le PREMIER est le porteur : c'est lui * qui va sur le composant Mesh du Character et qui joue l'animation, les * suivants deviennent des composants enfants en leader pose. * * Une LISTE alors qu'il n'y a qu'une case aujourd'hui, et ce n'est pas de la * sur-conception : le pack livre exprès `SKM_BodyA_torso`, `_arms`, * `_legs_01/02/03`, `_hands`, `_feet`, precisement pour qu'une armure puisse * eteindre les segments qu'elle recouvre. Le jour venu, la case 0 deviendra * `SKM_BodyA_empty` -- invisible, porteur d'animation -- et les huit segments * suivront derriere, SANS que ce code change. Un TObjectPtr unique, lui, * imposerait de tout reprendre au premier plastron. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Body") TArray> BodyMeshes; /** L'AnimBP du porteur. Il change avec la silhouette, d'ou sa place ici. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Body") TSubclassOf AnimClass; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts") TArray Heads; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts") TArray Hairstyles; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts") TArray Eyebrows; /** Vide pour une silhouette qui n'en porte pas -- le pack n'en donne qu'a A. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts") TArray Beards; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts") TArray Mustaches; /** * Fragment cherche dans le NOM des slots de materiau du mesh de tete pour y * trouver celui des yeux. * * Les yeux ne sont pas un mesh : le pack les livre comme un element du mesh * de tete. Il ne suffit donc pas de poser un parametre de couleur, il faut * REMPLACER le materiau de ce slot -- encore faut-il savoir lequel. * * Un fragment de nom et pas un index, parce qu'un index est faux deux fois : * il oblige a le verifier a la main dans l'editeur, et il suppose surtout * que les six tetes rangent leurs slots dans le MEME ordre. Les slots du * pack s'appellent `M_Eye_A_01`, `M_Eye`, `M_Eyes_02`... tous contiennent * « Eye », aucun slot de peau ne le contient. * * Si le fragment ne trouve rien, le materiau n'est PAS pose et un * avertissement part dans le log. C'est volontaire : poser l'oeil sur un * slot au hasard repeindrait le visage entier, un symptome qu'on met bien * plus longtemps a diagnostiquer qu'une ligne de log. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Parts") FName EyeMaterialSlotHint = TEXT("Eye"); }; /** * Le catalogue : la table de correspondance entre les index d'une * FCharacterAppearance et les assets reels. * * Un seul asset a remplir, et le SEUL endroit a toucher pour ajouter une * coiffure. C'est aussi lui qui borne ce qu'un client peut demander : une * apparence arrivee du reseau est passee a Sanitize(), qui ramene tout index * hors bornes a une valeur sure. */ UCLASS(BlueprintType) class SURVIVAL_PROJET_API UCharacterPartsDataAsset : public UPrimaryDataAsset { GENERATED_BODY() public: /** * Une entree par valeur d'ECharacterBodyType, dans l'ordre de l'enum. * L'ordre EST le contrat : c'est lui qui donne son sens a BodyType. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog") TArray BodySets; // ------------------------------------------------------------------ // Palettes : une couleur = un bouton a l'ecran, sans reglage entre les // deux. L'ordre EST l'ordre d'affichage, et l'index qui part sur le reseau. // // Partagees par les deux silhouettes : deux nuanciers de peau a tenir a // jour divergeraient au premier ajout. // ------------------------------------------------------------------ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes") TArray SkinColors; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes") TArray HairColors; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes") TArray EyeColors; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes") TArray UnderwearColors; /** Les materiaux d'iris, un par forme d'oeil proposee. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Palettes") TArray> EyeMaterials; // ------------------------------------------------------------------ // Noms des parametres des materiaux du pack. // // Ici et pas en dur dans le C++ : ils appartiennent aux assets, pas au // code. Changer de pack de personnages ne doit demander qu'un nouveau // catalogue -- et ces noms contiennent des ESPACES, ce qu'on n'invente // pas de memoire. // ------------------------------------------------------------------ /** `M_Body_Base`. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters") FName SkinColorParameter = TEXT("Skin color"); /** * `M_Head_Base` -- le meme role que ci-dessus, mais l'autre master, et * surtout un NOM DIFFERENT : `Skin` cote tete, `Skin color` cote corps. * * Le master contient bien la chaine « Skin Base color », mais ce n'est pas * le nom du parametre expose -- verifie dans `MI_HeadA_01_a`, ou le chercher * ne rend aucun resultat alors que `Skin` apparait dans le groupe * « Base colors ». Morale : un nom de parametre se lit dans l'editeur du * Material Instance, jamais dans les chaines du binaire du master. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters") FName HeadSkinColorParameter = TEXT("Skin"); UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters") FName UnderwearColorParameter = TEXT("Underware color"); /** `M_Hairstyle_Base`, parent des cheveux, barbes, moustaches ET sourcils. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters") FName HairColorParameter = TEXT("Color"); /** `M_Eyes_Base`. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Catalog|Material Parameters") FName EyeColorParameter = TEXT("Eye color"); // ------------------------------------------------------------------ // Lecture // ------------------------------------------------------------------ /** Le jeu de pieces d'une silhouette, ou nullptr si l'index est hors bornes. */ const FCharacterBodySet* GetBodySet(ECharacterBodyType BodyType) const; /** La liste d'une categorie pour une silhouette, vide si rien n'est defini. */ const TArray& GetParts(ECharacterBodyType BodyType, ECharacterPartCategory Category) const; /** Nombre d'options d'une categorie. Sert a borner l'UI comme la console. */ UFUNCTION(BlueprintPure, Category = "Catalog") int32 GetPartCount(ECharacterBodyType BodyType, ECharacterPartCategory Category) const; /** * Ramene une apparence dans les bornes du catalogue. * * Appelee sur le SERVEUR a la reception d'une apparence client, et au * chargement des reglages -- un catalogue ampute entre deux versions du jeu * laisserait sinon des index qui ne designent plus rien. Les pieces * facultatives (barbe, moustache) retombent sur NoPart, les obligatoires * sur 0 : mieux vaut la mauvaise tete que pas de tete. */ void Sanitize(FCharacterAppearance& InOut) const; };