148 lines
4.6 KiB
C++
148 lines
4.6 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "HarvestingComponent.h"
|
|
|
|
#include "DrawDebugHelpers.h"
|
|
#include "Engine/World.h"
|
|
#include "FpsPlayer.h"
|
|
#include "GameFramework/Controller.h"
|
|
#include "GameFramework/Pawn.h"
|
|
#include "HarvestableResource.h"
|
|
#include "ItemDataAsset.h"
|
|
|
|
UHarvestingComponent::UHarvestingComponent()
|
|
{
|
|
// Aucun Tick : ce composant ne travaille qu'au moment du coup, une poignee de
|
|
// fois par minute. C'est toute la difference avec UInteractionComponent, qui
|
|
// doit entretenir un prompt a l'ecran.
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
|
|
// Pas de SetIsReplicatedByDefault : aucune RPC ne vit ici, elles sont sur le
|
|
// pawn. Voir le commentaire de classe.
|
|
}
|
|
|
|
bool UHarvestingComponent::GetViewPoint(FVector& OutLocation, FRotator& OutRotation) const
|
|
{
|
|
const APawn* OwnerPawn = Cast<APawn>(GetOwner());
|
|
if (!OwnerPawn)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Par le controller et pas par la camera : GetPlayerViewPoint renvoie
|
|
// exactement ce que le joueur a a l'ecran, secousses et balancement de tete
|
|
// compris. Le coup part donc pile du centre du viseur.
|
|
if (const AController* OwnerController = OwnerPawn->GetController())
|
|
{
|
|
OwnerController->GetPlayerViewPoint(OutLocation, OutRotation);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
AActor* UHarvestingComponent::TraceSwingTarget(FVector& OutImpactPoint, FVector& OutDirection) const
|
|
{
|
|
OutImpactPoint = FVector::ZeroVector;
|
|
OutDirection = FVector::ForwardVector;
|
|
|
|
FVector ViewLocation;
|
|
FRotator ViewRotation;
|
|
if (!GetViewPoint(ViewLocation, ViewRotation))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
OutDirection = ViewRotation.Vector();
|
|
const FVector TraceEnd = ViewLocation + OutDirection * SwingRange;
|
|
|
|
FCollisionQueryParams Params(SCENE_QUERY_STAT(HarvestSwingTrace), /*bTraceComplex=*/false, GetOwner());
|
|
|
|
FHitResult Hit;
|
|
const bool bHit = GetWorld()->SweepSingleByChannel(
|
|
Hit,
|
|
ViewLocation,
|
|
TraceEnd,
|
|
FQuat::Identity,
|
|
SwingChannel,
|
|
FCollisionShape::MakeSphere(SwingRadius),
|
|
Params);
|
|
|
|
#if ENABLE_DRAW_DEBUG
|
|
if (bDrawDebug)
|
|
{
|
|
const FColor Color = bHit ? FColor::Green : FColor::Red;
|
|
DrawDebugLine(GetWorld(), ViewLocation, TraceEnd, Color, false, 1.5f, 0, 1.f);
|
|
if (bHit)
|
|
{
|
|
DrawDebugSphere(GetWorld(), Hit.ImpactPoint, SwingRadius, 12, Color, false, 1.5f);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!bHit)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// ImpactPoint et pas Location : Location est le centre de la sphere balayee a
|
|
// l'arret, donc a SwingRadius de la surface. Les copeaux en jailliraient a
|
|
// vingt-cinq centimetres du tronc, en plein air.
|
|
OutImpactPoint = Hit.ImpactPoint;
|
|
|
|
return Hit.GetActor();
|
|
}
|
|
|
|
float UHarvestingComponent::GetSwingPower(const UItemDataAsset* Tool) const
|
|
{
|
|
// Un objet qui n'est pas un outil frappe comme un poing : tenir une pomme ne
|
|
// doit ni empecher de cueillir une baie, ni aider a abattre un arbre.
|
|
return (Tool && Tool->IsTool()) ? Tool->HarvestPower : UnarmedHarvestPower;
|
|
}
|
|
|
|
EHarvestResult UHarvestingComponent::ApplySwing(AActor* Target, const UItemDataAsset* Tool,
|
|
const FVector& ImpactPoint, const FVector& HitDirection)
|
|
{
|
|
AActor* Owner = GetOwner();
|
|
if (!Owner || !Owner->HasAuthority())
|
|
{
|
|
return EHarvestResult::Invalid;
|
|
}
|
|
|
|
AHarvestableResource* Resource = Cast<AHarvestableResource>(Target);
|
|
if (!IsValid(Resource))
|
|
{
|
|
// Le cas normal, et de loin : la plupart des coups tombent sur le sol, un
|
|
// mur, ou rien du tout. Ce n'est pas une erreur, juste un coup dans le vide.
|
|
return EHarvestResult::Invalid;
|
|
}
|
|
|
|
// Le client a vise il y a un aller-retour : entre-temps il a bouge, et la
|
|
// position que le serveur connait de lui est en retard. On revalide donc, avec
|
|
// la meme portee elargie par ServerRangeTolerance.
|
|
//
|
|
// La distance se mesure a la BOITE ENGLOBANTE de la cible et pas a son origine :
|
|
// le pivot d'un arbre de pack est au SOL, donc frapper le tronc a deux metres de
|
|
// haut mesurerait une distance bien plus grande que la portee du coup. Le
|
|
// symptome serait un grand arbre impossible a abattre pour un client, et
|
|
// parfaitement normal chez l'hote.
|
|
const FBox TargetBounds = Resource->GetComponentsBoundingBox(/*bNonColliding=*/true);
|
|
const float MaxDistanceSq = FMath::Square(SwingRange * ServerRangeTolerance);
|
|
|
|
if (TargetBounds.ComputeSquaredDistanceToPoint(Owner->GetActorLocation()) > MaxDistanceSq)
|
|
{
|
|
UE_LOG(LogTemp, Verbose, TEXT("UHarvestingComponent : %s a frappe %s, hors de portee cote serveur."),
|
|
*GetNameSafe(Owner), *GetNameSafe(Resource));
|
|
return EHarvestResult::Invalid;
|
|
}
|
|
|
|
AFpsPlayer* Harvester = Cast<AFpsPlayer>(Owner);
|
|
if (!Harvester)
|
|
{
|
|
return EHarvestResult::Invalid;
|
|
}
|
|
|
|
return Resource->TryHarvest(Harvester, Tool, GetSwingPower(Tool), ImpactPoint, HitDirection);
|
|
}
|