
UCLASS(BlueprintType)
class PROJECTSANDWALKER_API UInventoryItem : public UDataAsset
{
GENERATED_BODY()
//
// Properties
//
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
FString ItemName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
FString ItemDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
UTexture2D* ItemIcon;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties",
meta=(Bitmask, BitmaskEnum = "EInventoryItemType"))
int32 InventoryItemType;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
FIntPoint Dimension;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
FIntPoint ItemPivot;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
TArray<FIntPoint> BlocksOffsetFromPivot;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
TArray<FExtraData> ExtraData;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
TSubclassOf<AWorldItem> WorldItemClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
bool bIsRecipe;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
UTexture2D* CraftedItemIcon;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
TArray<FCraftingMaterialEntry> Material;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
UInventoryItem* CraftedItem;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties")
bool bIsTool;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory Item | Properties", meta = (EditCondition = "bIsTool"))
TSubclassOf<AToolBase> ToolClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true), Category="Inventory Item | Properties")
FVector2D IconSize;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true), Category="Inventory Item | Properties")
FVector2D IconPosition;
#pragma region EDITOR
#if WITH_EDITORONLY_DATA
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true), Category="Inventory Item | Editor")
FIntPoint TopLeft;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true), Category="Inventory Item | Editor")
FIntPoint BottomRight;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true), Category="Inventory Item | Editor")
UBlueprint* BlueprintAsset;
#endif
#if WITH_EDITOR
UFUNCTION(BlueprintCallable)
void SetupInstance(const FString& Name, const FString& Description, UTexture2D* Icon, const FVector2D& Size,
const FVector2D& Position, const FIntPoint& Pivot, const TArray<FIntPoint>& Blocks,
const int32& Type, const TArray<FCraftingMaterialEntry>& CraftingIngredients);
UFUNCTION(BlueprintCallable)
void SetDimension(FIntPoint NewTopLeft, FIntPoint NewBottomRight);
#endif
#pragma endregion
};UCLASS(Blueprintable, BlueprintType)
class PROJECTSANDWALKER_API AWorldItem : public AActor
{
GENERATED_BODY()
public:
AWorldItem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory|Item")
UInventoryItem* InventoryItem;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventory|Item")
FGuid Guid;
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
};// Sets default values
ASteamPunkShip::ASteamPunkShip()
{
BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
SetRootComponent(BoxComp);
Generator = CreateDefaultSubobject<UChildActorComponent>("Generator");
Generator->SetupAttachment(RootComponent);
CameraSpringArm = CreateDefaultSubobject<USpringArmComponent>("Spring Arm");
CameraSpringArm->SetupAttachment(RootComponent);
ShipCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
ShipCamera->SetupAttachment(CameraSpringArm, USpringArmComponent::SocketName);
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SetActorRotation(GetActorRotation() + FRotator(0, 90, 0));
}
void ASteamPunkShip::SetAdditionalVelocity(float InAdditionalVelocity)
{
AdditionalVelocity = InAdditionalVelocity;
}
void ASteamPunkShip::AddAdditionalVelocity(float InAdditionalVelocity)
{
AdditionalVelocity += InAdditionalVelocity;
}
float ASteamPunkShip::GetAdditionalVelocity()
{
return AdditionalVelocity;
}
void ASteamPunkShip::SetAdditionalVector(FVector& InAdditionalVector)
{
AdditionalVector = InAdditionalVector;
}
void ASteamPunkShip::ResetAdditionalVector()
{
AdditionalVector = FVector(0, 0, 0);
}
void ASteamPunkShip::ShipTakeDamage(float Amount, FVector ImpulseDirection)
{
OnShipDamagedDelegate.Broadcast();
ImpulseForce = ImpulseDirection * DamageImpactForce;
float NewAmount = Health - Amount;
Health = FMath::Max(NewAmount, 0);
if (Health == 0)
{
IsFunctional = false;
OnStateChangedDelegate.Broadcast(IsFunctional);
}
}
void ASteamPunkShip::SetHealth(float Amount)
{
Health = FMath::Min(Amount, MaxHealth);
if (Health > 0)
{
IsFunctional = true;
OnStateChangedDelegate.Broadcast(IsFunctional);
}
}
float ASteamPunkShip::GetHealth()
{
return Health;
}void ASteamPunkShip::Move_Implementation(const FVector& Value)
{
if (Fuel <= 0 || Accelaration == 0 || !IsFunctional)
{
SteerDirection = 0;
ForwardAccel = 0;
IsAccelerating = false;
return;
}
if (Controller != nullptr)
{
SteerDirection = Value.X;
ForwardAccel = Value.Y;
if (Value.Y != 0)
{
IsAccelerating = true;
}
if (Value.X != 0)
{
IsAccelerating = true;
}
}
}
void ASteamPunkShip::MoveStop_Implementation()
{
IsAccelerating = false;
ForwardAccel = 0;
SteerDirection = 0;
}
void ASteamPunkShip::Look_Implementation(const FVector& Value)
{
if (Controller != nullptr)
{
// add yaw and pitch input to controller
AddControllerYawInput(Value.X * RotationMult);
AddControllerRollInput(Value.Y * RotationMult);
}
}
void ASteamPunkShip::JumpTrigger_Implementation()
{
if (Fuel <= 0 || Accelaration == 0 || !IsFunctional)
{
AccelUp = false;
IsAccelerating = false;
return;
}
AccelUp = true;
IsAccelerating = true;
}
void ASteamPunkShip::JumpStop_Implementation()
{
AccelUp = false;
IsAccelerating = false;
}
void ASteamPunkShip::Crouch_Implementation()
{
if (Accelaration == 0 || !IsFunctional)
{
return;
}
AccelDown = true;
}
void ASteamPunkShip::CrouchStop_Implementation()
{
AccelDown = false;
}
void ASteamPunkShip::Interact_Implementation()
{
if (AController* DriverController = GetController())
{
Driver->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
Driver->SetActorHiddenInGame(false);
Driver->SetActorLocation(CameraSpringArm->GetComponentLocation());
DriverController->Possess(Driver);
Driver = nullptr;
}
UE_LOG(LogTemp, Display, TEXT("Interact"));
}void ASteamPunkShip::UpdateImpulse(const float& DeltaTime)
{
if (ImpulseForce.Size() <= 0)
return;
SetActorLocation(GetActorLocation() + (ImpulseForce * DeltaTime), true);
FVector NewForce = (ImpulseForce - (ImpulseForce.GetSafeNormal() * ImpulseReduceRate * DeltaTime));
if (NewForce.Dot(ImpulseForce) < 0)
ImpulseForce = FVector(0, 0, 0);
else
ImpulseForce = NewForce;
}
void ASteamPunkShip::UpdateVerticalMovement(const float& DeltaTime)
{
if (AccelUp)
VerticalVelocity = FMath::Min(VerticalVelocity + (UpwardAccelaration * DeltaTime), MaxUpwardVelocity);
if (AccelDown)
VerticalVelocity = FMath::Max(VerticalVelocity + (DownwardAccelaration * DeltaTime), MaxDownwardVelocity);
if (VerticalVelocity == 0)
return;
if (VerticalVelocity > 0 && GetActorLocation().Z > HeightLimit)
{
VerticalVelocity = FMath::Max(VerticalVelocity - SuppressionForce * DeltaTime, 0);
}
SetActorLocation(GetActorLocation() + (GetActorUpVector() * VerticalVelocity * DeltaTime), true);
if (VerticalVelocity > 0)
{
VerticalVelocity = (VerticalVelocity + DownwardDrag * DeltaTime < 0)
? 0
: VerticalVelocity + DownwardDrag * DeltaTime;
}
else
{
VerticalVelocity = (VerticalVelocity + UpwardDrag * DeltaTime > 0)
? 0
: VerticalVelocity + UpwardDrag * DeltaTime;
}
}
void ASteamPunkShip::UpdateSteering(const float& DeltaTime)
{
if (SteerDirection != 0)
SteeringSpeed += DeltaTime * SteeringPower * SteerDirection;
if (SteeringSpeed == 0)
return;
if (SteeringSpeed > 0)
{
SteeringSpeed = (SteeringSpeed - SteeringDrag * DeltaTime <= 0)
? 0
: SteeringSpeed - SteeringDrag * DeltaTime;
}
else
{
SteeringSpeed = (SteeringSpeed + SteeringDrag * DeltaTime >= 0)
? 0
: SteeringSpeed + SteeringDrag * DeltaTime;
}
SetActorRotation(GetActorRotation() + FRotator(0, SteeringSpeed * DeltaTime, 0));
}
void ASteamPunkShip::UpdateMovement(const float& DeltaTime)
{
if (ForwardAccel != 0)
{
if (ForwardAccel < 0)
{
if (Velocity > 0)
{
Velocity -= Accelaration * DeltaTime;
}
Velocity = FMath::Max(0, Velocity);
}
else if (Velocity < MaxVelocity)
{
Velocity += Accelaration * DeltaTime ;
}
}
FVector Movement = FVector::ZeroVector;
if (Velocity == 0)
return;
Movement += GetActorRightVector() * Velocity * DeltaTime;
Movement += AdditionalVector * AdditionalVelocity * DeltaTime;
SetActorLocation(GetActorLocation() + Movement, true);
Velocity = (Velocity + Drag * DeltaTime <= 0) ? 0 : Velocity + Drag * DeltaTime;
}
// Called every frame
void ASteamPunkShip::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
UpdateSteering(DeltaTime);
UpdateVerticalMovement(DeltaTime);
UpdateMovement(DeltaTime);
UpdateImpulse(DeltaTime);
if (PotentialFuel <= 0)
return;
if (IsAccelerating)
{
Fuel -= DeltaTime;
//Fuel = FMath::Max(Fuel, 0);
PotentialFuel -= DeltaTime;
PotentialFuel = FMath::Max(PotentialFuel, 0);
for (int i = 0; i < PropellerRibbonNiagaraSystems.Num(); i++)
{
if (!PropellerRibbonNiagaraSystems[i]->IsActive())
{
PropellerRibbonNiagaraSystems[i]->Activate();
}
}
}
else
{
for (int i = 0; i < PropellerRibbonNiagaraSystems.Num(); i++)
{
if (PropellerRibbonNiagaraSystems[i]->IsActive())
{
PropellerRibbonNiagaraSystems[i]->Deactivate();
}
}
}
if (Fuel <= 0)
{
if (UInventoryComponent* Inventory = Generator->GetChildActor()->GetComponentByClass<UInventoryComponent>())
{
TArray<FInventoryItemPosition> Items = Inventory->GetAllItems();
FInventoryItemPosition* ItemPos = &Items[0];
if (!ItemPos)
{
return;
}
if (ItemPos->Item->InventoryItemType & static_cast<int>(EInventoryItemType::Burnable))
{
for (auto& ExtraData : ItemPos->Item->ExtraData)
{
if (ExtraData.Type != 6)
{
continue;
}
Inventory->TryRemoveItem(ItemPos->Item);
if (!Inventory->GetAllItems().IsEmpty())
Fuel = ExtraData.Value + Fuel;
if (UInventoryManager* InventoryManager = Inventory->InventoryManager)
{
InventoryManager->UpdateProgressBar(Inventory, PotentialFuel / MaxFuel);
}
break;
}
}
Inventory->RefreshUIElements();
}
}
}UINTERFACE(MinimalAPI, Blueprintable)
class UMountableBase : public UInterface
{
GENERATED_BODY()
};
class PROJECTSANDWALKER_API IMountableBase
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
IMountableBase();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void PrimaryAction();
virtual void PrimaryAction_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void PrimaryActionEnd();
virtual void PrimaryActionEnd_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void SecondaryAction();
virtual void SecondaryAction_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void Move(const FVector& Value);
virtual void Move_Implementation(const FVector& Value);
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void MoveStop();
virtual void MoveStop_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void Look(const FVector& Value);
virtual void Look_Implementation(const FVector& Value);
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void RunStart();
virtual void RunStart_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void RunStop();
virtual void RunStop_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void JumpTrigger();
virtual void JumpTrigger_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void JumpStart();
virtual void JumpStart_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void JumpStop();
virtual void JumpStop_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void Crouch();
virtual void Crouch_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void CrouchStop();
virtual void CrouchStop_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void Interact();
virtual void Interact_Implementation();
UFUNCTION(BlueprintNativeEvent, Category = "Input")
void ToggleInventory();
virtual void ToggleInventory_Implementation();
};void AMainController::BeginPlay()
{
Super::BeginPlay();
//Add Input Mapping Context
if (AMainController* PlayerController = Cast<AMainController>(this))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<
UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(PlayerMappingContext, 0);
}
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent))
{
// Actions
EnhancedInputComponent->BindAction(PrimaryActionAction, ETriggerEvent::Triggered, this,
&AMainController::PrimaryAction);
EnhancedInputComponent->BindAction(PrimaryActionAction, ETriggerEvent::Completed, this,
&AMainController::PrimaryActionEnd);
EnhancedInputComponent->BindAction(SecondaryActionAction, ETriggerEvent::Triggered, this,
&AMainController::SecondaryAction);
//Movement
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMainController::Move);
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Completed, this, &AMainController::MoveStop);
//Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMainController::Look);
//Running
EnhancedInputComponent->BindAction(RunningAction, ETriggerEvent::Started, this, &AMainController::RunStart);
EnhancedInputComponent->BindAction(RunningAction, ETriggerEvent::Completed, this,
&AMainController::RunStop);
//Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this,
&AMainController::JumpTrigger);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AMainController::JumpStart);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &AMainController::JumpStop);
//Crouch
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Triggered, this, &AMainController::Crouch);
EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &AMainController::CrouchStop);
//Interact
EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this,
&AMainController::Interact);
//Inventory
EnhancedInputComponent->BindAction(InventoryAction, ETriggerEvent::Started, this,
&AMainController::ToggleInventory);
}
}
}
void AMainController::OnPossess(APawn* inPawn)
{
Super::OnPossess(inPawn);
if (!inPawn->GetClass()->ImplementsInterface(UMountableBase::StaticClass()))
{
bImplementsInterface = false;
return;
}
bImplementsInterface = true;
}
void AMainController::PrimaryAction()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_PrimaryAction(GetPawn());
}
void AMainController::PrimaryActionEnd()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_PrimaryActionEnd(GetPawn());
}
void AMainController::SecondaryAction()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_SecondaryAction(GetPawn());
}
void AMainController::Move(const FInputActionValue& Value)
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_Move(GetPawn(), Value.Get<FVector>());
}
void AMainController::MoveStop()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_MoveStop(GetPawn());
}
void AMainController::Look(const FInputActionValue& Value)
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_Look(GetPawn(), Value.Get<FVector>());
}
void AMainController::RunStart()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_RunStart(GetPawn());
}
void AMainController::RunStop()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_RunStop(GetPawn());
}
void AMainController::JumpTrigger()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_JumpTrigger(GetPawn());
}
void AMainController::JumpStart()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_JumpStart(GetPawn());
}
void AMainController::JumpStop()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_JumpStop(GetPawn());
}
void AMainController::Crouch()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_Crouch(GetPawn());
}
void AMainController::CrouchStop()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_CrouchStop(GetPawn());
}
void AMainController::Interact()
{
if (!bImplementsInterface)
{
return;
}
IMountableBase::Execute_Interact(GetPawn());
}
void AMainController::ToggleInventory()
{
if (!bImplementsInterface || bIsDead)
{
return;
}
IMountableBase::Execute_ToggleInventory(GetPawn());
}
void AMainController::SetIsDead(bool b)
{
bIsDead = b;
}
void UItemWidget::NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
Super::NativeOnMouseEnter(InGeometry, InMouseEvent);
for (UBorder* BorderWidget : BorderWidgets)
{
if (BorderWidget)
{
BorderWidget->SetBrushColor(BackgroundHoverColor);
}
}
OnMouseEnterDelegate.Execute(this, InventoryItemPosition.Item);
}
void UItemWidget::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
{
Super::NativeOnMouseLeave(InMouseEvent);
for (UBorder* BorderWidget : BorderWidgets)
{
if (BorderWidget)
{
BorderWidget->SetBrushColor(BackgroundColor);
}
}
OnMouseLeaveDelegate.Execute();
}
void UItemWidget::NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent,
UDragDropOperation*& OutOperation)
{
if (!bCanRemoveItem)
{
Debug::LogWarning(TEXT("Item is not droppable, cannot start drag operation."), true, 2);
// Ensure focus is cleared so the grid doesn't keep keyboard focus after a blocked drag
UWidgetBlueprintLibrary::SetFocusToGameViewport();
return;
}
for (UBorder* BorderWidget : BorderWidgets)
{
if (BorderWidget)
{
BorderWidget->SetBrushColor(BackgroundPressedColor);
}
}
UDragDropOperation* DragOperation = NewObject<UDragDropOperation>();
// Create drag info object
UDragInfo* DragInfo = NewObject<UDragInfo>();
DragInfo->ItemGridPosition = InventoryItemPosition;
// --- Calculate grab offset ---
// Get mouse position relative to this widget
FVector2D LocalMousePos = InGeometry.AbsoluteToLocal(InMouseEvent.GetScreenSpacePosition());
int32 LocalTileX = FMath::FloorToInt(LocalMousePos.X / TileSize);
int32 LocalTileY = FMath::FloorToInt(LocalMousePos.Y / TileSize);
DragInfo->GrabOffset = FIntPoint(LocalTileX, LocalTileY);
// Start the drag with the current rotation of this instance
DragInfo->Rotation = InventoryItemPosition.Rotation;
// Set drag operation properties
DragOperation->Payload = DragInfo;
DragOperation->DefaultDragVisual = this;
DragOperation->Pivot = EDragPivot::MouseDown;
OutOperation = DragOperation;
Super::NativeOnDragDetected(InGeometry, InMouseEvent, OutOperation);
OnItemDragDetectedDelegate.Execute(this, InventoryItemPosition);
}
FReply UItemWidget::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
if (InMouseEvent.IsShiftDown())
{
OnMouseButtonUpDelegate.Execute(this, InventoryItemPosition, true);
return FReply::Handled();
}
OnMouseButtonUpDelegate.Execute(this, InventoryItemPosition, false);
return FReply::Handled();
}
FReply UItemWidget::NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
OnMouseButtonDoubleClickDelegate.Execute(this, InventoryItemPosition);
return FReply::Handled();
}// This function is called when the drag operation is detected to be stopped outside the widget
bool UInventoryGridWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent,
UDragDropOperation* InOperation)
{
if (InOperation->Payload)
{
UDragInfo* DragData = Cast<UDragInfo>(InOperation->Payload);
if (!DragData)
{
return false;
}
UInventoryItem* DraggedItem = DragData->ItemGridPosition.Item;
FInventoryItemPosition ItemPosition;
ItemPosition.Item = DraggedItem;
ItemPosition.Position = DraggedItemTopLeftTile;
// Ensure rotation is reduced/consistent
ItemPosition.Rotation = InventoryRotationUtils::ReduceRotationForItem(DraggedItem, DragData->Rotation);
Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
OnItemDropped.Execute(this, ItemPosition);
bDrawDropLocation = false;
// Remove keyboard focus so TAB can close inventory again
UWidgetBlueprintLibrary::SetFocusToGameViewport();
return true;
}
return false;
}bool UInventoryWidget::NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent,
UDragDropOperation* InOperation)
{
if (InOperation->Payload)
{
UDragInfo* DragData = Cast<UDragInfo>(InOperation->Payload);
if (!DragData)
{
return false;
}
UInventoryItem* DraggedItem = DragData->ItemGridPosition.Item;
Super::NativeOnDrop(InGeometry, InDragDropEvent, InOperation);
OnItemDropped.Execute(this, DraggedItem);
return true;
}
return false;
}void UInventoryManager::BeginPlay()
{
// Set player reference
Player = GetOwner<AMainCharacterClass>();
// Set player controller reference
if (AController* Controller = Player->GetController())
{
//This might end up with nullptr even if there is a controller. and this might never happend if a player doesn't have a controller
PlayerController = Cast<APlayerController>(Controller);
}
//Create inventory widget
if (!InventoryWidgetClass)
{
Debug::LogWarning("InventoryWidgetClass is not set in InventoryManager. Please set it in the editor or code.",
true, 2);
return;
}
if (!PlayerController)
{
Debug::LogWarning("Tried to create inventory widget while not having a valid player controller line 89 on Inventory manager. cpp",
true, 5);
return;
}
InventoryWidget = CreateWidget<UInventoryWidget>(PlayerController, InventoryWidgetClass);
InventoryWidget->OnItemDropped.BindDynamic(this, &UInventoryManager::OnItemDroppedFromInventory);
InventoryWidget->SetVisibility(ESlateVisibility::Hidden);
InventoryWidget->AddToViewport();
}
void UInventoryManager::OpenComplimentaryInventory(UInventoryComponent* ComplimentaryInventory)
{
if (!ComplimentaryInventory)
{
UE_LOG(LogTemp, Warning, TEXT("Invalid inventory component for opening complimentary inventory."));
return;
}
ComplimentaryInventory->InventoryManager = this;
if (InventoryWidget->IsVisible())
{
// if we only have 1 inventory, we should still open the complimentary inventory
if (LinkageList.Num() == 1)
{
AddInventoryGridWidget(ComplimentaryInventory);
return;
}
ToggleInventory();
return;
}
ToggleInventory();
AddInventoryGridWidget(ComplimentaryInventory);
}
UInventoryGridWidget* UInventoryManager::AddInventoryGridWidget(UInventoryComponent* InventoryComponent)
{
if (!InventoryGridWidgetClass || !InventoryWidget)
{
Debug::LogWarning("InventoryGridWidgetClass or InventoryWidget is not set in InventoryManager. Please check your setup.",true, 2);
return nullptr;
}
if (!InventoryComponent)
{
Debug::LogWarning("InventoryComponent is null. Cannot add inventory grid.", true, 2);
return nullptr;
}
// Create the grid widget
UInventoryGridWidget* GridWidget = CreateWidget<UInventoryGridWidget>(InventoryWidget, InventoryGridWidgetClass);
if (!GridWidget)
{
Debug::LogWarning("Failed to create InventoryGridWidget. Please check your InventoryGridWidgetClass.", true, 2);
return nullptr;
}
GridWidget->OnItemDropped.BindDynamic(this, &UInventoryManager::OnItemDroppedFromGrid);
GridWidget->TileSize = InventoryComponent->TileSize;
// Find the linkage list entry for the component, if none, make a new one
FInventoryWidgetLink* LinkEntry = GetOrCreateLinkForComponent(InventoryComponent);
LinkEntry->SetGridWidget(GridWidget);
// Add to our array
InventoryWidget->GridWidgets.Add(GridWidget);
ConfigureGridWidget(InventoryComponent, GridWidget);
RepositionAllGrids();
RefreshItemWidgets(InventoryComponent);
Player->OnInventoryOpened.Broadcast(InventoryComponent);
return GridWidget;
}
void UInventoryManager::RefreshItemWidgets(UInventoryComponent* InventoryComponent)
{
if (!InventoryComponent)
{
Debug::LogWarning("RefreshInventoryWidgets called with null InventoryComponent", true, 2);
return;
}
if (!InventoryWidget)
{
Debug::LogWarning("InventoryWidget is not set in InventoryManager. Please check your setup.", true, 2);
return;
}
// Find the grid widget associated with this inventory component
FInventoryWidgetLink* LinkEntry = GetOrCreateLinkForComponent(InventoryComponent);
UInventoryGridWidget* GridWidget = LinkEntry ? LinkEntry->GridWidget : nullptr;
if (!GridWidget)
{
//Debug::LogWarning("No grid widget found for the provided inventory component: " + LinkEntry->InventoryComponent->InventoryName, true, 2);
return;
}
ClearItemWidgets(GridWidget);
TArray<UItemWidget*> UpdatedWidgets;
for (const auto& Pair : InventoryComponent->GetAllItems())
{
FItemGridPosition ItemPos;
ItemPos.Item = Pair.Item;
ItemPos.Position = Pair.Position;
FInventoryItemPosition ItemPosition = { ItemPos.Item, ItemPos.Position };
{
const uint8 StoredRot = InventoryComponent->GetRotationAt(ItemPos.Position);
ItemPosition.Rotation = InventoryRotationUtils::ReduceRotationForItem(ItemPos.Item, StoredRot);
}
const FIntPoint MinOff = InventoryRotationUtils::GetMinRotatedOffset(ItemPos.Item, ItemPosition.Rotation);
const FVector2D TopLeft(
(ItemPos.Position.X + MinOff.X) * InventoryComponent->TileSize,
(ItemPos.Position.Y + MinOff.Y) * InventoryComponent->TileSize
);
const bool bIsRecipe = InventoryComponent->IsCraftingStation();
UItemWidget* TypedItemWidget = CreateItemWidget(GridWidget, ItemPosition, bIsRecipe, InventoryComponent->TileSize);
if (TypedItemWidget)
{
if (UPanelSlot* NewPanelSlot = GridWidget->GetGridCanvasPanel()->AddChild(TypedItemWidget))
{
if (UCanvasPanelSlot* CanvasSlot = Cast<UCanvasPanelSlot>(NewPanelSlot))
{
// Size explicitly based on rotated dimensions so layout/hit-test match the visual
const int32 Dx = FMath::Max(1, ItemPos.Item->Dimension.X);
const int32 Dy = FMath::Max(1, ItemPos.Item->Dimension.Y);
const bool bOdd = (ItemPosition.Rotation % 2) == 1;
const FVector2D WidgetSize(
(bOdd ? Dy : Dx) * InventoryComponent->TileSize,
(bOdd ? Dx : Dy) * InventoryComponent->TileSize
);
CanvasSlot->SetAutoSize(false);
CanvasSlot->SetSize(WidgetSize);
CanvasSlot->SetPosition(TopLeft);
CanvasSlot->SetZOrder(10);
}
}
UpdatedWidgets.Add(TypedItemWidget);
}
}
// Save widgets
LinkEntry = GetOrCreateLinkForComponent(InventoryComponent);
if (LinkEntry)
{
LinkEntry->SetItemWidgets(UpdatedWidgets);
}
}
UItemWidget* UInventoryManager::CreateItemWidget(UInventoryGridWidget* GridWidget, FInventoryItemPosition ItemPosition,
bool bIsRecipe, float TileSize)
{
UItemWidget* TypedItemWidget = CreateWidget<UItemWidget>(GridWidget, ItemWidgetClass);
if (!TypedItemWidget)
{
return nullptr;
}
UInventoryComponent* InventoryComponent = GetComponentWithGridWidget(GridWidget);
TypedItemWidget->bIsRecipe = bIsRecipe;
TypedItemWidget->bCanRemoveItem = InventoryComponent->bCanRemoveItems;
// Uncomment this to disable dragging the item widget
//TypedItemWidget->bIsDroppable = InventoryComponent->bCanRemoveItems;
TypedItemWidget->Setup(&ItemPosition, TileSize, InventoryComponent);
TypedItemWidget->OnMouseEnterDelegate.BindDynamic(this, &UInventoryManager::OnItemEntered);
TypedItemWidget->OnMouseLeaveDelegate.BindDynamic(this, &UInventoryManager::OnItemExited);
TypedItemWidget->OnItemDragDetectedDelegate.BindDynamic(this, &UInventoryManager::OnItemDragDetected);
TypedItemWidget->OnMouseButtonUpDelegate.BindDynamic(this, &UInventoryManager::OnItemClicked);
TypedItemWidget->OnMouseButtonDoubleClickDelegate.BindDynamic(this, &UInventoryManager::OnItemDoubleClicked);
return TypedItemWidget;
}
void UInventoryManager::OnItemEntered(UItemWidget* ItemWidget, UInventoryItem* Item)
{
// Process for when the cusor enter item widget area
}
void UInventoryManager::OnItemExited()
{
// Process for when the cursor exit item widget area
}
void UInventoryManager::OnItemDragDetected(UItemWidget* ItemWidget, FInventoryItemPosition InventoryItemPosition)
{
Process for when an drag operation is detected
}
void UInventoryManager::OnItemClicked(UItemWidget* ItemWidget, FInventoryItemPosition InventoryItemPosition,
bool bIsShiftClicked)
{
// Process for when an item widget is clicked
}
void UInventoryManager::OnItemDoubleClicked(UItemWidget* ItemWidget, FInventoryItemPosition InventoryItemPosition)
{
// Process for when an item widget is double clicked
}
void UInventoryManager::OnItemDroppedFromGrid(UInventoryGridWidget* GridWidget,
FInventoryItemPosition InventoryItemPosition)
{
// Process for when drag operation ended with the dragged item being dropped on a grid widget
}
void UInventoryManager::OnItemDroppedFromInventory(UInventoryWidget* InInventoryWidget, UInventoryItem* InventoryItem)
{
// Process for when drag operation ended with the dragged item being dropped on the inventory widget
}