Initial commit: Item Interaction Ecosystem plugin (UE5.7)
Server-authoritative, data-driven item/inventory/interaction/carry/equipment/ world-slot/placement/crafting framework. 8 modules, compiles against UE 5.7. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
165
Source/ItemInventory/Private/ItemFeatureLibrary.cpp
Normal file
165
Source/ItemInventory/Private/ItemFeatureLibrary.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright ExByte Studios. Item Interaction Ecosystem.
|
||||
|
||||
#include "ItemFeatureLibrary.h"
|
||||
#include "ItemInventoryLog.h"
|
||||
#include "ItemNativeTags.h"
|
||||
#include "ItemDatabaseSubsystem.h"
|
||||
#include "ItemAuxiliaryRows.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
float GetWorldSeconds(const UObject* WorldContext)
|
||||
{
|
||||
const UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContext, EGetWorldErrorMode::ReturnNull) : nullptr;
|
||||
return World ? World->GetTimeSeconds() : 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
bool UItemFeatureLibrary::UpdateLazyFeatures(const UObject* WorldContext, FItemInstanceData& Item, FName& OutTransformItemId)
|
||||
{
|
||||
OutTransformItemId = NAME_None;
|
||||
|
||||
const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
|
||||
if (!DB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FItemFeatureRow> RottingRows;
|
||||
DB->GetFeatureRows(Item.ItemId, ItemEcosystemTags::Feature_Rotting, RottingRows);
|
||||
if (RottingRows.Num() == 0)
|
||||
{
|
||||
return false; // not perishable
|
||||
}
|
||||
|
||||
float DecayPerHour = 0.f;
|
||||
FName RottenItemId = NAME_None;
|
||||
for (const FItemFeatureRow& Row : RottingRows)
|
||||
{
|
||||
if (Row.Param == FName("DecayRatePerHour")) { DecayPerHour = Row.GetFloat(); }
|
||||
else if (Row.Param == FName("RottenItemId")) { RottenItemId = FName(*Row.Value); }
|
||||
}
|
||||
if (DecayPerHour <= 0.f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const float Now = GetWorldSeconds(WorldContext);
|
||||
const float Last = Item.RuntimeProperties.GetFloat(ItemEcosystemTags::Property_LastUpdateTime, Now);
|
||||
float Freshness = Item.RuntimeProperties.GetFloat(ItemEcosystemTags::Property_Freshness, 1.f);
|
||||
|
||||
const float ElapsedHours = FMath::Max(0.f, Now - Last) / 3600.f;
|
||||
Freshness = FMath::Clamp(Freshness - DecayPerHour * ElapsedHours, 0.f, 1.f);
|
||||
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_Freshness, Freshness);
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_LastUpdateTime, Now);
|
||||
|
||||
if (Freshness <= 0.f)
|
||||
{
|
||||
Item.StateTags.AddTag(ItemEcosystemTags::State_Rotten);
|
||||
if (!RottenItemId.IsNone())
|
||||
{
|
||||
OutTransformItemId = RottenItemId;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UItemFeatureLibrary::UpdateInventoryFeatures(const UObject* WorldContext, UInventoryComponent* Inventory)
|
||||
{
|
||||
if (!Inventory || !Inventory->GetOwner() || !Inventory->GetOwner()->HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FItemInstanceData> Items;
|
||||
Inventory->GetAllItems(Items);
|
||||
for (FItemInstanceData& Item : Items)
|
||||
{
|
||||
FName TransformId;
|
||||
if (!UpdateLazyFeatures(WorldContext, Item, TransformId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!TransformId.IsNone())
|
||||
{
|
||||
// Transform into the rotten variant, preserving quantity.
|
||||
const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext);
|
||||
FItemInstanceData NewItem = DB ? DB->MakeItemInstance(TransformId, Item.Quantity) : FItemInstanceData(TransformId, Item.Quantity);
|
||||
Inventory->RemoveItem(Item.InstanceId);
|
||||
Inventory->AddItem(NewItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory->UpdateItem(Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UItemFeatureLibrary::ForceRot(const UObject* WorldContext, FItemInstanceData& Item, FName& OutTransformItemId)
|
||||
{
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_Freshness, 0.f);
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_LastUpdateTime, GetWorldSeconds(WorldContext));
|
||||
Item.StateTags.AddTag(ItemEcosystemTags::State_Rotten);
|
||||
|
||||
OutTransformItemId = NAME_None;
|
||||
if (const UItemDatabaseSubsystem* DB = UItemDatabaseSubsystem::Get(WorldContext))
|
||||
{
|
||||
TArray<FItemFeatureRow> Rows;
|
||||
DB->GetFeatureRows(Item.ItemId, ItemEcosystemTags::Feature_Rotting, Rows);
|
||||
for (const FItemFeatureRow& Row : Rows)
|
||||
{
|
||||
if (Row.Param == FName("RottenItemId") && !Row.Value.IsEmpty())
|
||||
{
|
||||
OutTransformItemId = FName(*Row.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UItemFeatureLibrary::BreakItem(FItemInstanceData& Item)
|
||||
{
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_Durability, 0.f);
|
||||
Item.StateTags.AddTag(ItemEcosystemTags::State_Broken);
|
||||
}
|
||||
|
||||
void UItemFeatureLibrary::DrainBattery(FItemInstanceData& Item, float DrainPerSecond, float Seconds)
|
||||
{
|
||||
const float Charge = Item.RuntimeProperties.GetFloat(ItemEcosystemTags::Property_BatteryCharge, 1.f);
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_BatteryCharge,
|
||||
FMath::Clamp(Charge - DrainPerSecond * Seconds, 0.f, 1.f));
|
||||
}
|
||||
|
||||
float UItemFeatureLibrary::AddFuel(FItemInstanceData& Item, float Amount, float MaxFuel)
|
||||
{
|
||||
const float Current = Item.RuntimeProperties.GetFloat(ItemEcosystemTags::Property_FuelAmount, 0.f);
|
||||
const float NewValue = FMath::Clamp(Current + Amount, 0.f, MaxFuel);
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_FuelAmount, NewValue);
|
||||
return NewValue - Current;
|
||||
}
|
||||
|
||||
float UItemFeatureLibrary::TransferFuel(FItemInstanceData& From, FItemInstanceData& To, float Amount, float ToMaxFuel)
|
||||
{
|
||||
const float Available = From.RuntimeProperties.GetFloat(ItemEcosystemTags::Property_FuelAmount, 0.f);
|
||||
const float ToCurrent = To.RuntimeProperties.GetFloat(ItemEcosystemTags::Property_FuelAmount, 0.f);
|
||||
const float Room = FMath::Max(0.f, ToMaxFuel - ToCurrent);
|
||||
const float Moved = FMath::Min3(Amount, Available, Room);
|
||||
if (Moved <= 0.f)
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
From.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_FuelAmount, Available - Moved);
|
||||
To.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_FuelAmount, ToCurrent + Moved);
|
||||
return Moved;
|
||||
}
|
||||
|
||||
void UItemFeatureLibrary::SetWetness(FItemInstanceData& Item, float Wetness)
|
||||
{
|
||||
const float Clamped = FMath::Clamp(Wetness, 0.f, 1.f);
|
||||
Item.RuntimeProperties.SetFloat(ItemEcosystemTags::Property_Wetness, Clamped);
|
||||
if (Clamped > 0.5f) { Item.StateTags.AddTag(ItemEcosystemTags::State_Wet); }
|
||||
else { Item.StateTags.RemoveTag(ItemEcosystemTags::State_Wet); }
|
||||
}
|
||||
Reference in New Issue
Block a user