Quests and shared story authoring
NiceQuests and NiceStoryline add separate pages to the existing NiceLobby menu. Their editable catalogs are /Game/NiceGame/Data/DA_NGS_Quests and DA_NGS_Story. Tools/setup_gameplay.py installs the extended PlayerState/GameState without replacing existing match rules or controllers.
Quests
Each quest has a stable ID, main/side category, title, description, prerequisite quest IDs, objectives and rewards. Objectives match a trusted gameplay event and optional subject, with a required count. Never reuse an ID for an unrelated quest in a released save format.
The example FirstSupplies requires accepting the quest, then acquiring three Wood; it awards 150 XP and Trail Boots. CampPreparations follows it, requires a successful Harvest interaction, and awards XP plus the Camp skill. Existing backpack contents do not retroactively count as newly acquired items.
Trusted server code emits events through UNGSProgressionComponent::RecordEvent. Inventory emits ItemAcquired, interactables emit Interacted with the interaction kind, quest completion emits QuestCompleted, and combat emits Defeated. There is no client RPC to submit arbitrary objective progress.
UNGSRewardLibrary::Grant preflights the entire XP/item/skill reward and records a persistent claim ID. Insufficient space or unmet skill prerequisites leaves the reward pending. Replaying a claim cannot duplicate XP or items. Free skill ranks are tracked separately from purchased ranks so travel/save validation does not charge points for a quest reward.
Shared campaign
The campaign component lives on ANGSGameState, while owned player components submit votes through RPCs. In this co-op implementation, all active players in the server's GameState form one party. Multiple independent story parties in one server are not implemented.
A node contains episode/scene identifiers, narrative, optional required event/subject, rewards, area/boss unlock IDs, choices and an optional Level Sequence. Each choice references a next node. The catalog validates references and duplicate IDs; loops are permitted. A node's rewards are granted once per character per campaign, including history rewards for late joiners. Backpack/skill failures remain claimable through the Storyline page.
The voting threshold is floor(active players / 2) + 1. A solo player needs one vote; two players need two; three need two. Players can change votes before a majority is reached. Departures recalculate the roster and threshold. Ties wait without selecting a random branch. Votes must identify the current node, so delayed input cannot select a similarly named choice in the next scene. Quest requirements also check existing completed quests when a party enters a scene.
The example has Arrival โ Prepare Camp or Explore Ruins โ First Night. Preparing camp waits for CampPreparations; ruins wait for FirstSupplies. Nodes unlock SafeCamp/OldRuins and CampRaider. These are replicated access flags: authored doors, spawners or encounters must query IsAreaUnlocked/IsBossUnlocked. The plugin does not create a finished boss fight or hidden-area level automatically.
Optional Level Sequences play on the server with replicated playback. Author camera possession, participant binding and cinematics for the actual scene; no new animation/cinematic art is supplied by the placeholder catalog.
Persistence and travel
Personal quest and reward claims are stored with the character. Shared story is stored separately by UNGSStoryStore in the server/host's save directory, keyed by Nice Story > Campaign Slot. Each deployment/host has its own campaign unless you deliberately provide a common backend. EOS identity/session transport alone does not synchronize these disk saves between different hosts.
The GameInstance cache retains campaign state across map travel and guards against an outgoing world overwriting a newer world. Unknown/newer/corrupt campaign saves are preserved rather than overwritten. Save writes are synchronous checkpoints; automatic cross-host cloud storage and host migration are not yet implemented.
DA_NLS_GameSettings > Travel > Retire Pawns Before Seamless Travel enables a project workaround for a Mover NetworkPrediction assertion observed during listen-server world replacement. It unpossesses/destroys pawns, allows their destruction to replicate for one second, then uses the existing seamless travel flow. PlayerState progression remains intact; destination pawns are spawned normally. The original character graphs and engine files are unchanged. Validate this option with your actual latency and travel maps; it is enabled by the setup script for this project.
Development validation runs use transient characters/campaigns under -NGSValidate; they do not overwrite player profiles.