138 lines
6.7 KiB
C++
138 lines
6.7 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Engine/DataAsset.h"
|
|
#include "GameplayTagContainer.h"
|
|
#include "QuestTypes.h"
|
|
|
|
#include "QuestDataAsset.generated.h"
|
|
|
|
class ULevelSequence;
|
|
class UTexture2D;
|
|
|
|
/**
|
|
* One quest, authored as an asset.
|
|
*
|
|
* A quest is a tag, a list of steps and a list of things that have to be true before it can be taken.
|
|
* Everything else about it, the journal entry, the rewards and the conditions on individual steps, hangs
|
|
* off those three, so a quest with no code of its own is a data asset drop and one line of dialogue.
|
|
*
|
|
* Steps are not a state machine. Each one listens to whatever the game already broadcasts for it, so an
|
|
* InteractWith step is satisfied by the same interaction the player performs to open a chest, and two
|
|
* quests watching the same NPC both count the same conversation without either of them owning it.
|
|
*/
|
|
UCLASS(BlueprintType, PrioritizeCategories = ("Quest", "Objectives", "Requirements", "Rewards"))
|
|
class PROJECTELERI_API UQuestDataAsset : public UPrimaryDataAsset
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
|
|
/** Identity of the quest. Every lookup, every requirement and every save entry goes through this
|
|
* tag, so it is the one field that has to be filled in. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Quest", meta = (GameplayTagFilter = "Quests"))
|
|
FGameplayTag QuestTag;
|
|
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Quest")
|
|
FText QuestName;
|
|
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Quest", meta = (MultiLine = true))
|
|
FText QuestDescription;
|
|
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Quest")
|
|
TSoftObjectPtr<UTexture2D> QuestIcon;
|
|
|
|
/** The NPC this quest is handed out by. The journal uses it to say who to go back to, and a
|
|
* dialogue can ask "do you have anything for me" through it. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Quest", meta = (ForceInlineRow, Categories = "NpcTags"))
|
|
FGameplayTag QuestGiverTag;
|
|
|
|
/** Days the player has to hand the quest in. 0 means it never runs out. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Quest", meta = (ClampMin = 0))
|
|
int32 DeadlineInDays = 0;
|
|
|
|
/**
|
|
* Cutscenes the quest plays as it changes stage, keyed by the status it moves to.
|
|
*
|
|
* A stage a quest enters is a status, so this is what makes a quest able to show its own beat
|
|
* without any blueprint being involved: the scene under Completed plays the moment the quest is
|
|
* handed in, and the one under Failed plays when a deadline runs out, which is what makes failing a
|
|
* quest something the player sees rather than something that happens to the journal.
|
|
*
|
|
* The scenes go through UDialogueSceneSubsystem, which is what holds the player still for the
|
|
* length of the sequence and gives the controls back the moment it stops. One scene at a time is
|
|
* played, so a quest that changes stage twice in a single frame shows the first scene it asked for
|
|
* and stays quiet about the second.
|
|
*/
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Cutscene")
|
|
TMap<EQuestStatus, TSoftObjectPtr<ULevelSequence>> StatusScenes;
|
|
|
|
/** The steps, in the order the journal lists them. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Objectives", meta = (TitleProperty = "Description"))
|
|
TArray<FQuestObjective> Objectives;
|
|
|
|
/** When set, a step starts listening only once the step before it is done. Off by default, so a
|
|
* quest that asks for three separate things reads as one list of three rather than as a chain. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Objectives")
|
|
bool bSequentialObjectives = false;
|
|
|
|
/** The items a DeliverItems step asks for are taken out of the inventory at the moment the quest
|
|
* is handed in, which is what makes a delivery a delivery. Turn it off for a quest that only
|
|
* wants to see the item and give it back. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Objectives")
|
|
bool bConsumeDeliveredItems = true;
|
|
|
|
/** Everything that has to be true before this quest can be offered. A prerequisite quest is one of
|
|
* these rather than a field of its own: "the fisherman's request", Completed. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Requirements", meta = (ExcludeBaseStruct, BaseStruct = "/Script/ProjectEleri.GameEventRequirement"))
|
|
TArray<FInstancedStruct> OfferRequirements;
|
|
|
|
/** Checked a second time when the quest is handed in, for a quest whose conditions can lapse
|
|
* between finishing the last step and getting back to the giver. */
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Requirements", meta = (ExcludeBaseStruct, BaseStruct = "/Script/ProjectEleri.GameEventRequirement"))
|
|
TArray<FInstancedStruct> TurnInRequirements;
|
|
|
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Rewards")
|
|
FQuestRewardBundle Rewards;
|
|
|
|
/**
|
|
* True when everything the quest asks for before it can be taken holds right now.
|
|
*
|
|
* TestContext is the player character, the same context the event system hands its requirements, so
|
|
* a condition written for one system behaves identically in the other.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Eleri|Quest")
|
|
bool AreOfferRequirementsMet(const UObject* TestContext) const;
|
|
|
|
/** Checked when the quest is handed in rather than when it is offered */
|
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Eleri|Quest")
|
|
bool AreTurnInRequirementsMet(const UObject* TestContext) const;
|
|
|
|
/** The step with this id, or null. Objective ids are unique inside one quest. */
|
|
const FQuestObjective* FindObjective(FName ObjectiveId) const;
|
|
|
|
/** The step with this id and where it sits in Objectives, or INDEX_NONE */
|
|
int32 FindObjectiveIndex(FName ObjectiveId) const;
|
|
|
|
/** True when the quest asks the player to hand anything over */
|
|
bool HasDeliverStep() const;
|
|
|
|
/** Every step that has to be done for the quest to be handed in. Hidden and optional steps are
|
|
* left out, since neither of them can hold a quest back. */
|
|
int32 CountRequiredObjectives() const;
|
|
|
|
/** True when the step at Index may run: everything before it is done when the quest asks for its
|
|
* steps in order, and it is free to run when it does not. */
|
|
bool IsObjectiveUnlocked(int32 Index, const FQuestRuntimeState& State) const;
|
|
|
|
/** The items every finished DeliverItems step is asking for, resolved to what the inventory files
|
|
* them under. Empty when there is nothing to hand over. */
|
|
void CollectDeliverables(const FQuestRuntimeState& State, TArray<TPair<FPrimaryAssetId, int32>>& OutItems) const;
|
|
|
|
/** Says whether the asset is worth offering at all: a quest with no tag, no steps or a duplicate
|
|
* step id would otherwise fail quietly in the middle of a playthrough. */
|
|
bool IsValidQuest(FString& OutError) const;
|
|
};
|