// 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 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> StatusScenes; /** The steps, in the order the journal lists them. */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Objectives", meta = (TitleProperty = "Description")) TArray 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 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 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>& 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; };