// Copyright ExByte Studios. Item Interaction Ecosystem. #pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "ItemPlacementRules.h" #include "PlacementPreviewComponent.generated.h" class UStaticMeshComponent; struct FItemDefinitionRow; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlacementStateChanged, bool, bIsValid); /** * Drives the local ghost-mesh preview and the server-validated confirm for * placeable items (section 13). The client shows a green/red ghost; the server * re-runs every rule before committing (collision, slope, distance, surface). */ UCLASS(ClassGroup = (Placement), meta = (BlueprintSpawnableComponent)) class ITEMPLACEMENT_API UPlacementPreviewComponent : public UActorComponent { GENERATED_BODY() public: UPlacementPreviewComponent(); virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; UPROPERTY(BlueprintReadOnly, Category = "Placement") bool bIsPlacing = false; UPROPERTY(BlueprintReadOnly, Category = "Placement") FName ItemId; UPROPERTY(BlueprintReadOnly, Category = "Placement") FGuid ItemInstanceId; UPROPERTY(BlueprintReadOnly, Category = "Placement") FTransform PreviewTransform = FTransform::Identity; UPROPERTY(BlueprintReadOnly, Category = "Placement") bool bCurrentValid = false; UPROPERTY(BlueprintAssignable, Category = "Placement") FOnPlacementStateChanged OnPlacementStateChanged; /** Client: begin placing an inventory item. */ UFUNCTION(BlueprintCallable, Category = "Placement") void StartPlacement(FGuid InstanceId); /** Client: confirm at the current preview transform (sends a server request). */ UFUNCTION(BlueprintCallable, Category = "Placement") void ConfirmPlacement(); /** Client: abort placement. */ UFUNCTION(BlueprintCallable, Category = "Placement") void CancelPlacement(); UFUNCTION(BlueprintCallable, Category = "Placement") void RotatePreview(float DeltaYawDegrees); /** Pure rule check used by both the client preview and the server (section 13.3). */ static bool ValidatePlacement(const UObject* WorldContext, const FItemDefinitionRow& Def, const FTransform& Transform, AActor* Instigator, FString& OutReason); protected: UFUNCTION(Server, Reliable) void Server_ConfirmPlacement(FGuid InstanceId, FTransform Transform); private: UPROPERTY(Transient) TObjectPtr Ghost = nullptr; float PreviewYaw = 0.f; bool ComputePreviewTransform(FTransform& OutTransform) const; void DestroyGhost(); void SetValidity(bool bNewValid); bool GetViewPoint(FVector& OutLoc, FRotator& OutRot) const; };