Files
2026-08-16 19:35:27 +02:00

181 lines
7.0 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "ItemDataAsset.h"
#include "HarvestableDataAsset.generated.h"
class UItemDataAsset;
class UNiagaraSystem;
class USoundBase;
/**
* Une ligne de butin : « 1 a 3 Bois, 70 % du temps ».
*
* Le tirage n'a lieu QUE sur le serveur (AHarvestableResource::TryHarvest), donc
* l'aleatoire ne peut pas diverger d'une machine a l'autre -- les clients ne
* recoivent jamais le jet, seulement l'objet qui atterrit dans l'inventaire.
*/
USTRUCT(BlueprintType)
struct FHarvestDrop
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Drop")
TObjectPtr<UItemDataAsset> Item;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Drop", meta = (ClampMin = "1"))
int32 MinQuantity = 1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Drop", meta = (ClampMin = "1"))
int32 MaxQuantity = 1;
/** Probabilite que cette ligne tombe. 1 = toujours. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Drop", meta = (ClampMin = "0", ClampMax = "1"))
float Chance = 1.f;
/** Tire une quantite pour ce coup. Renvoie 0 quand le jet echoue. */
int32 Roll() const;
};
/**
* Les REGLES de recolte d'une ressource : ce qu'il faut pour l'abattre, ce
* qu'elle rend, en combien de temps elle repousse.
*
* Un asset par ESPECE, pas par instance ni par mesh : les quinze variantes de
* sapin du pack partagent toutes DA_Harvest_Tree, chacune posant son propre mesh
* sur son acteur. C'est la meme separation que UItemDataAsset et APickupItem --
* la donnee decrit une categorie, l'acteur porte le visuel et l'etat.
*
* Ce qui ne doit JAMAIS venir ici : les points de vie restants, le fait qu'un
* arbre soit deja abattu, l'heure de sa repousse. C'est de l'etat, il vit sur
* AHarvestableResource -- sinon abattre un arbre abattrait toute la foret.
*/
UCLASS(BlueprintType)
class EMBERWILD_API UHarvestableDataAsset : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
/** Identifiant stable, pour la sauvegarde a venir. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable")
FName HarvestableId;
/** Nom lisible, pour les logs et un futur message « Il vous faut une hache ». */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable")
FText DisplayName;
/**
* Outil capable d'entamer cette ressource.
*
* `None` ne veut pas dire « n'importe quoi » mais « a mains nues » : c'est ce
* qui rend un buisson de baies cueillable au poing, sans code particulier.
* Un arbre demande Axe, un filon Pickaxe.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable")
EToolType RequiredTool = EToolType::Axe;
/**
* Points a retirer avant epuisement.
*
* En points et pas en nombre de coups, pour que la hache en fer abatte en
* quatre coups l'arbre qui en demande dix a la hache de pierre : c'est
* `HarvestPower` de l'outil qui fait la difference. Un compte de coups en dur
* ici obligerait a dupliquer chaque ressource par palier d'outil.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable", meta = (ClampMin = "1"))
float Health = 100.f;
/**
* Ce que rend CHAQUE coup, meme celui qui n'abat pas.
*
* C'est la moitie qui rend le geste satisfaisant : un arbre qui ne donne rien
* pendant neuf coups laisse croire qu'on tape dans le vide. Garde les
* quantites basses -- c'est le lot final qui doit peser.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable")
TArray<FHarvestDrop> PerHitDrops;
/** Bonus du coup qui epuise la ressource. C'est la recompense de l'abattage. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable")
TArray<FHarvestDrop> DepletionDrops;
/**
* Secondes avant que la ressource redevienne recoltable. 0 = jamais.
*
* Une souche qui repousse dispense d'un systeme de spawn tant que la carte
* est peuplee a la main : rien ne disparait, donc rien n'a a etre recree.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable", meta = (ClampMin = "0", Units = "s"))
float RegrowDelay = 180.f;
// ------------------------------------------------------------------
// Retour au joueur
//
// Sur la DONNEE et non sur l'acteur : c'est l'espece qui decide qu'un coup
// de hache dans du bois claque et qu'un coup de pioche dans la pierre
// resonne. Regler ca instance par instance sur quatre cents arbres poses
// dans le niveau serait ingerable.
// ------------------------------------------------------------------
/** Joue a chaque coup qui entame reellement la ressource. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback")
TObjectPtr<USoundBase> HitSound;
/** Copeaux, eclats de pierre. Joue au point d'impact, oriente par la normale. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback")
TObjectPtr<UNiagaraSystem> HitEffect;
/**
* Le coup n'a rien fait : mauvais outil, ou ressource deja epuisee.
*
* Un seul son pour les deux cas, et c'est volontaire : dans les deux le
* joueur doit entendre que son geste ne sert a rien. Le laisser vide donne un
* coup parfaitement muet, ce que l'oreille lit comme un bug.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback")
TObjectPtr<USoundBase> IneffectiveHitSound;
/** Craquement de chute, eboulement. Joue sur le coup qui epuise. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback")
TObjectPtr<USoundBase> DepletionSound;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback")
TObjectPtr<UNiagaraSystem> DepletionEffect;
/**
* Angle maximal du balancement encaisse a chaque coup, en degres.
*
* 0 pour un rocher : la pierre ne plie pas, et la voir osciller casse
* immediatement l'illusion de masse. Quelques degres suffisent sur un arbre --
* au-dela le tronc a l'air en caoutchouc.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback",
meta = (ClampMin = "0", ClampMax = "45", Units = "deg"))
float HitShakeAngle = 3.f;
/** Duree du balancement amorti, en secondes. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback",
meta = (ClampMin = "0", Units = "s"))
float HitShakeDuration = 0.45f;
/** Oscillations par seconde. Plus haut = plus nerveux, plus leger. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Harvestable|Feedback", meta = (ClampMin = "0.1"))
float HitShakeFrequency = 7.f;
/** Vrai si cette ressource se recolte a mains nues. */
UFUNCTION(BlueprintPure, Category = "Harvestable")
bool IsHarvestableByHand() const { return RequiredTool == EToolType::None; }
#if WITH_EDITOR
/**
* Une table de butin dont une ligne pointe dans le vide ne planterait pas :
* l'arbre donnerait simplement moins, sans rien dire. C'est le genre de bug
* qu'on cherche une heure -- autant que l'editeur le refuse a la sauvegarde.
*/
virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
#endif
};