(Feat) Add Harvetable

This commit is contained in:
2026-08-16 19:35:27 +02:00
parent d6f46f32af
commit 2c272c11b9
41 changed files with 1759 additions and 71 deletions
@@ -0,0 +1,126 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Engine/EngineTypes.h"
#include "HarvestableResource.h"
#include "HarvestingComponent.generated.h"
class UItemDataAsset;
/**
* Le COUP : ce qui part quand la hache touche.
*
* A poser sur le pawn du joueur, a cote d'UInteractionComponent -- dont il est le
* pendant exact, et la comparaison est la meilleure facon de comprendre les deux :
*
* UInteractionComponent trace en continu, affiche un prompt, agit sur E.
* UHarvestingComponent ne trace QU'AU moment du coup, n'affiche rien.
*
* On ne « vise » pas un arbre, on le frappe : il n'y a donc ni cible memorisee,
* ni surbrillance, ni texte a l'ecran. Le trace part une fois par coup, depuis
* l'AnimNotify pose dans le montage de l'outil -- jamais a l'appui du clic, sans
* quoi la hache abattrait l'arbre avant de l'avoir touche.
*
* ------------------------------------------------------------------
* Reseau : le client vise, le serveur tranche
* ------------------------------------------------------------------
*
* C'est le schema d'UInteractionComponent, et pour la meme raison : le trace doit
* partir du point de vue REEL du joueur au moment du geste. Le refaire cote
* serveur le referait depuis une position qui a un demi-aller-retour de retard,
* et un joueur en mouvement raterait des arbres qu'il vise pourtant en plein.
*
* La cible part donc dans la RPC (AFpsPlayer::Server_UseItem) et le serveur
* revalide la distance avec une tolerance. Ce n'est PAS de l'anti-triche -- coop
* entre amis -- mais un garde-fou contre un client desynchronise qui abattrait
* un arbre a l'autre bout de la carte.
*
* Aucune RPC ne vit sur ce composant : elles sont toutes sur le pawn, qui portait
* deja Server_UseItem. Un composant qui en porterait aurait besoin de
* SetIsReplicatedByDefault(true), sans quoi le moteur les jetterait en silence.
*/
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
class EMBERWILD_API UHarvestingComponent : public UActorComponent
{
GENERATED_BODY()
public:
UHarvestingComponent();
/**
* Trace le coup depuis le point de vue du joueur. A appeler EN LOCAL.
*
* Renvoie n'importe quel acteur touche, pas seulement une ressource : c'est le
* serveur qui decide de ce qu'on peut recolter, et lui renvoyer un nullptr
* parce qu'on a mal identifie la cible chez nous serait lui retirer ce mot.
*
* @param OutImpactPoint point touche, pour les copeaux et la validation.
* @param OutDirection direction du coup, normalisee.
* @return l'acteur touche, ou nullptr si le coup part dans le vide.
*/
UFUNCTION(BlueprintCallable, Category = "Harvesting")
AActor* TraceSwingTarget(FVector& OutImpactPoint, FVector& OutDirection) const;
/**
* Applique le coup a la cible. A n'appeler que sur le SERVEUR.
*
* @param Tool l'objet en main, nul si les mains sont vides.
* @return ce qui s'est produit ; l'appelant s'en sert pour decider de l'usure.
*/
EHarvestResult ApplySwing(AActor* Target, const UItemDataAsset* Tool,
const FVector& ImpactPoint, const FVector& HitDirection);
/** Points retires par un coup avec cet outil, mains vides comprises. */
UFUNCTION(BlueprintPure, Category = "Harvesting")
float GetSwingPower(const UItemDataAsset* Tool) const;
protected:
/**
* Portee du coup en cm.
*
* Plus courte que celle de l'interaction, et c'est voulu : ramasser une branche
* a bout de bras est un confort, abattre un arbre a la meme distance donnerait
* une hache qui frappe le vide. On doit etre AU pied de l'arbre.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Harvesting", meta = (ClampMin = "1", Units = "cm"))
float SwingRange = 200.f;
/**
* Rayon de la sphere balayee.
*
* Nettement plus large que celui de l'interaction : on ne vise pas un tronc au
* pixel pres, on donne un coup de hache. Un rayon serre rend la recolte penible
* sans rendre le jeu plus exigeant.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Harvesting", meta = (ClampMin = "0", Units = "cm"))
float SwingRadius = 25.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Harvesting")
TEnumAsByte<ECollisionChannel> SwingChannel = ECC_Visibility;
/**
* Puissance d'un coup a mains nues.
*
* Sur le composant et pas sur un DataAsset, pour la raison evidente qu'il n'y a
* pas d'objet a qui la demander -- exactement comme UnarmedUseMontage sur
* UHeldItemComponent. Elle n'a d'effet que sur les ressources dont
* RequiredTool vaut None : cueillir des baies, arracher des herbes.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Harvesting", meta = (ClampMin = "0"))
float UnarmedHarvestPower = 10.f;
/** Marge de distance accordee par le serveur. Voir le commentaire de classe. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Harvesting|Network", meta = (ClampMin = "1.0"))
float ServerRangeTolerance = 2.f;
/** Affiche le trace du coup. Le premier reflexe quand la hache ne touche rien. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Harvesting")
bool bDrawDebug = false;
private:
/** Point de vue reel du joueur, camera comprise. */
bool GetViewPoint(FVector& OutLocation, FRotator& OutRotation) const;
};