QuestManager.ts
, which does the following:Tip: All quest management code is maintained in a single file for easy access and reference. This pattern is used for other systems in the world.
Entity | Description |
---|---|
Quest board | The Quest gizmo is a panel that can be positioned in the world. It always displays the available quests in the world and their status for the player who is viewing the board. Tip: After you deploy this gizmo, you don’t need to do much to configure or maintain it. |
Quests | Quests are data entities that you create through the Systems menu in the Desktop Editor. A quest is some descriptive text for the player to be displayed in the Quest board, as well as the type of quest: Simple or Tracked (which means it is governed by a persistent variable). These quest types are discussed below. |
Script | In TypeScript, you must create the mechanisms for tracking and resolving the quests in your world. In this tutorial world, QuestManager.ts provides a simple model for managing quests. It is described in this module. |
Note: In this world, all quests are of Simple type. Progress toward completion of these quests is not maintained over multiple sessions in the world.
Tip: These types of quests are great for indicating to the player progress toward the goal. For example, the quests in the tutorial world that track the collection of multiple gems could be converted to quests of Tracked type, which are tied to a persistent variable. However, the implementation of Simple persistent variables was easier in this case.
AND
or logical OR
.Tip: When building Tracked quests, you should start simple.
Quest Name | Description |
---|---|
QuestCollect1Coin | Collect a single coin. This quest requires engagement with the gem trader interface. |
QuestCollect1Gem | Collect a single gem. This quest is designed to introduce players to the questing system. |
QuestCollect5Gems | Collect five gems in a single run of the game. This quest requires beating the Village Elder and the Traveling Merchant to each gem. |
QuestCollect15Gems | Collect a total of 15 gems. This quest requires replay of the game multiple times. |
QuestCollect1RedGem | Collect a single red gem. This gem can be collected if you trade some green gems for a coin and then trade the coin for the gem in the kiosk. |
export enum QuestNames {
'QuestCollect1Coin', // trade 2 gems for 1 coin in the kiosk
'QuestCollect1Gem', // collect 1 gem.
'QuestCollect5Gems', // collect five gems in the world
'QuestCollect15Gems', // collect 15 total gems (requires replay)
'QuestCollect1RedGem', // collect 1 red gem (requires collecting coins and trading them at the kiosk)
};
QuestManager.ts
, the following events are defined at the top of the file:export const questComplete = new hz.LocalEvent<{player: hz.Player, questName: QuestNames}>('questComplete');
export const questReset = new hz.LocalEvent<{player: hz.Player, questName: QuestNames}>('questReset');
export const questBoardUpdate = new hz.LocalEvent<{}>('questBoardUpdate');
NPCManager.ts
script, the gem collection quests are completed using the following code:if (!isNPC(ps.player)) {
if ((ps.gemsCollected >= 1) && (ps.player.hasCompletedAchievement('QuestCollect1Gem') == false)) {
this.sendLocalBroadcastEvent( questComplete, {player: ps.player, questName: QuestNames.QuestCollect1Gem } );
} else if ((ps.gemsCollected >= 5) && (ps.player.hasCompletedAchievement('QuestCollect5Gems') == false)) {
this.sendLocalBroadcastEvent( questComplete, {player: ps.player, questName: QuestNames.QuestCollect5Gems } );
}
}
ps.player
) is checked to see if it is a human player. If so, then the quest logic is triggered:
ps.gemsCollected
) is greater than or equal to 1
and the quest has not been completed:
questComplete
event.questComplete
event requires two pieces of data: 1) the player (ps.player
) and 2) the quest to complete (a reference to the enum item QuestNames.QuestCollect1Gem
).5
:
questComplete
event is broadcast, including the player and the QuestNames.Collect5Gems
enumerated value.start()
method of the QuestManager.ts
script, one for each of the defined events:// listener for questComplete event.
this.connectLocalBroadcastEvent(questComplete, (data:{player: hz.Player, questName: QuestNames}) => {
this.completeQuest(data.player, data.questName);
});
// listener for questReset event.
this.connectLocalBroadcastEvent(questReset, (data:{player: hz.Player, questName: QuestNames}) => {
this.resetQuest(data.player, data.questName);
});
// listener for questBoardUpdate event.
this.connectLocalBroadcastEvent(questBoardUpdate, ({}) => {
this.questBoardUpdate();
});
questComplete
event simply passes the input data (player, quest) to a local function (this.completeQuest
) for processing: public completeQuest(player: hz.Player, questName: QuestNames): void {
if (isNPC(player) == false) {
let qValue = QuestNames[questName]
if (player.hasCompletedAchievement(qValue) == false) {
player.setAchievementComplete(qValue, true)
console.log("Quest " + qValue + " complete for " + player.name.get()+"!")
this.world.ui.showPopupForPlayer(player, 'Quest Complete!',2)
if (this.props.sfxQuestWin) {
this.props.sfxQuestWin.as(hz.AudioGizmo).play()
}
this.async.setTimeout(() => {
if (this.checkAllQuestsCompleted(player) == true) {
if (this.props.vfxAllQuestsWin) {
let myVFX: hz.ParticleGizmo = this.props.vfxAllQuestsWin.as(hz.ParticleGizmo)
myVFX.play()
}
if (this.props.sfxAllQuestsWin) {
let mySFX: hz.AudioGizmo = this.props.sfxAllQuestsWin.as(hz.AudioGizmo)
mySFX.play()
}
}
}, 3000);
}
}
}
completeQuest
function:player
is human and if the quest is currently set to false
:if (player.hasCompletedAchievement(qValue) == false) {
player.setAchievementComplete(qValue, true)
setAchievementComplete
API endpoint receives the name of the quest (contained in qValue
) and its new state (true
) as parameters. Since it is triggered off of the player
object, it already knows the player to which to apply the change.checkAllQuestsCompleted()
to evaluate if the player has completed all quests. If so, special VFX and sound effects are played on loop.Note: After a quest is complete, it remains completed unless the developer resets a quest.
Note: This resets all quest data for the current player.
QuestResetTrigger.ts
scriptQuestResetTrigger.ts
contains a CodeBlockEvent listener: start() {
this.connectCodeBlockEvent(
this.entity,
hz.CodeBlockEvents.OnPlayerEnterTrigger,
(enteredBy: hz.Player) => {
const keys = Object.keys(QuestNames)
keys.forEach((key,value) => {
if ((value.valueOf() == 0) && (value.toString() != "0") && (value.toString() != "undefined") )
console.log("Resetting quest: " + value)
this.sendLocalBroadcastEvent( questReset, {player: enteredBy, questName: value } );
})
}
)
}
QuestNames
enum. In this case, the iteration returns values that are text names (good) and array index values (not useful). So, if the returned value
is a text value, then the embedded questReset
event is broadcast. Similar to the questComplete
event, this one takes the player and the name of the quest as parameters. By iterating through all of the text names for the quests, all available quests can be reset in this manner.QuestManager.ts
, the received event triggers the resetQuest
function for the player and the named event:public resetQuest(player: hz.Player, questName: QuestNames): void {
let qValue = QuestNames[questName]
if (qValue != undefined ) {
console.log("Resetting quest: " + qValue)
player.setAchievementComplete(qValue, false)
}
}
questBoardUpdate
event is defined, and its listener executes the questBoardUpdate()
function:public questBoardUpdate(): void {
if (this.props.questBoard) {
let myQuests: string[] = []
const keys = Object.keys(QuestNames)
keys.forEach((key,value) => {
if (key) {
myQuests.push(key.toString())
}
})
if (myQuests.length > 0) {
let myBoard: hz.AchievementsGizmo = this.props.questBoard.as(hz.AchievementsGizmo)
myBoard.displayAchievements(myQuests)
console.log("Quest board displaying these quests:" + myQuests.toString())
} else {
console.error("No quests to display!")
}
}
}
questNames
enum into a string array and passes this string to the following:let myBoard: hz.AchievementsGizmo = this.props.questBoard.as(hz.AchievementsGizmo)
myBoard.displayAchievements(myQuests)
questBoard
property) to display the set of quests in the string array myQuests
, as well as their current status for the player, effectively refreshing the board.