| Scripts/contextual_help.js | "use strict";
this.name        = "ContextualHelp";
this.author      = "phkb";
this.copyright   = "2023 phkb";
this.description = "Implements a contextual help system to most gui screens.";
this.licence     = "CC BY-NC-SA 4.0";
// handling help on OXP pages?
this._page = 0;
this._text = "";
this._selectedKey = "";
this._keyDict = {
    "guiOPTIONS": "GUI_SCREEN_OPTIONS",
    "guiEQUIP": "GUI_SCREEN_EQUIP_SHIP",
    "guiSHIP": "GUI_SCREEN_SHIPYARD",
    "guiINTER": "GUI_SCREEN_INTERFACES",
    "guiSTATUS": "GUI_SCREEN_STATUS",
    "guiMAN": "GUI_SCREEN_MANIFEST",
    "guiLRC": "GUI_SCREEN_LONG_RANGE_CHART",
    "guiSRC": "GUI_SCREEN_SHORT_RANGE_CHART",
    "guiSYS": "GUI_SCREEN_SYSTEM_DATA",
    "guiMKT": "GUI_SCREEN_MARKET",
    "guiMINF": "GUI_SCREEN_MARKETINFO"
};
this._backgroundDict = {};
this._overlayDict = {};
this._extraText = {};
this._shownHints = [];
//-------------------------------------------------------------------------------------------------------------
this.startUp = function() {
    // register our hot key on the various GUI screens
    var keys = Object.keys(this._keyDict);
    var i = keys.length;
    while (i--) {
        var options = {};
        options["guiScreen"] = this._keyDict[keys[i]];
        options["registerKeys"] = {};
        options["registerKeys"][keys[i]] = [{key:"h",mod1:true}];
        options["callback"] = this.$setupHelpPage.bind(this);
        setExtraGuiScreenKeys(this.name, options);
    }
    if (missionVariables.ContextualHelp_Hints) {
        this._shownHints = JSON.parse(missionVariables.ContextualHelp_Hints);
    }
}
//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function() {
    missionVariables.ContextualHelp_Hints = JSON.stringify(this._shownHints);
}
//-------------------------------------------------------------------------------------------------------------
this.guiScreenChanged = function(to, from) {
    var showHint = false;
    if (this._shownHints.indexOf(to) == -1) {
        if (to == "GUI_SCREEN_OPTIONS") showHint = true;
        if (to == "GUI_SCREEN_EQUIP_SHIP") showHint = true;
        if (to == "GUI_SCREEN_SHIPYARD") showHint = true;
        if (to == "GUI_SCREEN_INTERFACES") showHint = true;
        if (to == "GUI_SCREEN_STATUS") showHint = true;
        if (to == "GUI_SCREEN_MANIFEST") showHint = true;
        if (to == "GUI_SCREEN_LONG_RANGE_CHART") showHint = true;
        if (to == "GUI_SCREEN_SHORT_RANGE_CHART") showHint = true;
        if (to == "GUI_SCREEN_SYSTEM_DATA") showHint = true;
        if (to == "GUI_SCREEN_MARKET") showHint = true;
        if (to == "GUI_SCREEN_MARKETINFO") showHint = true;
    }
    if (showHint == true) {
        this._shownHints.push(to);
        player.consoleMessage("Press Ctrl-H to display help");
    }
}
//-------------------------------------------------------------------------------------------------------------
// allows custom help text to be appended to the rest of the help screen text.
// usage $addHelpTextToGuiScreen(this.name, "GUI_SCREEN_MANIFEST", "Some extra helpful notes, hints or suggestions.");
this.$addHelpTextToGuiScreen = function(source, gui, text) {
    if (!source || source == "") {
        log(this.name, "Invalid source specified. Must be a valid text string (eg 'myoxp')");
        return;
    }
    var keys = Object.keys(this._keyDict);
    var i = keys.length;
    var found = false;
    while (i--) {
        if (this._keyDict[keys[i]] == gui) {found = true; break;}
    }
    if (found == false) {
        log(this.name, "Invalid GUI screen code specified: " + gui);
        return;
    }
    if (!this._extraText.hasOwnProperty(source)) {
        this._extraText[source] = {};
    }
    this._extraText[source][gui] = text;
}
//-------------------------------------------------------------------------------------------------------------
// allows custom background images to be set for the various help screens
// usage: $setBackgroundForHelpScreen("GUI_SCREEN_MANIFEST", "my_background_image.png");
this.$setBackgroundForHelpScreen = function(gui, background) {
    this._backgroundDict[gui] = background;
}
//-------------------------------------------------------------------------------------------------------------
// allows custom overlay images to be set for the variable help screens
// usage: $setOverlayForHelpScreen("GUI_SCREEN_MANIFEST", "my_overlay_image.png");
this.$setOverlayForHelpScreen = function(gui, overlay) {
    this._overlayDict[gui] = overlay;
}
//-------------------------------------------------------------------------------------------------------------
this.$setupHelpPage = function(key) {
    this._selectedKey = key;
    var gui = this._keyDict[this._selectedKey];
    var lookup = "[help_" + gui + "]";
    this._text = expandDescription(lookup);
    // add in additional OXP help here
    var keys = Object.keys(this._extraText);
    if (keys && keys.length > 0) {
        for (var i = 0; i < keys.length; i++) {
            if (this._extraText[keys[i]][gui]) this._text += "\n" + expandDescription(this._extraText[keys[i]][gui]);
        }
    }
    this._page = 1;
    this.$showHelpPage();
}
//-------------------------------------------------------------------------------------------------------------
this.$showHelpPage = function $showHelpPage() {
    var that = $showHelpPage;
    var columnText = (that.columnText = that.columnText || this.$columnText);
    // arrange the text into an array of text items that don't overflow the page
    var textLines = columnText(this._text, 32);
    // max rows of text to show
    var rows = 19; // could fit an extra line in, but having a one-line gap at the bottom looks better
    if (player.ship.hudAllowsBigGui) rows = 25;
    // work out the max number of pages we have
    var maxPage = parseInt((textLines.length - 1) / rows) + 1;
    var text = "";
    var counter = 0;
    // set teh start point, based on the current page
    var line = (this._page - 1) * rows;
    // start compiling the text for this page
    do {
        text += textLines[line] + "\n";
        line += 1;
        counter += 1;
        if (line > textLines.length - 1) break;
    } while (counter < rows)
    // build up the options for the mission screen
    var extra = "";
    if (this._page < maxPage || this._page != 1) extra += "Use Page Up/Page Down for more. ";
    var opts = {
        screenID: "oolite-contextual-help-map",
        title: "Help" + (maxPage > 1 ? " (Page " + this._page + " of " + maxPage + ")" : ""),
        allowInterrupt: true,
        exitScreen: this._keyDict[this._selectedKey],
        choices: {"99_EXIT":extra + "Press ESC or Enter to return"},
        registerKeys: {"keyEsc":[{key:"esc"}]},
        message: text
    };
    // if we've been given a background image for this page, set it now
    if (this._backgroundDict[this._keyDict[this._selectedKey]] != "") {
        opts["background"] = this._backgroundDict[this._keyDict[this._selectedKey]];
    }
    // if we've been given an overlay image for this page, set it now
    if (this._overlayDict[this._keyDict[this._selectedKey]] != undefined && this._overlayDict[this._keyDict[this._selectedKey]] != "") {
        //log(this.name, "overlay = " + this._overlayDict[this._keyDict[this._selectedKey]]);
        opts["overlay"] = this._overlayDict[this._keyDict[this._selectedKey]];
    } else {
        // use our default overlay
        log(this.name, "overlay default");
        opts["overlay"] = {name:"ch-info.png", height:546};
    }
    // only add the page up or down keys if there are pages to go to.
    if (this._page > 1) opts["registerKeys"]["keyPgUp"] = [{key:"pageUp"}];
    if (this._page < maxPage) opts["registerKeys"]["keyPgDn"] = [{key:"pageDown"}];
    mission.runScreen(opts, this.$screenHandler, this);
}
//-------------------------------------------------------------------------------------------------------------
this.$screenHandler = function(choice, key) {
    switch (key) {
        case "keyPgUp": this._page -= 1; this.$showHelpPage(); break;
        case "keyPgDn": this._page += 1; this.$showHelpPage(); break;
        case "enter": // we'll accept enter as well for the exit
        case "keyEsc": return;
    }
}
//-------------------------------------------------------------------------------------------------------------
// arranges text into a array of strings with a particular column width
// handles \n codes in the text
this.$columnText = function $columnText(originalText, columnWidth) {
	var returnText = [];
	if (defaultFont.measureString(originalText) > columnWidth) {
        var paras = originalText.split("\n");
        for (var p = 0; p < paras.length; p++) {
            var words = paras[p].split(" ");
            var word = "";
            var line = "";
            for (var w = 0; w < words.length; w++) {
                word = words[w].trim();
                // make sure we have a word to add before trying to add it
                if (word !== "") {
                    if (defaultFont.measureString(line + " " + word) <= columnWidth) {
                        line += (line === "" ? "" : " ") + word;
                    } else {
                        returnText.push(line);
                        line = word;
                    }
                }
            }
            // add any remainder
            returnText.push(line);
        }
	} else {
		returnText.push(originalText);
	}
	return returnText;
}
 |