Added phone item
This commit is contained in:
@@ -33,7 +33,4 @@ public:
|
||||
protected:
|
||||
virtual void CaptureState(FInstancedStruct& OutState) const override;
|
||||
virtual void ApplyState(const FInstancedStruct& InState) override;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
|
||||
TArray<TObjectPtr<UItemInstance>> StoredItems;
|
||||
};
|
||||
@@ -7,7 +7,10 @@
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
#include "NakedDesire/Items/ItemDefinition.h"
|
||||
#include "NakedDesire/Items/ItemInstance.h"
|
||||
#include "NakedDesire/Phone/PhoneItemDefinition.h"
|
||||
#include "NakedDesire/Phone/PhoneItemInstance.h"
|
||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
|
||||
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
|
||||
@@ -19,6 +22,12 @@ const TArray<TObjectPtr<UItemInstance>>& UInventorySubsystem::GetWardrobeItems()
|
||||
return WardrobeItems;
|
||||
}
|
||||
|
||||
UPhoneItemInstance* UInventorySubsystem::GetEquippedPhone()
|
||||
{
|
||||
EnsureHydrated();
|
||||
return EquippedPhone;
|
||||
}
|
||||
|
||||
void UInventorySubsystem::AddToWardrobe(UItemInstance* Item)
|
||||
{
|
||||
EnsureHydrated();
|
||||
@@ -37,6 +46,12 @@ void UInventorySubsystem::EquipFromWardrobe(UItemInstance* Item)
|
||||
{
|
||||
EnsureHydrated();
|
||||
|
||||
if (UPhoneItemInstance* Phone = Cast<UPhoneItemInstance>(Item))
|
||||
{
|
||||
EquipPhone(Phone);
|
||||
return;
|
||||
}
|
||||
|
||||
UClothingItemInstance* Clothing = Cast<UClothingItemInstance>(Item);
|
||||
if (!Clothing || !Clothing->GetClothingItemDefinition())
|
||||
return;
|
||||
@@ -74,6 +89,12 @@ void UInventorySubsystem::UnequipToWardrobe(UItemInstance* Item)
|
||||
{
|
||||
EnsureHydrated();
|
||||
|
||||
if (UPhoneItemInstance* Phone = Cast<UPhoneItemInstance>(Item))
|
||||
{
|
||||
UnequipPhone(Phone);
|
||||
return;
|
||||
}
|
||||
|
||||
UClothingItemInstance* Clothing = Cast<UClothingItemInstance>(Item);
|
||||
if (!Clothing || !Clothing->GetClothingItemDefinition())
|
||||
return;
|
||||
@@ -90,6 +111,45 @@ void UInventorySubsystem::UnequipToWardrobe(UItemInstance* Item)
|
||||
OnWardrobeChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventorySubsystem::EquipPhone(UPhoneItemInstance* Phone)
|
||||
{
|
||||
if (!Phone || Phone == EquippedPhone)
|
||||
return;
|
||||
|
||||
UGlobalSaveGameData* Save = GetSave();
|
||||
|
||||
// Hot-swap: the previously equipped phone returns to the wardrobe (§9.9).
|
||||
if (EquippedPhone)
|
||||
{
|
||||
if (Save)
|
||||
Save->RemoveEquippedItem(EquippedPhone);
|
||||
StoreItem(EquippedPhone);
|
||||
}
|
||||
|
||||
UnstoreItem(Phone);
|
||||
EquippedPhone = Phone;
|
||||
if (Save)
|
||||
Save->AddEquippedItem(Phone);
|
||||
|
||||
OnPhoneChanged.Broadcast(EquippedPhone);
|
||||
OnWardrobeChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventorySubsystem::UnequipPhone(UPhoneItemInstance* Phone)
|
||||
{
|
||||
if (!Phone || EquippedPhone != Phone)
|
||||
return;
|
||||
|
||||
if (UGlobalSaveGameData* Save = GetSave())
|
||||
Save->RemoveEquippedItem(Phone);
|
||||
|
||||
EquippedPhone = nullptr;
|
||||
StoreItem(Phone);
|
||||
|
||||
OnPhoneChanged.Broadcast(nullptr);
|
||||
OnWardrobeChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventorySubsystem::EnsureHydrated()
|
||||
{
|
||||
UGlobalSaveGameData* Save = GetSave();
|
||||
@@ -108,6 +168,23 @@ void UInventorySubsystem::EnsureHydrated()
|
||||
if (UItemInstance* Instance = UItemInstance::CreateFromRecord(this, Record))
|
||||
WardrobeItems.Add(Instance);
|
||||
}
|
||||
|
||||
// Only the phone is hydrated from the equipped bucket here; equipped clothing is owned and
|
||||
// hydrated by the per-character UClothingManager. Pre-check the definition type so we don't
|
||||
// mint throwaway clothing instances.
|
||||
EquippedPhone = nullptr;
|
||||
for (const FItemSaveRecord& Record : Save->GetEquippedItems())
|
||||
{
|
||||
const UItemDefinition* Definition = Record.Definition.LoadSynchronous();
|
||||
if (!Definition || !Definition->IsA<UPhoneItemDefinition>())
|
||||
continue;
|
||||
|
||||
if (UPhoneItemInstance* Phone = Cast<UPhoneItemInstance>(UItemInstance::CreateFromRecord(this, Record)))
|
||||
{
|
||||
EquippedPhone = Phone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UInventorySubsystem::StoreItem(UItemInstance* Item)
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
class UItemInstance;
|
||||
class UClothingManager;
|
||||
class UGlobalSaveGameData;
|
||||
class UPhoneItemInstance;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWardrobeChangedSignature);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPhoneChangedSignature, UPhoneItemInstance*, Phone);
|
||||
|
||||
/**
|
||||
* Runtime owner of the off-body item store (the apartment wardrobe, GDD §6.5 / §10.4).
|
||||
@@ -31,9 +33,16 @@ public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnWardrobeChangedSignature OnWardrobeChanged;
|
||||
|
||||
// Fires when the phone slot changes; broadcasts the newly-equipped phone, or nullptr when emptied.
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnPhoneChangedSignature OnPhoneChanged;
|
||||
|
||||
/** Live wardrobe instances (off-body, mirrored from the save). */
|
||||
const TArray<TObjectPtr<UItemInstance>>& GetWardrobeItems();
|
||||
|
||||
/** The phone occupying the dedicated phone slot (§6.5 / §9.9), or nullptr if none is equipped. */
|
||||
UPhoneItemInstance* GetEquippedPhone();
|
||||
|
||||
/** Bring an item into the wardrobe (purchase, world-return). */
|
||||
void AddToWardrobe(UItemInstance* Item);
|
||||
|
||||
@@ -51,12 +60,18 @@ private:
|
||||
void StoreItem(UItemInstance* Item); // live list + save bucket, no broadcast
|
||||
void UnstoreItem(UItemInstance* Item); // live list + save bucket, no broadcast
|
||||
|
||||
void EquipPhone(UPhoneItemInstance* Phone); // hot-swaps the previous phone back to the wardrobe
|
||||
void UnequipPhone(UPhoneItemInstance* Phone); // stores the phone back in the wardrobe
|
||||
|
||||
UClothingManager* GetPlayerClothingManager() const;
|
||||
UGlobalSaveGameData* GetSave() const;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UItemInstance>> WardrobeItems;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UPhoneItemInstance> EquippedPhone;
|
||||
|
||||
// The save the live list was built from; re-hydrate when this changes (e.g. load game).
|
||||
TWeakObjectPtr<UGlobalSaveGameData> HydratedSave;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "PhoneItemDefinition.h"
|
||||
|
||||
#include "PhoneItemInstance.h"
|
||||
|
||||
TSubclassOf<UItemInstance> UPhoneItemDefinition::GetInstanceClass() const
|
||||
{
|
||||
return UPhoneItemInstance::StaticClass();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NakedDesire/Items/ItemDefinition.h"
|
||||
#include "PhoneItemDefinition.generated.h"
|
||||
|
||||
// Immutable definition for a phone (GDD §6.2 / §9.9). A phone is a regular UItemInstance item that
|
||||
// lives in the wardrobe / bag and occupies the dedicated phone slot when active (§6.5).
|
||||
// Phone-tier stats (camera / livestream / battery-capacity multipliers, §9.9) are deferred to Phase 8/9.
|
||||
UCLASS(BlueprintType)
|
||||
class NAKEDDESIRE_API UPhoneItemDefinition : public UItemDefinition
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual TSubclassOf<UItemInstance> GetInstanceClass() const override;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Phone")
|
||||
float MaxBattery = 100.0f;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "PhoneItemInstance.h"
|
||||
|
||||
#include "PhoneItemDefinition.h"
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
|
||||
void UPhoneItemInstance::CaptureState(FInstancedStruct& OutState) const
|
||||
{
|
||||
FPhoneInstanceState PhoneState;
|
||||
PhoneState.CurrentBattery = CurrentBattery;
|
||||
OutState.InitializeAs<FPhoneInstanceState>(PhoneState);
|
||||
}
|
||||
|
||||
void UPhoneItemInstance::ApplyState(const FInstancedStruct& InState)
|
||||
{
|
||||
if (const FPhoneInstanceState* PhoneState = InState.GetPtr<FPhoneInstanceState>())
|
||||
{
|
||||
CurrentBattery = PhoneState->CurrentBattery;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "PhoneItemDefinition.h"
|
||||
#include "NakedDesire/Items/ItemInstance.h"
|
||||
#include "PhoneItemInstance.generated.h"
|
||||
|
||||
/** Per-instance mutable state for a phone. Battery is per-phone-instance (§9.9). */
|
||||
USTRUCT()
|
||||
struct FPhoneInstanceState : public FItemInstanceState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
float CurrentBattery = 100.0f;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class NAKEDDESIRE_API UPhoneItemInstance : public UItemInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Charge of this specific phone, [0,1]. Drain / charge logic lands with the phone battery system (Phase 8/9).
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Phone Item")
|
||||
float CurrentBattery = 100.0f;
|
||||
|
||||
UPhoneItemDefinition* GetPhoneItemDefinition() const { return Cast<UPhoneItemDefinition>(ItemDefinition); }
|
||||
|
||||
protected:
|
||||
virtual void CaptureState(FInstancedStruct& OutState) const override;
|
||||
virtual void ApplyState(const FInstancedStruct& InState) override;
|
||||
};
|
||||
@@ -62,7 +62,7 @@ void USaveSubsystem::PopulateStartingData(UGlobalSaveGameData* Save) const
|
||||
if (!GameInstance || !GameInstance->StartingSaveData)
|
||||
return;
|
||||
|
||||
for (UItemDefinition* Definition : GameInstance->StartingSaveData->StartingItems)
|
||||
for (UItemDefinition* Definition : GameInstance->StartingSaveData->EquippedItems)
|
||||
{
|
||||
if (!Definition)
|
||||
continue;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "StartingSaveData.generated.h"
|
||||
|
||||
class UPhoneItemDefinition;
|
||||
class UItemDefinition;
|
||||
|
||||
UCLASS()
|
||||
@@ -15,5 +16,11 @@ class NAKEDDESIRE_API UStartingSaveData : public UPrimaryDataAsset
|
||||
|
||||
public:
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TArray<TObjectPtr<UItemDefinition>> StartingItems;
|
||||
TArray<TObjectPtr<UItemDefinition>> EquippedItems;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TArray<TObjectPtr<UItemDefinition>> WardrobeItems;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TObjectPtr<UPhoneItemDefinition> Phone;
|
||||
};
|
||||
Reference in New Issue
Block a user