// Copyright ExByte Studios. Item Interaction Ecosystem. #include "Modules/ModuleManager.h" #include "ItemDebugLog.h" #include "ItemDatabaseSubsystem.h" #include "ItemDefinitionRow.h" #include "InventoryComponent.h" #include "ItemFeatureLibrary.h" #include "ItemCraftingLibrary.h" #include "PickupItemActor.h" #include "GameFramework/Pawn.h" #include "GameFramework/PlayerController.h" #include "Engine/World.h" #include "Engine/Engine.h" DEFINE_LOG_CATEGORY(LogItemDebug); // Implemented as the Item Debugger console toolkit (section 29). namespace ItemDebugCmd { static APawn* GetPlayerPawn(UWorld* World) { APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr; return PC ? PC->GetPawn() : nullptr; } static UInventoryComponent* GetPlayerInventory(UWorld* World) { APawn* Pawn = GetPlayerPawn(World); return Pawn ? Pawn->FindComponentByClass() : nullptr; } static void Screen(const FString& Msg, const FColor Color = FColor::Green) { UE_LOG(LogItemDebug, Log, TEXT("%s"), *Msg); if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 4.f, Color, Msg); } } static void Give(const TArray& Args, UWorld* World) { if (Args.Num() < 1) { Screen(TEXT("Usage: ItemEco.Give [Qty]"), FColor::Yellow); return; } UInventoryComponent* Inv = GetPlayerInventory(World); UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(World); if (!Inv || !DB) { Screen(TEXT("No player inventory / database."), FColor::Red); return; } const FName Id(*Args[0]); const int32 Qty = Args.Num() > 1 ? FMath::Max(1, FCString::Atoi(*Args[1])) : 1; if (!DB->HasItem(Id)) { Screen(FString::Printf(TEXT("Unknown item '%s'"), *Id.ToString()), FColor::Red); return; } FItemInstanceData Item = DB->MakeItemInstance(Id, Qty); Inv->AddItem(Item); Screen(FString::Printf(TEXT("[Inventory] gave %s x%d"), *Id.ToString(), Qty)); } static void Spawn(const TArray& Args, UWorld* World) { if (Args.Num() < 1) { Screen(TEXT("Usage: ItemEco.Spawn [Qty]"), FColor::Yellow); return; } APawn* Pawn = GetPlayerPawn(World); if (!Pawn) { Screen(TEXT("No player pawn."), FColor::Red); return; } const FName Id(*Args[0]); const int32 Qty = Args.Num() > 1 ? FMath::Max(1, FCString::Atoi(*Args[1])) : 1; const FTransform Xf(Pawn->GetActorRotation(), Pawn->GetActorLocation() + Pawn->GetActorForwardVector() * 200.f + FVector(0, 0, 50.f)); if (APickupItemActor::SpawnPickup(World, Id, Qty, Xf)) { Screen(FString::Printf(TEXT("Spawned pickup %s x%d"), *Id.ToString(), Qty)); } else { Screen(FString::Printf(TEXT("Failed to spawn '%s'"), *Id.ToString()), FColor::Red); } } static void Clear(const TArray&, UWorld* World) { UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } TArray Items; Inv->GetAllItems(Items); for (const FItemInstanceData& It : Items) { Inv->RemoveItem(It.InstanceId); } Screen(FString::Printf(TEXT("Cleared %d items."), Items.Num())); } static void Dump(const TArray&, UWorld* World) { UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } TArray Items; Inv->GetAllItems(Items); Screen(FString::Printf(TEXT("--- Inventory (%d entries) ---"), Items.Num()), FColor::Cyan); for (const FItemInstanceData& It : Items) { UE_LOG(LogItemDebug, Log, TEXT(" %s x%d [container %s slot %d]"), *It.ItemId.ToString(), It.Quantity, *It.CurrentContainerId.ToString(), It.SlotIndex); } } static void Validate(const TArray&, UWorld* World) { UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(World); if (!DB) { Screen(TEXT("No database."), FColor::Red); return; } TArray Issues; DB->ValidateData(Issues); Screen(FString::Printf(TEXT("Validation: %d issue(s)."), Issues.Num()), Issues.Num() ? FColor::Yellow : FColor::Green); for (const FItemValidationIssue& Issue : Issues) { UE_LOG(LogItemDebug, Warning, TEXT(" [%s] %s: %s"), Issue.bIsError ? TEXT("ERROR") : TEXT("WARN"), *Issue.ItemId.ToString(), *Issue.Message); } } // Mutates the first inventory item via the feature library. static void MutateFirst(UWorld* World, TFunctionRef Mutator, const TCHAR* What) { UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } TArray Items; Inv->GetAllItems(Items); if (Items.Num() == 0) { Screen(TEXT("Inventory empty."), FColor::Yellow); return; } FItemInstanceData Item = Items[0]; Mutator(Item); Inv->UpdateItem(Item); Screen(FString::Printf(TEXT("%s %s"), What, *Item.ItemId.ToString())); } static void Rot(const TArray&, UWorld* World) { UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } TArray Items; Inv->GetAllItems(Items); if (Items.Num() == 0) { Screen(TEXT("Inventory empty."), FColor::Yellow); return; } FItemInstanceData Item = Items[0]; FName TransformId; UItemFeatureLibrary::ForceRot(World, Item, TransformId); if (!TransformId.IsNone()) { UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(World); FItemInstanceData NewItem = DB ? DB->MakeItemInstance(TransformId, Item.Quantity) : FItemInstanceData(TransformId, Item.Quantity); Inv->RemoveItem(Item.InstanceId); Inv->AddItem(NewItem); Screen(FString::Printf(TEXT("Rotted -> %s"), *TransformId.ToString())); } else { Inv->UpdateItem(Item); Screen(FString::Printf(TEXT("Rotted %s"), *Item.ItemId.ToString())); } } static void Break(const TArray&, UWorld* World) { MutateFirst(World, [](FItemInstanceData& It) { UItemFeatureLibrary::BreakItem(It); }, TEXT("Broke")); } static void DrainBattery(const TArray& Args, UWorld* World) { const float Amt = Args.Num() > 0 ? FCString::Atof(*Args[0]) : 0.25f; MutateFirst(World, [Amt](FItemInstanceData& It) { UItemFeatureLibrary::DrainBattery(It, Amt, 1.f); }, TEXT("Drained battery of")); } static void AddFuel(const TArray& Args, UWorld* World) { const float Amt = Args.Num() > 0 ? FCString::Atof(*Args[0]) : 5.f; MutateFirst(World, [Amt](FItemInstanceData& It) { UItemFeatureLibrary::AddFuel(It, Amt, 9999.f); }, TEXT("Added fuel to")); } static void UnlockAll(const TArray&, UWorld* World) { UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } TArray Items; Inv->GetAllItems(Items); int32 Count = 0; for (const FItemInstanceData& It : Items) { if (Inv->ClearItemLock(It.InstanceId)) { ++Count; } } Screen(FString::Printf(TEXT("Unlocked %d items."), Count)); } static void RollLoot(const TArray& Args, UWorld* World) { if (Args.Num() < 1) { Screen(TEXT("Usage: ItemEco.RollLoot [Rolls]"), FColor::Yellow); return; } UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } const int32 Rolls = Args.Num() > 1 ? FMath::Max(1, FCString::Atoi(*Args[1])) : 5; UItemCraftingLibrary::RollLootIntoInventory(World, Inv, FName(*Args[0]), Rolls); Screen(FString::Printf(TEXT("Rolled loot '%s' x%d"), *Args[0], Rolls)); } static void Craft(const TArray& Args, UWorld* World) { if (Args.Num() < 1) { Screen(TEXT("Usage: ItemEco.Craft "), FColor::Yellow); return; } UInventoryComponent* Inv = GetPlayerInventory(World); if (!Inv) { Screen(TEXT("No player inventory."), FColor::Red); return; } const bool bOk = UItemCraftingLibrary::Craft(World, Inv, FName(*Args[0]), FGameplayTag()); Screen(FString::Printf(TEXT("Craft '%s': %s"), *Args[0], bOk ? TEXT("OK") : TEXT("FAILED")), bOk ? FColor::Green : FColor::Red); } } // --- Console command registration --- static FAutoConsoleCommandWithWorldAndArgs GCmdGive( TEXT("ItemEco.Give"), TEXT("Give [Qty] to the local player inventory."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Give)); static FAutoConsoleCommandWithWorldAndArgs GCmdSpawn( TEXT("ItemEco.Spawn"), TEXT("Spawn [Qty] as a world pickup in front of the player."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Spawn)); static FAutoConsoleCommandWithWorldAndArgs GCmdClear( TEXT("ItemEco.Clear"), TEXT("Clear the local player inventory."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Clear)); static FAutoConsoleCommandWithWorldAndArgs GCmdDump( TEXT("ItemEco.Dump"), TEXT("Dump the local player inventory to the log."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Dump)); static FAutoConsoleCommandWithWorldAndArgs GCmdValidate( TEXT("ItemEco.Validate"), TEXT("Run data validation on the item database."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Validate)); static FAutoConsoleCommandWithWorldAndArgs GCmdRot( TEXT("ItemEco.Rot"), TEXT("Force-rot the first inventory item."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Rot)); static FAutoConsoleCommandWithWorldAndArgs GCmdBreak( TEXT("ItemEco.Break"), TEXT("Break the first inventory item."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Break)); static FAutoConsoleCommandWithWorldAndArgs GCmdDrain( TEXT("ItemEco.DrainBattery"), TEXT("DrainBattery [Amount] from the first item."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::DrainBattery)); static FAutoConsoleCommandWithWorldAndArgs GCmdAddFuel( TEXT("ItemEco.AddFuel"), TEXT("AddFuel [Amount] to the first item."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::AddFuel)); static FAutoConsoleCommandWithWorldAndArgs GCmdUnlock( TEXT("ItemEco.UnlockAll"), TEXT("Clear all item locks in the player inventory."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::UnlockAll)); static FAutoConsoleCommandWithWorldAndArgs GCmdRollLoot( TEXT("ItemEco.RollLoot"), TEXT("RollLoot [Rolls] into the player inventory."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::RollLoot)); static FAutoConsoleCommandWithWorldAndArgs GCmdCraft( TEXT("ItemEco.Craft"), TEXT("Craft using the player inventory."), FConsoleCommandWithWorldAndArgsDelegate::CreateStatic(&ItemDebugCmd::Craft)); IMPLEMENT_MODULE(FDefaultModuleImpl, ItemDebug)