Start screen

I was playing a quest game on and saw

How would one make a start screen like this?

I'm actually the one who made that main menu you're referencing. I'll preface this by saying this is all relatively advanced coding for Quest, but if that doesn't deter you I'll go into greater detail.

The short answer is: On start-up, a Quest script generates custom HTML code (using the JS.eval() Quest function to run jQuery calls) to build the look of the menu and all its links. I also wrote up a custom JavaScript file containing functions for each link, so when the player clicks one of the menu buttons, javascript can then run accordingly. For buttons that need to interact directly with Quest, I can use the ASLEvent() function in the JavaScript file (tutorial on ASLEvent here) to do what I want.

The longer answer is this:

  1. On startup, Quest runs the following game.initMainMenu script:
// Initialize Main Menu
JS.eval ("$('#MainMenu').remove();")
game.MenuImageURL = GetFileURL("MenuJPG.jpg")
MenuContent = ""
// Generate Title
TitleFont = game.TitleFont
if (TitleFont="CollegiateFLFImport") {
  TitleFontSize = "360%"
else {
  TitleFontSize = "260%"
// Set this boolean to True if I plan on using Main Menu image background, and should thus display title at top of screen
TitleScreenImage = True
// Otherwise, the title will be displayed in the center of the screen in black
MenuContent = MenuContent + "<div id=\"MainMenu\" style=\"max-width:100vw\">"
MenuWrapper = "<div id=\"MainMenuWrapper\" style=\"display:table-cell; vertical-align:middle;position: absolute; width:98%; padding:5px;left: 50%;top: 50%;-webkit-transform: translate(-50%,-50%); -ms-transform: translate(-50%,-50%); transform: translate(-50%,-50%);\">"
MainMenuTitle = "<div id=\"MainMenuTitle\" style=\"text-align:center; font-size:"+TitleFontSize+";font-family:"+TitleFont+";\">"+game.gamename+"</div>"
if (game.subtitle <> null) {
  if (LengthOf(game.subtitle) > 0) {
    MainMenuTitle = MainMenuTitle + "<div id=\"MainMenuSubtitle\" style=\"text-align:center; font-size:130%;\">"+game.subtitle+"</div>"
if ( <> null) {
  if (LengthOf( > 0) {
    MainMenuTitle = MainMenuTitle + "<div id=\"MainMenuAuthor\" style=\"text-align:center; font-size:130%;\">by ""</div>"
if (TitleScreenImage) {
  // Displays Title at top of screen, rather than middle
  MenuContent = MenuContent + MainMenuTitle + MenuWrapper
else {
  // Display Title in middle of screen
  MenuContent = MenuContent + MenuWrapper + MainMenuTitle
MenuContent = MenuContent + "<br><br><br><br>"
// Generate Menu Options
MenuFillColor = "rgba(211,211,211,0.75)"
MenuContent = MenuContent + "<table id=\"MainMenuOptions\" style=\"text-align:center;vertical-align:middle;margin-left: auto; margin-right: auto;  width: 180px; border:solid; background-color:"+MenuFillColor+"; border-radius:5px; font-size:1.2em;\">"
MenuContent = MenuContent + "<tr><td style=\"padding:10px;\"><a class=\"cmdlink\" style=\"GetCurrentLinkTextFormat(); color:blue;\" onclick=showMainMenuModal(\"NewGame\")>New Game</a></td></tr>"
MenuContent = MenuContent + "<tr><td style=\"padding:10px;\"><a class=\"cmdlink\" style=\"GetCurrentLinkTextFormat(); color:blue;\" onclick=LoadGamePrompt()>Load Game</a></td></tr>"
MenuContent = MenuContent + "<tr><td style=\"padding:10px;\"><a class=\"cmdlink\" style=\"GetCurrentLinkTextFormat(); color:blue;\" onclick=showPopupJournal(\"fetishoptions\")>Fetish Options</a></td></tr>"
MenuContent = MenuContent + "<tr><td style=\"padding:10px;\"><a class=\"cmdlink\" style=\"GetCurrentLinkTextFormat(); color:blue;\" onclick=showMainMenuModal(\"Credits\")>Credits</a></td></tr>"
MenuContent = MenuContent + "</table></div>"
// Populate Bottom Corners
// MenuContent = MenuContent + "<div style=\"position: absolute; bottom: 5px; left: 5px;\">Social Media1<br>Social Media2</div>"
MenuContent = MenuContent + "<div style=\"position: absolute; bottom: 5px; right: 5px; -webkit-text-stroke: 0.8px rgb(54,54,54)\">v"+ToString(game.version)+"</div>"
MenuContent = MenuContent + "</div>"
JS.eval ("$('#gameBorder').append('"+MenuContent+"');")
// Application of Background image moved to the ShowMainMenu function
// If Asura tower used as main menu background, set font color to rgb(231,234,223), otherwise use Black
if (TitleScreenImage) {
  TitleFontColor = "rgb(231,234,223)"
  JS.setCss ("#MainMenuTitle", "color:"+TitleFontColor)
  JS.eval ("$('#MainMenuTitle').css('-webkit-text-stroke', '0.3px rgb(63,82,80)');")
  JS.setCss ("#MainMenuSubtitle", "color:"+TitleFontColor)
  JS.eval ("$('#MainMenuSubtitle').css('-webkit-text-stroke', '0.5px rgb(63,82,80)');")
  JS.setCss ("#MainMenuAuthor", "color:"+TitleFontColor)
  JS.eval ("$('#MainMenuAuthor').css('-webkit-text-stroke', '0.5px rgb(63,82,80)');")
// Start the game with the Main Menu hidden
JS.eval ("$('#MainMenu').hide();")
  1. In the code snippet above, each menu link has an onclick event tied to it, linking to a JavaScript function. Those JavaScript functions are here in a custom JavaScript file I made and added to the game (on desktop, put the .js file in the same directory as your game, then import it using the Advanced tab in the editor):
// MainMenuJavaCode.js

function showMainMenuModal(optionalPage) {
	// NOTE: optionalPage should be a string
	//If mainmenu modal not already open, then open it...
  try {
	if (!!!document.getElementById("mainmenupopup") || !$('#msgboxMainMenu').dialog('isOpen')) {
      // Update height & width according to page.
	  if (typeof optionalPage !== 'undefined') {
		  if (optionalPage === "NewGame" || optionalPage === "NewGameWhileGameRunning") {
			var boxWidth = Math.min(400, $(window).width())
			var boxHeight = 'auto'
		  // Load Game option opens the load code modal. No need for special code here.
		  // Similarly, if fetish options is selected, it should be calling showPopupJournal('fetishoptions')
		  else if (optionalPage === "Credits") {
			var boxWidth = Math.min(560, $(window).width())
			var boxHeight = 'auto'
		  else {
		    var boxWidth = 'auto'
			var boxHeight = 'auto'
  	  else {
	    var boxWidth = Math.min(280, $(window).width())
	    var boxHeight = 'auto'
      var msgboxOptions = {
          modal: true,
          autoOpen: false,
		  resizable: false,
		  draggable: false,
          title: "MainMenu",
          width: boxWidth,
		  height: boxHeight,
	  	  show: "scale",
		  hide: "scale",
          buttons: [],
          closeOnEscape: false,
	  // Give the messagebox modal an ID to more easily identify whether or not it's open
	  // Make position of dialog box "fixed" rather than "absolute"
	  $('#mainmenupopup').css("position", "fixed");
	// Check if optionalPage parameter is set. If it is, open to that page, if not then open to main page
	if (typeof optionalPage !== 'undefined') {
		if (optionalPage =='NewGame' || optionalPage == 'NewGameWhileGameRunning') {
		  $('#msgboxMainMenu').dialog('option', 'title', 'Start New Game?');
		  if (optionalPage=='NewGame') {
		    $('#msgboxMainMenuCaption').html("<div style='font-size:1.1em'>Would you like to start a new game?</div>")
			$('#msgboxMainMenuCaption').append("<table style='width:100%;'><tr><b><td style='text-align:left; padding-left:30%;'><button style='font-size:1.1em;cursor:pointer;' onclick=NewGame('False');>Yes</button></td><td style='text-align:right; padding-right:30%;'><button style='font-size:1.1em;cursor:pointer;' onclick=CloseMainMenuModal();>No</button></td></b></tr></table>")
		  else {
		    $('#msgboxMainMenuCaption').html("<div style='font-size:1.1em'>Would you like to start a new game?</div><div>(WARNING: All unsaved progress will be lost!)</div>")
			$('#msgboxMainMenuCaption').append("<table style='width:100%;'><tr><b><td style='text-align:left; padding-left:30%;'><button style='font-size:1.1em;cursor:pointer;' onclick=NewGame('True');>Yes</button></td><td style='text-align:right; padding-right:30%;'><button style='font-size:1.1em;cursor:pointer;' onclick=CloseMainMenuModal();>No</button></td></b></tr></table>")
			// TODO: Logic for starting a new game if game already in progress...
		else if (optionalPage =='Credits') {
		  $('#msgboxMainMenu').dialog('option', 'title', 'Credits');
		  $('#msgboxMainMenu').dialog('option', 'buttons', [{text: 'Return', click: function() {$('#msgboxMainMenu').dialog('close');}}]);
		  var CreditsContent = "<p style='text-align:center'>Story & Code by Dredux (DeepDredux)<br><span style='font-size:0.8em'>Music by Eric Matyas (</span><br><br>Check out my social media for the latest updates!<table style='margin-left: auto; margin-right: auto;'> <tr><td style='text-align:right'><b>Twitter:</b></td><td><a href=''>@DeepDredux</a></td></tr></table>"
		  CreditsContent = CreditsContent + "<br><div style='text-align:center'>Consider supporting the developer!</div><table style='margin-left: auto; margin-right: auto;'><tr><td style='text-align:right'><b>Tip Jar:</b></td><td><a href=''></a></td></tr></table>"
		  CreditsContent = CreditsContent + "</p>"
	else {
	  // If modal page not defined close the modal
	// Rerun theme to update buttons and the like
  catch(ERROR) {

function NewGame(GameRunning) {
	// Start a new game
	if (GameRunning=='True') {
	else {
	$('body').css('cursor', 'wait');
	// $('#msgboxMainMenu').dialog('close'); // Moved this to the StartNewGame quest function so it only closes when it's actually done.

function CloseMainMenuModal() {

// 18+ Splash Screen Functions
function Hide18WarningScreen() {
  $('#18WarningScreen').append("<div id='18ThankYou' style='text-align:center'><br><b><i>Thank you. Enjoy your stay in Lillivale...</i> &#9786;</b></div>")
  setTimeout (function() {
    //$('#18WarningScreen').hide({ effect: "scale", origin: [ "bottom", "center" ], duration: 1000 });
    $('#18WarningScreen').hide({ effect: "fade", duration: 1000 , complete: function(){$('#18ThankYou').remove()} });
  }, 500)
  setTimeout (function () {
    // Unmute MenuAudio if present. Add delay equal to the start of the fadeout above.
	var audio1 = document.getElementById("MenuAudio1");
    if (typeof optionalPage !== 'audio1') {
	  // Restart audio from beginning;
	  audio1.currentTime = 0;
	  audio1.muted = false;
	var audio2 = document.getElementById("MenuAudio2");
    if (typeof optionalPage !== 'audio2') {
	  // Restart audio from beginning;
	  audio2.currentTime = 0;
	  audio2.muted = false;
  }, 500)

function Show18WarningScreen() {
    setTimeout (function () {
    // Mute MenuAudio if present. Add a delay to give the menu time to initialize
	var audio1 = document.getElementById("MenuAudio1");
	var audio2 = document.getElementById("MenuAudio2");
	if (typeof optionalPage !== 'audio1') {
      audio1.muted = true;
	if (typeof optionalPage !== 'audio2') {
      audio2.muted = true;
  }, 100)
  1. Then, because the menu is not always visible, I have another Quest function to show it on start-up, then hide it when the player moves on from it. I do this by setting up a special MainMenu room in Quest that will run the following ShowMainMenu() function whenever the player enters it (or starts in it) to show it, then hide the main menu again when they leave:
// Shows/hides the game's main menu
// game.initMainMenu contains the html initializing the main menu object
// Will Show the main menu if ShowMenu=True
// Will Hide the main menu if ShowMenu=False. ShowMainMenu(False) should really only be called when first starting the game or in the 'Continue Game' case where the main menu was pulled up mid-game
if (ShowMenu) {
  game.OnMainMenuFlag = True
  // JS.eval ("$('#gameBorder').hide();")
  JS.eval ("$('#txtCommandDiv').hide(); $('#gamePanesRunning').hide(); $('#divOutput').hide(); $('#BottomBar').hide(); $('#status').hide();")
  JS.eval ("$('#MainMenu').show();")
  JS.eval ("$('div#gameBorder').css('background-image', 'url(" + game.MenuImageURL + ")');")
  JS.eval ("$('div#gameBorder').css('background-repeat', 'no-repeat');")
  JS.eval ("$('div#gameBorder').css('background-attachment', 'fixed');")
  JS.eval ("$('div#gameBorder').css('background-size', '100% 100%');")
  ChangeUITheme ("Light")
  // Add MenuAudio
  AudioURL1 = GetFileURL("AmbientLoop.ogg")
  AudioURL2 = GetFileURL("Going-Different-Ways_Looping.ogg")
  JS.eval ("$('#MainMenu').append('<audio autoplay loop id=\"MenuAudio1\"><source src=\""+AudioURL1+"\" type=\"audio/ogg\"></audio>');")
  JS.eval ("$('#MainMenu').append('<audio autoplay loop id=\"MenuAudio2\"><source src=\""+AudioURL2+"\" type=\"audio/ogg\"></audio>');")
  Volume1 = 0.05
  Volume2 = 0.1
  // JS.eval ("var audio=document.getElementById('MenuAudio');audio.volume="+Volume+";")
  JS.eval ("document.getElementById('MenuAudio1').volume="+Volume1+";")
  JS.eval ("document.getElementById('MenuAudio2').volume="+Volume2+";")
else {
  game.OnMainMenuFlag = False
  // JS.eval ("$('#gameBorder').show();")
  JS.eval ("$('#txtCommandDiv').show(); $('#gamePanesRunning').show(); $('#divOutput').show(); $('#BottomBar').show(); $('#status').show();")
  JS.eval ("$('#MainMenu').hide();")
  JS.eval ("$('div#gameBorder').css('background-image', '');")
  JS.eval ("$('div#gameBorder').css('background-repeat', '');")
  JS.eval ("$('div#gameBorder').css('background-attachment', '');")
  JS.eval ("$('div#gameBorder').css('background-size', '');")
  ChangeUITheme (game.UITheme_last)
  // Fade Out then Remove MenuAudio
  JS.eval ("$('#MenuAudio1').animate({volume: 0.0}, 2000, function() {$('#MenuAudio1').remove();});")
  JS.eval ("$('#MenuAudio2').animate({volume: 0.0}, 2000, function() {$('#MenuAudio2').remove();});")
  // JS.eval ("$('#MenuAudio').remove();")

A lot of the above code is specific to what I wanted to do for my game, so I wouldn't get too bogged down in the specifics, the important bit is having an HTML link (or button) on-screen that can call a JavaScript function that can then call a Quest function to do what you want.

Apologize if that's not super clear. Depending on what you want to do, there may be a simpler way or even a pre-existing Quest library out there to help you, but if you still want to stick with it and try your hand at some custom interface stuff like that main menu, definitely recommend brushing up on HTML/JavaScript.

Log in to post a reply.