Revise tutorial step handling

- Remove the delay functionality from the tutorial. It was unused and
  that for a reason. Switching to the next step after a fixed amount of
  time is never wanted. It was only used to force-show the ready button
  in one case, but a designated bool communicates the purpose better.
- Rename the "ready" button to "continue" as it fits better.
- Rename "leave" to "isLast" as it's more descriptive.
- Rename the "warning" object of the instruction panel to "hint" as it's
  not always display warnings, and move its captions to the class
  prototype like the coding conventions state.
- Simplify the logic in NextStep a bit to make it more readable.
This commit is contained in:
Vantha 2026-02-25 17:17:49 +01:00 committed by Vantha
parent f3b71a1fbf
commit 702e3f1113
4 changed files with 41 additions and 45 deletions

View file

@ -6,17 +6,17 @@ class InstructionPanel
{
panel = Engine.GetGUIObjectByName("instructionPanel");
text = Engine.GetGUIObjectByName("instructionPanelText");
warning = Engine.GetGUIObjectByName("instructionPanelWarning");
readyButton = Engine.GetGUIObjectByName("instructionPanelReady");
hint = Engine.GetGUIObjectByName("instructionPanelHint");
continueButton = Engine.GetGUIObjectByName("instructionPanelContinueButton");
instructions = [];
closePage;
constructor(closePage)
{
this.closePage = closePage;
this.readyButton.onPress = () =>
this.continueButton.onPress = () =>
{
Engine.PostNetworkCommand({ "type": "dialog-answer", "tutorial": "ready" });
Engine.PostNetworkCommand({ "type": "dialog-answer", "tutorial": "continue" });
};
}
@ -27,7 +27,7 @@ class InstructionPanel
displayWarning(warning)
{
this.warning.caption = setStringTags(warning, this.WarningTags);
this.hint.caption = setStringTags(warning, this.WarningTags);
}
displayStep(panelData)
@ -35,22 +35,22 @@ class InstructionPanel
this.text.caption = this.instructions.concat(setStringTags(panelData.text, this.NewInstructionTags)).join("\n");
this.instructions.push(panelData.text);
if (panelData.readyButton)
if (panelData.showContinueButton)
{
this.readyButton.hidden = false;
if (panelData.leave)
this.continueButton.hidden = false;
if (panelData.isLast)
{
this.warning.caption = translate("Click to quit this tutorial.");
this.readyButton.caption = translate("Quit");
this.readyButton.onPress = this.closePage;
this.hint.caption = translate("Click to quit this tutorial.");
this.continueButton.caption = translate("Quit");
this.continueButton.onPress = this.closePage;
}
else
this.warning.caption = translate("Click when ready.");
this.hint.caption = this.HintCaptions.Continue;
}
else
{
this.warning.caption = translate("Follow the instructions.");
this.readyButton.hidden = true;
this.hint.caption = this.HintCaptions.Instruction;
this.continueButton.hidden = true;
}
}
}
@ -64,3 +64,9 @@ InstructionPanel.prototype.NewInstructionTags = { "color": "255 226 149" };
* Tags applied to warning messages.
*/
InstructionPanel.prototype.WarningTags = { "color": "orange" };
InstructionPanel.prototype.HintCaptions = {
"Continue": translate("Click when continue."),
"Instruction": translate("Follow the instructions."),
"Quit": translate("Click to quit this tutorial.")
};

View file

@ -3,8 +3,8 @@
<object size="10 10 100%-20 100%-48">
<object name="instructionPanelText" type="text" style="TutorialPanel"/>
</object>
<object name="instructionPanelWarning" type="text" style="ModernLeftLabelText" size="10 100%-42 100%-20 100%-12"/>
<object name="instructionPanelReady" type="button" style="ModernButtonRed" size="100%-160 100%-42 100%-20 100%-12">
<translatableAttribute id="caption">Ready</translatableAttribute>
<object name="instructionPanelHint" type="text" style="ModernLeftLabelText" size="10 100%-42 100%-20 100%-12"/>
<object name="instructionPanelContinueButton" type="button" style="ModernButtonRed" size="100%-160 100%-42 100%-20 100%-12">
<translatableAttribute id="caption">Continue</translatableAttribute>
</object>
</object>

View file

@ -39,16 +39,11 @@ Trigger.prototype.NextStep = function(deserializing = false)
if (this.index > this.tutorialSteps.length)
return;
const step = this.tutorialSteps[this.index];
let needDelay = true;
let readyButton = false;
Trigger.prototype.Init = step.Init || null;
if (!deserializing && this.Init)
this.Init();
Trigger.prototype.IsDone = step.IsDone || (() => false);
const stepAlreadyDone = this.IsDone();
for (const event of this.tutorialEvents)
{
const action = event + "Trigger";
@ -56,36 +51,31 @@ Trigger.prototype.NextStep = function(deserializing = false)
{
Trigger.prototype[action] = step[event];
this.EnableTrigger(event, action);
if (!stepAlreadyDone)
needDelay = false;
}
else
this.DisableTrigger(event, action);
}
// Steps without actions to be performed by the player must have
// - either the property delay (a value > 0 to wait for a given time, and -1 to display the Ready button)
// - or no trigger functions (needDelay will be set automatically to true and the Ready button displayed)
if (step.delay || needDelay)
Trigger.prototype.IsDone = step.IsDone || (() => false);
const showContinueButton =
step.panelData.showContinueButton === undefined ?
this.IsDone() || this.tutorialEvents.every(event => !step[event]) :
step.panelData.showContinueButton;
if (showContinueButton)
{
if (step.delay && step.delay > 0)
this.DoAfterDelay(+step.delay, "NextStep", {});
else
this.EnableTrigger("OnPlayerCommand", "OnPlayerCommandTrigger");
Trigger.prototype.OnPlayerCommandTrigger = function(msg)
{
this.EnableTrigger("OnPlayerCommand", "OnPlayerCommandTrigger");
Trigger.prototype.OnPlayerCommandTrigger = function(msg)
{
if (msg.cmd.type == "dialog-answer" && msg.cmd.tutorial && msg.cmd.tutorial == "ready")
this.NextStep();
};
readyButton = true;
}
if (msg.cmd.type == "dialog-answer" && msg.cmd.tutorial && msg.cmd.tutorial == "continue")
this.NextStep();
};
}
this.DisplayStep(step, readyButton, ++this.index == this.tutorialSteps.length);
this.DisplayStep(step, showContinueButton, ++this.index == this.tutorialSteps.length);
};
Trigger.prototype.DisplayStep = function(step, readyButton = false, leave = false)
Trigger.prototype.DisplayStep = function(step, showContinueButton = false, isLast = false)
{
const cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification({
@ -95,8 +85,8 @@ Trigger.prototype.DisplayStep = function(step, readyButton = false, leave = fals
"type": step.type,
"panelData": {
...step.panelData,
"readyButton": readyButton,
"leave": leave
"showContinueButton": showContinueButton,
"isLast": isLast
}
}
});

View file

@ -274,9 +274,9 @@ Trigger.prototype.tutorialSteps = [
"panelData": {
"text":
markForTranslation("You may notice that berries are a finite supply of food. We will need a more lasting food source. Fields produce an unlimited food resource, but are slower to gather than forageable fruits.\n") +
markForTranslation("But to minimize the distance between a farm and its corresponding food dropsite, we will first build a Farmstead.")
markForTranslation("But to minimize the distance between a farm and its corresponding food dropsite, we will first build a Farmstead."),
"showContinueButton": true
},
"delay": -1,
"OnOwnershipChanged": function(msg)
{
if (this.houseGoal.has(+msg.entity))