87 lines
3.1 KiB
C++
87 lines
3.1 KiB
C++
// © 2025 Naked People Team. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "UObject/Object.h"
|
|
#include "CommissionTypes.h"
|
|
#include "Commission.generated.h"
|
|
|
|
class UCommissionObjective;
|
|
class ANakedDesireCharacter;
|
|
class UCommission;
|
|
|
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnCommissionChangedSignature, UCommission*);
|
|
|
|
/**
|
|
* A commission (GDD §13). Authored inline in a UCommissionBoardConfig and duplicated to a runtime
|
|
* instance by UMissionSubsystem; the duplicate owns the state machine and its live objectives.
|
|
* Completes when every objective reports satisfied; the subsystem pays the reward and persists state.
|
|
*/
|
|
UCLASS(EditInlineNew, BlueprintType)
|
|
class NAKEDDESIRE_API UCommission : public UObject
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
// --- Authoring ---
|
|
// Stable id used for save/restore (must be unique within the board config).
|
|
UPROPERTY(EditDefaultsOnly)
|
|
FName CommissionId;
|
|
|
|
UPROPERTY(EditDefaultsOnly)
|
|
FText Title;
|
|
|
|
// Lore-only forum poster (§13.3) — no gameplay effect.
|
|
UPROPERTY(EditDefaultsOnly)
|
|
FText PosterUsername;
|
|
|
|
UPROPERTY(EditDefaultsOnly)
|
|
ECommissionTier Tier = ECommissionTier::Daily;
|
|
|
|
UPROPERTY(EditDefaultsOnly)
|
|
FCommissionReward Reward;
|
|
|
|
UPROPERTY(EditDefaultsOnly, Instanced)
|
|
TArray<UCommissionObjective*> Objectives;
|
|
|
|
// When true, objectives activate one at a time in array order (strip at A -> walk to B -> ... ).
|
|
// When false (default), all objectives are active at once and may be satisfied in any order.
|
|
UPROPERTY(EditDefaultsOnly)
|
|
bool bSequentialObjectives = false;
|
|
|
|
// --- Runtime ---
|
|
FOnCommissionChangedSignature OnStateChanged; // any transition
|
|
FOnCommissionChangedSignature OnCompleted; // -> Completed
|
|
|
|
ECommissionState GetState() const { return State; }
|
|
|
|
UFUNCTION(BlueprintPure) ECommissionTier GetTier() const { return Tier; }
|
|
UFUNCTION(BlueprintPure) FText GetTitle() const { return Title; }
|
|
UFUNCTION(BlueprintPure) FText GetPosterUsername() const { return PosterUsername; }
|
|
UFUNCTION(BlueprintPure) FCommissionReward GetReward() const { return Reward; }
|
|
UFUNCTION(BlueprintPure) TArray<UCommissionObjective*> GetObjectives() const { return Objectives; }
|
|
|
|
void Accept(ANakedDesireCharacter* InPlayer); // Offered -> Accepted; arms objectives
|
|
void Abandon(); // Accepted -> Offered; no penalty (§13.2)
|
|
void Expire(); // Accepted -> Expired (deadline)
|
|
|
|
// Re-apply a persisted state on load (re-arms objectives when restoring Accepted).
|
|
void RestoreState(ECommissionState SavedState, ANakedDesireCharacter* InPlayer);
|
|
|
|
private:
|
|
void Complete();
|
|
bool AreAllObjectivesSatisfied() const;
|
|
void ArmObjectives();
|
|
void DisarmObjectives();
|
|
void AdvanceSequential(); // activate the first unsatisfied objective, or complete if none remain
|
|
void SetState(ECommissionState NewState);
|
|
|
|
void HandleObjectiveStateChanged(UCommissionObjective* Objective);
|
|
|
|
ECommissionState State = ECommissionState::Offered;
|
|
int32 ActiveObjectiveIndex = INDEX_NONE; // sequential mode: the objective currently armed
|
|
|
|
UPROPERTY()
|
|
ANakedDesireCharacter* Player = nullptr;
|
|
}; |