Block 9 — Progression Hooks (1 day)

Goal: Milestones formalize unlock triggers, replace hardcoded gating from Block 7, and give players directed short-term goals via quest log.

Depends on: Block 7 (target unlocks, Distractor, recruitment), Block 4 (raid completion events).


Deliverables

Milestone DataTable (DT_Milestones)

UDataTable using row struct FMilestoneTableRow:

UENUM(BlueprintType)
enum class EProgressionEvent : uint8 { RaidCompleted, RaccoonRecruited, StationUpgraded, GearCrafted };
 
UENUM(BlueprintType)
enum class EMilestoneReward : uint8 { UnlockUnit, UnlockTarget, AddRaccoon, AddResource };
 
USTRUCT(BlueprintType)
struct FMilestoneTableRow : public FTableRowBase {
    UPROPERTY(EditAnywhere) FString DisplayName;
    UPROPERTY(EditAnywhere) FString Description;       // "Complete 5 raids"
    UPROPERTY(EditAnywhere) EProgressionEvent TriggerEvent;
    UPROPERTY(EditAnywhere) int32 Threshold;            // how many events to trigger
    UPROPERTY(EditAnywhere) EMilestoneReward RewardType;
    UPROPERTY(EditAnywhere) FString RewardPayload;      // flexible: unit type name, target asset path, etc.
    UPROPERTY(EditAnywhere) FString RewardDescription;  // "Hauler unit unlocked!"
};

MVP Milestones:

Row NameDisplayTriggerThresholdRewardPayload
FirstScore”First Score”RaidCompleted1AddRaccoon”Hauler” — new Hauler auto-joins
GrowingOperation”Growing Operation”RaidCompleted5UnlockTarget”RichNeighbor,GroceryStore”
StreetCred”Street Cred”RaccoonRecruited4UnlockUnit”Distractor”

DataTable at Content/TrashNTreasure/Data/Tables/DT_Milestones.uasset.

FMilestoneProgress Struct

USTRUCT(BlueprintType)
struct FMilestoneProgress {
    UPROPERTY(BlueprintReadOnly) int32 CurrentCount = 0;
    UPROPERTY(BlueprintReadOnly) bool bCompleted = false;
};

UProgressionSubsystem — Full Implementation (C++)

FunctionSignaturePurpose
Initializevoid ()Called in ATNTGameMode::BeginPlay. Load DT_Milestones rows into memory. Init TMap<FName, FMilestoneProgress> for each row. Bind to subsystem delegates: URaidSubsystem::OnRaidCompleted, URaccoonSubsystem::OnRosterChanged.
HandleEventvoid (EProgressionEvent, int32 Count=1)C++ — iterates milestone table. For each milestone matching the event type and not yet completed: increment CurrentCount by Count. If CurrentCount >= Threshold: mark completed, call ApplyReward, broadcast OnMilestoneCompleted.
ApplyRewardvoid (FMilestoneTableRow&)Switch on RewardType:
UnlockUnit → add type to recruitment pool (flag on URaccoonSubsystem)
UnlockTarget → unlock buildings on city map (flag on UCityMapSubsystem)
AddRaccoonURaccoonSubsystem::AddRaccoon(type from payload)
AddResourceUResourceSubsystem::AddResource
IsMilestoneCompletebool (FName RowName)For UI checkmarks and gating queries
IsTargetUnlockedbool (URaidTargetDataAsset*)Checks target’s unlock milestone. If no milestone → always unlocked. If milestone → IsMilestoneComplete. Used by UCityMapSubsystem::GetUnlockedBuildings and ATNTBuilding for locked/unlocked visuals.
GetAllMilestonesTArray<FMilestoneDisplayData> ()For quest log UI. Returns name, description, progress fraction, completed flag, reward description.

Internal state (extends Block 1 stub — MilestoneProgress map declared there):

MemberTypePurpose
MilestoneTableUDataTable*Reference to DT_Milestones. Loaded in Initialize, rows cached for HandleEvent iteration
OnMilestoneCompletedFOnMilestoneCompleted (DYNAMIC_MULTICAST_DELEGATE_OneParam)BlueprintAssignable. Param: FName MilestoneRowName. Quest log and HUD bind to this

Event Binding

UProgressionSubsystem::Initialize binds to:

Source DelegateMaps To
URaidSubsystem::OnRaidCompletedHandleEvent(RaidCompleted, 1)
URaccoonSubsystem::OnRosterChangedCheck if raccoon was added → HandleEvent(RaccoonRecruited, 1)

Need a way to distinguish roster additions from removals. Options:

  • Add OnRaccoonAdded / OnRaccoonRemoved delegates alongside OnRosterChanged
  • Or check roster count delta in the handler

Recommend: add DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRaccoonAdded, FGuid, RaccoonId) to URaccoonSubsystem. Fire from AddRaccoon only.

Replace Hardcoded Triggers

Block 7 hardcoded unlock triggers. Replace with milestone queries:

  • UCityMapSubsystem::GetUnlockedBuildings → calls UProgressionSubsystem::IsTargetUnlocked per building
  • ATNTBuilding locked/unlocked visual state → queries IsTargetUnlocked on BeginPlay + listens to OnMilestoneCompleted
  • Distractor recruitment availability → URaccoonSubsystem::GetAvailableRecruitTypes checks IsMilestoneComplete("StreetCred")

Quest Log UI (UTNTQuestLogUI)

UMG widget toggled by hotkey (J):

  • Active milestones: Each shows name, description, progress bar (e.g., “Complete 5 raids: 3/5”)
  • Completed milestones: Checked off with reward description
  • Binds to OnMilestoneCompleted for live updates
  • Plain text, no styling

Widget: Content/TrashNTreasure/UI/WBP_QuestLog.uasset.

Milestone Completion Notification

When OnMilestoneCompleted fires:

  • HUD popup/toast: “Milestone Complete: [Name]! Reward: [RewardDescription]”
  • Blueprint handles the visual notification (bind to delegate on HUD widget)

Milestone Chain Verification

End-to-end test:

  1. New game → 2 raccoons (Scout + Hauler)
  2. Complete 1 raid → “First Score” fires → Hauler joins → roster now 3
  3. Complete 4 more raids → “Growing Operation” fires → Rich Neighbor + Grocery Store unlock on city map
  4. Recruit from raids until 4 total recruited → “Street Cred” fires → Distractor available in pool
  5. All milestone progress tracks correctly, rewards apply, quest log reflects

Done When

  • Milestones track progress and fire on correct triggers
  • “First Score” (1 raid) → new Hauler auto-joins
  • “Growing Operation” (5 raids) → Rich Neighbor + Grocery Store unlock on city map
  • “Street Cred” (4 recruits) → Distractor type available in recruitment pool
  • Quest log (J) shows active progress and completed milestones
  • Hardcoded unlock triggers from Block 7 replaced with milestone queries
  • New player can follow milestone chain from first raid to Distractor unlock
  • OnMilestoneCompleted fires and HUD shows notification toast

References

Progression · Core Loop