// 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 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; };