UE5 Quest System
This page is part of the documentaiton for my UE5 Quest System

Quest Events

UE5 Quest System Version: 2.0

    What are Quest Events?

    Quest events allow you to inject custom logic into your game at specific stages of a quest. You create these events using blueprints, then associate them with quests in your quest data table.

    There are three key points within the quest system where quest events can be integrated:

    On Quest State Change - This triggers an event when a quest transitions to a different state (e.g., from 'In Progress' to 'Completed').
    On Objective Completed - This event occurs when a player completes a quest objective, allowing you to execute specific actions or changes in the game world.
    When the Player Abandons a Quest - This event is triggered when a player decides to abandon a quest from their quest log, potentially allowing for cleanup or other responses to the player's decision. By default the quest system will try and run the Failed events if they exist when a quest is dropped. You can change this to run another states' events in the quest options.

    Quest Event Structure

    When you define a quest event each definition includes the following components:

    Event Handler - specifies the blueprint class that will execute when the event is triggered. See the included examples (outlined below) to see how these handlers can be set up and utilized.
    Event Data - is a key-value pair that feeds specific data to the event we are launching. By defining values within your event data, you can craft events that adapt dynamically based on the provided data. The included examples demonstrate how to leverage EventData to create versatile and dynamic events.
    Run On Reload - if set to true, the event will trigger again upon reloading the game, provided the conditions that originally triggered the event are still met. This feature ensures that certain events can persist or be re-initiated across game sessions, maintaining consistency and continuity in the quest experience.

    Create a new Quest Event Blueprint

    To create a new quest event, follow these steps:

    1. Start by making a child of the BP_QuestEvent located in the Blueprints/QuestEvents/ folder.
    2. In your new child blueprint, you'll need to override the OnQuestEvent event. This is the key event that gets triggered when your event is launched.

    Review the variables below for an outline of what is included in this blueprint class.

    Variables included in Quest Event Blueprints

    When you're setting up your quest event blueprint, you'll have access to a set of predefined variables inherited through the BP_QuestEvent base class. These variables are designed to help you tailor the event's behavior and interactions within your quest system. Utilize these variables to store and manipulate data relevant to the specific event you're creating, ensuring that your quest events are both dynamic and integrated seamlessly within your game's quests.

    autoDestroy? - This boolean variable determines whether the event should automatically be destroyed after the OnQuestEvent function has executed. By default, it's set to true, meaning that the event will clean itself up and be removed from the game once its logic has been executed. If you set autoDestroy? to false, the event will persist even after the onQuestEvent function has run. This setting is useful if your event involves ongoing or delayed logic that should not be terminated immediately after the initial trigger (like our Quest Timer example). When you choose not to auto-destroy the event, you're taking on the responsibility to explicitly call questEventDone within your event's logic when you determine the event's purpose has been fulfilled. Calling questEventDone will ensure the event is properly cleaned up and destroyed, preventing memory leaks or unintended behavior in your game.
    EventData - This variable stores the key-value pairs defined in your quest data table for a specific event. These pairs are used to pass dynamic data into the event, allowing for customizable and flexible event logic based on the specific needs of each quest or quest event instance. When you define an event in the data table, you'll specify these key-value pairs as part of the event's configuration. Then, within the event blueprint, you can access EventData to tailor the event's behavior based on these values. For example, if your event is designed to spawn a specific number of enemies, the "number" could be a value passed through EventData. Effectively, EventData enables you to create more generic, reusable event blueprints that can behave differently based on the data provided, enhancing the modularity and scalability of the quest system in your project.
    isPersistent? - When set to true, this indicates that the event is linked to the "Run on Reload" functionality. This means that if the game is reloaded and the conditions that initially triggered the event are still valid, the event will run again. This feature is particularly useful for creating events that need to maintain consistency across game sessions, ensuring that important quest-related actions are not missed or forgotten if a player exits and re-enters the game. If you decide to make an event persistent, it's important to carefully manage its state and conditions to ensure that it reactivates appropriately and does not lead to unintended consequences or repeated actions that were only meant to occur once. This value is read only, and here for your convenience if you need to know it. Changing it in the blueprint will do nothing.
    QuestRowName - This variable holds the name of the row in your quest data table that corresponds to the quest responsible for triggering this event. It acts as a key to connect the event logic to its relevant quest context, ensuring that the event knows which quest it is associated with and can behave accordingly. By referencing the QuestRowName, the event can access or modify the quest's state, check quest progress, or interact with other quest-specific variables and conditions. This connection is vital for ensuring that the event's actions are contextually relevant to the quest and its current state. Properly utilizing QuestRowName ensures that your event is not only triggered at the right time but also executes actions that are coherent and synchronized with the progress and requirements of the quest it's linked to.
    QuestState - This variable stores the current state of the quest when the event is initiated. The quest state could be any predefined state like 'Available', 'In Progress', 'Completed', 'Failed', etc. The significance of QuestState lies in its ability to provide context-sensitive behavior within the event logic. For example, an event might behave differently if triggered when a quest is 'In Progress' compared to when it's 'Completed'. Understanding the quest's state at the time of the event's initiation allows for more nuanced and responsive event logic, ensuring that the actions taken by the event are appropriate to the quest's current narrative and gameplay context. By referencing QuestState, the event can make informed decisions, tailor its responses, and execute actions that align with the player's progress and the overarching quest narrative in the game world.
    ObjectiveIndex - This variable indicates the index (or position) of the objective within the quest to which the event is linked. If the event is not directly tied to a specific objective, this variable will have a value of -1. On the other hand, if the event is related to an objective, ObjectiveIndex will hold a non-negative value corresponding to the objective's position in the quest's sequence of objectives. The value of ObjectiveIndex is crucial for events that are meant to trigger or respond to the progress of specific objectives. For instance, an event may need to check the completion status of an objective or perform an action when an objective reaches a certain state. By utilizing ObjectiveIndex, you can create more targeted and relevant event responses that directly interact with the specific objectives of a quest, allowing for a deeper and more integrated quest experience.
    LaunchedByPlayerState - This variable holds a reference to the player state that triggered the event. This connection is vital for events that need to interact with or retrieve information specific to the initiating player. Utilizing LaunchedByPlayerState allows your event to be context-sensitive and player-specific, enabling actions or changes that are tailored to the individual player's journey and status within the game. This reference can be used to access the quest player state component or to fetch the player controller and its quest components. By leveraging LaunchedByPlayerState, your event can enhance the player's experience by ensuring that the outcomes and actions of the event are directly relevant and specific to the player who triggered it, reinforcing the player's engagement and immersion in the game world.

    Quest Event - Spawn Actor From Class

    BP_QuestEvent_SpawnActorFromClass - Use this quest event to spawn an actor.

    EventData Variables:

    class - the reference path to your blueprint. For example: /Game/DynomegaQuestSystemV2/Demo/MapHelpers/BP_Butterflies.BP_Butterflies Take extra note of the double use of the file name at the end of the string.
    checkin - have the actor check in with the world helper (if it is a quest actor with objectives).
    helper - the tag on the spawn here helper we are using as our point of reference for spawn transform. if you do not use a helper it will spawn near the player.
    attach - if set will attach the spawned actor to the player.

    When using this quest event to dynamically load and spawn classes based on strings stored in a data table, it's vital to manually include the directories containing these classes in the "Additional Asset Directories to Cook" field within your project settings. This step is critical because when you package your game, the packaging system doesn't automatically detect and include classes referenced solely as strings in a data table. Without this manual inclusion, these classes won't be available in the packaged game, leading to issues where certain elements or actors don't spawn as intended.

    To ensure your actor classes used by Spawn Actor From Class are included in the final build when you package your project:

    1. Go to your project settings.
    2. Find the "Packaging" section.
    3. Locate the "Additional Asset Directories to Cook" field.
    4. Add the directories that contain the classes you reference in your data table.

    Quest Event - Destroy Actor with Tag

    BP_QuestEvent_DestroyActorWithTag - Use this quest event to destroy an actor with a tag.

    EventData Variables:

    tag - the tag of the actor(s)
    all - if set will destroy all, otherwise if omitted just the first detected will be destroyed

    Quest Event - Play Level Sequence

    BP_QuestEvent_PlayLevelSequenceWithTag - Use this quest event to play a level sequence actor in your level.

    EventData Variables:

    tag - the tag on the Level Sequence Actor you wish to play

    Quest Event - Give Item To Player

    BP_QuestEvent_GiveItemToPlayer - Use this quest event to give an item to the player. This uses the giveQuestRewardToPlayer Connected Systems BPI to keep it simple as if you are using this you are most likely using quest rewards and have this function setup to already give items to the player.

    EventData Variables:

    item - the row name of the item you want to give
    quantity - how many of the item you want to give

    Quest Event - Show Quest Window

    BP_QuestEvent_ShowQuestWindow - Use this quest event to show a quest window to the player.

    EventData Variables:

    quest - the row name of the quest you wish to show the window for.

    Quest Event - Simple Alert

    BP_QuestEvent_ShowSimpleAlert - Use this quest event to show a simple alert at the top of the player's viewport.

    EventData Variables:

    text - the text of your alert (required)
    duration - how long to show the alert for
    delay - the duration to wait before showing the alert.

    Quest Event - Quest Timers

    BP_QuestEvent_QuestTimer - Quest Timers allow you to set a specific time limit for players to complete a quest. When the timer is in use, players must finish the quest within the allotted time; otherwise, the quest's state will change to another state that you also specify. This change is often to mark the quest as failed or completed, depending on the nature of the quest and the timer's role within it.

    By integrating timers with quest events, you can introduce time-based challenges that add urgency and excitement to your quests, encouraging players to engage actively with the content and make strategic decisions under pressure.

    EventData Variables:

    action - the action to perform for the timer, acceptable values include:
    • start to start the quest timer.
      Make sure you also use Run on Reload? whenever you start a timer.
    • stop to stop the quest timer.
      If you are using this stop action from a Objective Complete Event and you are using Run on Reload? with your start action when you start the quest timer, you need to make sure to enable Run on Reload? for this event as well.
    • change to change the seconds to this value
    • add to add seconds to the current time remaining
    • remove to remove seconds from the current time remaining
    • update to perform multiple updates at once in one call.
    state - the quest state to change the quest to if this timer hits 0. Acceptable values are: failed, ready, completed.
    seconds - the number of seconds for this timer action. When used with the "start" action this will be the starting number of seconds. When used with "add" or "remove" it will increment or decrement the amount from the current time remaining. When used with the "change" action will change the current time remaining to this value.
    ui - if the key exists will show the timer on the player's UI. If a value is set the timer label on the UI will be set to this value.
    save - if the key exists the quest system will keep track of this timer using the save game for the player. This will let you persist the data across game restarts by the player. If a value is set the value is the number of seconds between saves. By default this value is set to 5.
    addseconds* - when used with the update action will add seconds to the current timer. Has no effect when used with any other actions.
    removeseconds* - when used with the update action will remove seconds from the current timer. Has no effect when used with any other actions.
    changeseconds* - when used with the update action will change the seconds remaining to this amount. Has no effect when used with any other actions.

    * - The "update" action is designed to handle updating multiple things about a timer at once, such as adding seconds and changing the state or UI message. If your only action is to add, remove, or change seconds to the current event you should use the associated action for that instead.


    Create a Quest Timer for your Quest

    Follow these steps to create a Quest Timer that starts via a Quest Event ...

    1. Navigate to the Quest State Events section for your quest in the DT_Quests Data Table.
    2. At this point you need to decide when you want the timer to start, this typically occurs in the "In Progress" quest state, which will start it as soon as the quest is accepted. If the "In Progress" key does not exist add it by clicking the Add Map Element next to Quest State Events.
    3. Expand the state section you added, for me that is "In Progress" since I want the timer to start as soon as the quest is accepted.
    4. Add an Array Element to the state's Events.
    5. Select the BP_QuestEvent_QuestTimer blueprint for the EventHandler.
    6. Expand the EventData section, then add the following variables (from above):

      1. action - with a value of start
      2. state - with a value of failed
      3. seconds - with a value of 35
      4. ui - with a value of My Example Timer
    7. If you want your quest timer to restart if the game restarts, enable the Run on Reload? boolean.
    8. You can also have your quest timer save, so instead of restarting it continues where it left off shortly before stopping. You can achieve this by enabling Run on Reload? and also by adding the save variable to the EventData. The value for this variable is the number of seconds between saves of the data, if you leave it blank it will save every 5 seconds.

    Now when your quest is accepted by the player you will see "My Example Timer" appear at the top of the Quest Tracking widget with a starting seconds of 35. When the timer reaches zero the state will be set to "Failed" for the quest.

    You can also initiate your quest timer when an objective is completed, however keep in mind that even on the objective level if the timer reaches zero, the state you define will be set for the quest.


    Timmy I think I found a bug Dyno! I added a Quest Timer event, but when I restart the game the timer is gone!

    Make sure you have the Run on Reload? boolean for the quest event starting the quest timer enabled in the Data Table. When true, this boolean lets our system know we want to restart this quest event if our quest is still in this state. Remember Timmy, our Quest Timers are built on top of the Quest Event system.

    You need to make sure you have this enabled on any Quest Event used to start the Quest Timer. However, it is not required on Quest Events used to perform any of the other Quest Timer actions (stop (except when you are using it as an objective complete event, then you should use run on reload), change, add, remove, update).


    Timmy Dyno - I added a Quest Timer event, but when I restart the game the timer is restarting instead of continuing where it left off ... how fix?

    You need to use the save event data key if you would like the timer to save the seconds remaining.

    Without this save key, and with Run on Reload? enabled, your quest timer will restart on each reload.

    By default it will save every 5 seconds, you can change this interval by setting a value to the key equal to the seconds you wish to use.

    You need to use both the Run on Reload? & save features to maintain this persistent state between restarts.


    Stopping Quest Timers

    By default quest timers will stop whenever the quest state changes to a state other than "In Progress". This is handled in AC_QuestSystem_PlayerState's internalQuestStateEventsHandler function.

    You can also stop a timer from an objective complete event by using the Quest Timer handler with the Event Data key action with a value of stop.


    How to stop a Quest Timer from another Blueprint

    The AC_QuestSystem_PlayerState component includes the QuestTimerSop function, which can be used to force a specific quest's timer to stop. This function expects the QuestRowName and ObjectiveIndex as inputs. If your quest timer is for the entire quest and not an objective you should leave the ObjectiveIndex at -1.

    Alternatively you can use the QuestTimerStopAll function, which will stop all timers. This function does not require a quest row name or objective index.


    How to Add/Remove Seconds for a Quest Timer from another Blueprint

    The AC_QuestSystem_PlayerState component includes a useful function named QuestTimerChangeSeconds, designed to modify the duration of an active quest timer. This function allows you to either add or subtract seconds from a timer and requires three inputs: QuestRowName, ObjectiveIndex, and Seconds. If your quest timer is for the entire quest and not an objective you should leave the ObjectiveIndex at -1.

    Alternatively there is also QuestTimerChangeSecondsAll, which will change the seconds on any active timer, and does not require the quest or objective.

    Under the surface this function is just dynamically creating a quest event to add seconds. When removing seconds you would just use a negative value.

    For instance, in our demo world, there's a repeatable quest where destroying a cupcake adds 5 seconds to the remaining time. This adjustment is made by invoking the QuestTimerChangeSecondsAll function, demonstrating a practical application of dynamically altering quest timers based on in-game actions.


    Starting Quest Timers from another Blueprint

    Since quest timers are a feature unique to quests, there is no included way to externally start them, and they should only be started through the provided Quest Events points. The provided example functions mentioned above and the requirements for creating a timer should give you enough examples to create this functionality if you really need it in your game, but if you can't figure it out and need help feel free to hop in my discord and state your need and use case.

    Multiplayer Notes

    When designing quest events in a multiplayer game, it's crucial to ensure that the events don't inadvertently impact other players, particularly when events involve spawning actors or modifying level elements. Here are some key considerations and strategies:

    Multiplayer Considerations

    Remember that in a multiplayer setting, multiple players might trigger the same events simultaneously or sequentially. It's essential to design your events to handle such scenarios gracefully to avoid unwanted duplications or interactions that could affect gameplay balance or player experience.

    Singleton Actors

    In the case of objects that should only exist once in the world, like the button in the demo world example, you need mechanisms to check for existing instances and prevent multiple spawns. The button checks for other instances and self-destructs if others are found, ensuring that only one instance is present for all players.

    Qualification Checks

    For interactive elements like the button mentioned previously, it's vital to incorporate checks to ensure that only qualified players can activate or use them. This prevents unqualified players from triggering events or actions they shouldn't access.

    Server and Client Logic

    The onQuestEvent event runs on the server, which is suitable for most game logic in a multiplayer environment. However, if you need to update the client-side UI or perform other client-specific actions, you'll need to trigger client-executed events or functions from the server-side event.

    Replication and Authority

    Ensure that your quest events respect the network authority principles. Generally, the server should handle critical game logic, including the spawning and destruction of shared game elements, to maintain consistency across clients.

    By keeping these points in mind and implementing appropriate checks and balances, you can ensure that your quest events enhance the multiplayer experience, maintaining both gameplay integrity and individual player progression.

    This documentation and asset version are new. If you encounter any bugs or if anything doesn't make sense, please let me know.