Added commissions system

This commit is contained in:
2026-06-01 00:27:56 +03:00
parent a3e23393dc
commit 003d9992e2
81 changed files with 2418 additions and 1065 deletions
@@ -0,0 +1,46 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "DayPhaseConstraint.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#define LOCTEXT_NAMESPACE "Commissions.Constraints.DayPhase"
void UDayPhaseConstraint::OnActivate()
{
if (UTimeOfDaySubsystem* Time = GetTime())
Time->OnPhaseChanged.AddUniqueDynamic(this, &UDayPhaseConstraint::HandlePhaseChanged);
}
void UDayPhaseConstraint::OnDeactivate()
{
if (UTimeOfDaySubsystem* Time = GetTime())
Time->OnPhaseChanged.RemoveDynamic(this, &UDayPhaseConstraint::HandlePhaseChanged);
}
bool UDayPhaseConstraint::IsMet() const
{
const UTimeOfDaySubsystem* Time = GetTime();
return Time && Time->GetPhase() == RequiredPhase;
}
void UDayPhaseConstraint::HandlePhaseChanged(EDayPhase NewPhase)
{
NotifyChanged();
}
UTimeOfDaySubsystem* UDayPhaseConstraint::GetTime() const
{
UWorld* World = Player ? Player->GetWorld() : nullptr;
return World ? World->GetSubsystem<UTimeOfDaySubsystem>() : nullptr;
}
FText UDayPhaseConstraint::GetDescription() const
{
return (RequiredPhase == EDayPhase::Night)
? LOCTEXT("Night", "at night")
: LOCTEXT("Day", "during the day");
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,32 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NakedDesire/Commissions/CommissionConstraint.h"
#include "NakedDesire/Global/TimeOfDaySubsystem.h"
#include "DayPhaseConstraint.generated.h"
// Holds only during the chosen day phase (§10.1).
UCLASS(EditInlineNew, DisplayName = "During Day Phase")
class NAKEDDESIRE_API UDayPhaseConstraint : public UCommissionConstraint
{
GENERATED_BODY()
public:
virtual bool IsMet() const override;
virtual FText GetDescription() const override;
protected:
virtual void OnActivate() override;
virtual void OnDeactivate() override;
private:
UPROPERTY(EditDefaultsOnly)
EDayPhase RequiredPhase = EDayPhase::Night;
UFUNCTION()
void HandlePhaseChanged(EDayPhase NewPhase);
UTimeOfDaySubsystem* GetTime() const;
};
@@ -0,0 +1,51 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "LocationConstraint.h"
#include "NakedDesire/Locations/LocationSubsystem.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#define LOCTEXT_NAMESPACE "Commissions.Constraints.Location"
void ULocationConstraint::OnActivate()
{
if (ULocationSubsystem* Locations = GetLocations())
{
Locations->OnLocationEntered.AddUniqueDynamic(this, &ULocationConstraint::HandleLocationChanged);
Locations->OnLocationExited.AddUniqueDynamic(this, &ULocationConstraint::HandleLocationChanged);
}
}
void ULocationConstraint::OnDeactivate()
{
if (ULocationSubsystem* Locations = GetLocations())
{
Locations->OnLocationEntered.RemoveDynamic(this, &ULocationConstraint::HandleLocationChanged);
Locations->OnLocationExited.RemoveDynamic(this, &ULocationConstraint::HandleLocationChanged);
}
}
bool ULocationConstraint::IsMet() const
{
const ULocationSubsystem* Locations = GetLocations();
return Locations && Locations->IsPlayerInLocation(RequiredLocation);
}
void ULocationConstraint::HandleLocationChanged(ULocationData* Location)
{
NotifyChanged();
}
ULocationSubsystem* ULocationConstraint::GetLocations() const
{
UWorld* World = Player ? Player->GetWorld() : nullptr;
return World ? World->GetSubsystem<ULocationSubsystem>() : nullptr;
}
FText ULocationConstraint::GetDescription() const
{
return FText::Format(LOCTEXT("AtLocation", "while at {0}"), FText::FromName(RequiredLocation.GetTagName()));
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,35 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "NakedDesire/Commissions/CommissionConstraint.h"
#include "LocationConstraint.generated.h"
class ULocationData;
class ULocationSubsystem;
// Holds while the player occupies a location matching (or nesting under) RequiredLocation.
UCLASS(EditInlineNew, DisplayName = "While At Location")
class NAKEDDESIRE_API ULocationConstraint : public UCommissionConstraint
{
GENERATED_BODY()
public:
virtual bool IsMet() const override;
virtual FText GetDescription() const override;
protected:
virtual void OnActivate() override;
virtual void OnDeactivate() override;
private:
UPROPERTY(EditDefaultsOnly)
FGameplayTag RequiredLocation;
UFUNCTION()
void HandleLocationChanged(ULocationData* Location);
ULocationSubsystem* GetLocations() const;
};
@@ -0,0 +1,43 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ObservedConstraint.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#include "NakedDesire/Stats/StatsManager.h"
#define LOCTEXT_NAMESPACE "Commissions.Constraints.Observed"
void UObservedConstraint::OnActivate()
{
if (Player && Player->StatsManager)
Player->StatsManager->OnObserversChanged.AddUniqueDynamic(this, &UObservedConstraint::HandleObserversChanged);
}
void UObservedConstraint::OnDeactivate()
{
if (Player && Player->StatsManager)
Player->StatsManager->OnObserversChanged.RemoveDynamic(this, &UObservedConstraint::HandleObserversChanged);
}
bool UObservedConstraint::IsMet() const
{
if (!Player || !Player->StatsManager)
return false;
return Player->StatsManager->GetObserverCount() >= MinObservers;
}
void UObservedConstraint::HandleObserversChanged()
{
NotifyChanged();
}
FText UObservedConstraint::GetDescription() const
{
return (MinObservers == 1)
? LOCTEXT("Single", "while someone is watching")
: FText::Format(LOCTEXT("Multiple", "while {0} people are watching"), FText::AsNumber(MinObservers));
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,29 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NakedDesire/Commissions/CommissionConstraint.h"
#include "ObservedConstraint.generated.h"
// Holds only while at least MinObservers NPCs are perceiving the player (the embarrassment observer set).
UCLASS(EditInlineNew, DisplayName = "While Observed By N NPCs")
class NAKEDDESIRE_API UObservedConstraint : public UCommissionConstraint
{
GENERATED_BODY()
public:
virtual bool IsMet() const override;
virtual FText GetDescription() const override;
protected:
virtual void OnActivate() override;
virtual void OnDeactivate() override;
private:
UPROPERTY(EditDefaultsOnly, meta = (ClampMin = 1))
int32 MinObservers = 1;
UFUNCTION()
void HandleObserversChanged();
};
@@ -0,0 +1,49 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "WearingSlotConstraint.h"
#include "NakedDesire/Clothing/ClothingManager.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#define LOCTEXT_NAMESPACE "Commissions.Constraints.WearingSlot"
void UWearingSlotConstraint::OnActivate()
{
if (Player && Player->ClothingManager)
{
Player->ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &UWearingSlotConstraint::HandleClothingChanged);
Player->ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &UWearingSlotConstraint::HandleClothingChanged);
}
}
void UWearingSlotConstraint::OnDeactivate()
{
if (Player && Player->ClothingManager)
{
Player->ClothingManager->OnClothingEquip.RemoveDynamic(this, &UWearingSlotConstraint::HandleClothingChanged);
Player->ClothingManager->OnClothingUnequip.RemoveDynamic(this, &UWearingSlotConstraint::HandleClothingChanged);
}
}
bool UWearingSlotConstraint::IsMet() const
{
if (!Player || !Player->ClothingManager)
return false;
return Player->ClothingManager->IsClothingTypeOn(Slot) == bMustBeWorn;
}
void UWearingSlotConstraint::HandleClothingChanged(UClothingItemInstance* ClothingItemInstance)
{
NotifyChanged();
}
FText UWearingSlotConstraint::GetDescription() const
{
return bMustBeWorn
? LOCTEXT("Wearing", "while dressed in the required item")
: LOCTEXT("NotWearing", "while that slot is bare");
}
#undef LOCTEXT_NAMESPACE
@@ -0,0 +1,36 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NakedDesire/Clothing/ClothingSlotType.h"
#include "NakedDesire/Commissions/CommissionConstraint.h"
#include "WearingSlotConstraint.generated.h"
class UClothingItemInstance;
// Holds while the chosen slot is occupied (bMustBeWorn) or empty (!bMustBeWorn). Covers both
// "while wearing a coat" (Outerwear occupied) and "while not wearing a top" (Top empty).
UCLASS(EditInlineNew, DisplayName = "While Wearing / Not Wearing Slot")
class NAKEDDESIRE_API UWearingSlotConstraint : public UCommissionConstraint
{
GENERATED_BODY()
public:
virtual bool IsMet() const override;
virtual FText GetDescription() const override;
protected:
virtual void OnActivate() override;
virtual void OnDeactivate() override;
private:
UPROPERTY(EditDefaultsOnly)
EClothingSlotType Slot = EClothingSlotType::Outerwear;
UPROPERTY(EditDefaultsOnly)
bool bMustBeWorn = true;
UFUNCTION()
void HandleClothingChanged(UClothingItemInstance* ClothingItemInstance);
};