Create designated tutorial class in the GUI

This keeps messags.js shorter, prevents unnecessary GetGUIObjectByName
calls and, most importantly, makes it a lot more extensible for the
future.
This commit is contained in:
Vantha
2026-02-24 16:07:05 +01:00
committed by Vantha
parent 0e6e0280fc
commit 663f5c5ff6
6 changed files with 79 additions and 76 deletions
@@ -9,7 +9,7 @@
</object>
<object hotkey="session.gui.tutorial.toggle">
<action on="Press">toggleTutorial();</action>
<action on="Press">g_Tutorial?.toggle();</action>
</object>
<object hotkey="silhouettes">
@@ -1,13 +1,3 @@
/**
* All tutorial messages received so far.
*/
var g_TutorialMessages = [];
/**
* GUI tags applied to the most recent tutorial message.
*/
var g_TutorialNewMessageTags = { "color": "255 226 149" };
/**
* The number of seconds we monitor to rate limit flares.
*/
@@ -174,7 +164,7 @@ var g_NotificationsTypes =
},
"tutorial": function(notification, player)
{
updateTutorial(notification);
g_Tutorial.update(notification);
},
"tribute": function(notification, player)
{
@@ -335,7 +325,7 @@ function findGuidForPlayerID(playerID)
/**
* Processes all pending notifications sent from the GUIInterface simulation component.
*/
function handleNotifications(closePageCallback)
function handleNotifications()
{
for (const notification of Engine.GuiInterfaceCall("GetNotifications"))
{
@@ -346,7 +336,7 @@ function handleNotifications(closePageCallback)
}
for (const player of notification.players)
g_NotificationsTypes[notification.type](notification, player, closePageCallback);
g_NotificationsTypes[notification.type](notification, player);
}
}
@@ -365,57 +355,6 @@ function focusAttack(attack)
}
}
function toggleTutorial()
{
const tutorialPanel = Engine.GetGUIObjectByName("tutorialPanel");
tutorialPanel.hidden = !tutorialPanel.hidden || !Engine.GetGUIObjectByName("tutorialText").caption;
}
/**
* Updates the tutorial panel when a new goal.
*/
function updateTutorial(notification, closePageCallback)
{
// Show the tutorial panel if not yet done
Engine.GetGUIObjectByName("tutorialPanel").hidden = false;
if (notification.warning)
{
Engine.GetGUIObjectByName("tutorialWarning").caption = coloredText(translate(notification.warning), "orange");
return;
}
const notificationText =
notification.instructions.reduce((instructions, item) =>
instructions + (typeof item === "string" ? translate(item) : colorizeHotkey(translate(item.text), item.hotkey)),
"");
Engine.GetGUIObjectByName("tutorialText").caption = g_TutorialMessages.concat(setStringTags(notificationText, g_TutorialNewMessageTags)).join("\n");
g_TutorialMessages.push(notificationText);
if (notification.readyButton)
{
Engine.GetGUIObjectByName("tutorialReady").hidden = false;
if (notification.leave)
{
Engine.GetGUIObjectByName("tutorialWarning").caption = translate("Click to quit this tutorial.");
Engine.GetGUIObjectByName("tutorialReady").caption = translate("Quit");
Engine.GetGUIObjectByName("tutorialReady").onPress = () =>
{
closePageCallback({ [Engine.openRequest]: endGame(true) });
};
}
else
Engine.GetGUIObjectByName("tutorialWarning").caption = translate("Click when ready.");
}
else
{
Engine.GetGUIObjectByName("tutorialWarning").caption = translate("Follow the instructions.");
Engine.GetGUIObjectByName("tutorialReady").hidden = true;
}
}
/**
* Process every CNetMessage (see NetMessage.h, NetMessages.h) sent by the CNetServer.
* Saves the received object to mainlog.html.
@@ -35,6 +35,7 @@ var g_ResearchProgress;
var g_TimeNotificationOverlay;
var g_TopPanel;
var g_TradeDialog;
var g_Tutorial;
/**
* Map, player and match settings set in game setup.
@@ -284,6 +285,7 @@ async function init(initData, hotloadData)
g_DiplomacyColors = new DiplomacyColors();
g_PlayerViewControl = new PlayerViewControl();
g_PlayerViewControl.registerViewedPlayerChangeHandler(g_DiplomacyColors.updateDisplayedPlayerColors.bind(g_DiplomacyColors));
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates);
g_DiplomacyColors.registerDiplomacyColorsChangeHandler(g_PlayerViewControl.rebuild.bind(g_PlayerViewControl));
g_DiplomacyColors.registerDiplomacyColorsChangeHandler(updateGUIObjects);
g_PauseControl = new PauseControl();
@@ -339,18 +341,17 @@ async function init(initData, hotloadData)
handleNetMessages().catch(reject);
}), new Promise(closePageCallback =>
{
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates.bind(undefined,
closePageCallback));
g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat, closePageCallback);
g_NetworkStatusOverlay = new NetworkStatusOverlay(closePageCallback);
g_QuitConfirmationDefeat = new QuitConfirmationDefeat(closePageCallback);
g_QuitConfirmationReplay = new QuitConfirmationReplay(closePageCallback);
// TODO: use event instead
onSimulationUpdate(closePageCallback);
Engine.GetGUIObjectByName("session").onSimulationUpdate =
onSimulationUpdate.bind(undefined, closePageCallback);
g_Tutorial = new Tutorial(closePageCallback);
})]);
// TODO: use event instead
onSimulationUpdate();
Engine.GetGUIObjectByName("session").onSimulationUpdate = onSimulationUpdate;
for (const handler of g_PlayersInitHandlers)
handler();
@@ -472,14 +473,14 @@ function initializeMusic()
global.music.setState(global.music.states.PEACE);
}
function resetTemplates(closePageCallback)
function resetTemplates()
{
// Update GUI and clear player-dependent cache
g_TemplateData = {};
Engine.GuiInterfaceCall("ResetTemplateModified");
// TODO: do this more selectively
onSimulationUpdate(closePageCallback);
onSimulationUpdate();
}
/**
@@ -677,7 +678,7 @@ function onTick()
Engine.GuiInterfaceCall("ClearRenamedEntities");
}
function onSimulationUpdate(closePageCallback)
function onSimulationUpdate()
{
// Templates change depending on technologies and auras, so they have to be reloaded after such a change.
// g_TechnologyData data never changes, so it shouldn't be deleted.
@@ -705,7 +706,7 @@ function onSimulationUpdate(closePageCallback)
handler();
// TODO: Move to handlers
handleNotifications(closePageCallback);
handleNotifications();
updateGUIObjects();
}
@@ -20,6 +20,7 @@
<script directory="gui/session/top_panel/"/>
<script directory="gui/session/top_panel/IconButtons/"/>
<script directory="gui/session/trade/"/>
<script directory="gui/session/tutorial/"/>
<object name="session">
@@ -52,7 +53,7 @@
<include file="gui/session/TimeNotificationOverlay.xml"/>
<include file="gui/session/TopPanel.xml"/>
<include file="gui/session/trade/TradeDialog.xml"/>
<include file="gui/session/tutorial_panel.xml"/>
<include file="gui/session/tutorial/Tutorial.xml"/>
<include file="gui/session/Menu.xml"/>
<!-- Contains miscellanious objects s.a.: the technology research -->
@@ -0,0 +1,62 @@
class Tutorial
{
panel = Engine.GetGUIObjectByName("tutorialPanel");
text = Engine.GetGUIObjectByName("tutorialText");
warning = Engine.GetGUIObjectByName("tutorialWarning");
readyButton = Engine.GetGUIObjectByName("tutorialReady");
instructions = [];
closePageCallback;
constructor(closePageCallback)
{
this.closePageCallback = closePageCallback;
}
toggle()
{
this.panel.hidden = !this.panel.hidden || !this.text.caption;
}
update(notification)
{
this.panel.hidden = false;
if (notification.warning)
{
this.warning.caption = coloredText(translate(notification.warning), "orange");
return;
}
const notificationText =
notification.instructions.reduce((instructions, item) =>
instructions + (typeof item === "string" ? translate(item) : colorizeHotkey(translate(item.text), item.hotkey)),
"");
this.text.caption = this.instructions.concat(setStringTags(notificationText, this.NewInstructionTags)).join("\n");
this.instructions.push(notificationText);
if (notification.readyButton)
{
this.readyButton.hidden = false;
if (notification.leave)
{
this.warning.caption = translate("Click to quit this tutorial.");
this.readyButton.caption = translate("Quit");
this.readyButton.onPress = () => { this.closePageCallback({ [Engine.openRequest]: endGame(true) }); };
}
else
this.warning.caption = translate("Click when ready.");
}
else
{
this.warning.caption = translate("Follow the instructions.");
this.readyButton.hidden = true;
}
}
}
/**
* GUI tags applied to the most recent instruction.
*/
Tutorial.prototype.NewInstructionTags = { "color": "255 226 149" };