| Config/script.js | "use strict";
this.name = "ConsoleLogMFD";
this.author = "phkb";
this.copyright = "(C) 2023 phkb.";
this.licence = "CC-NC-by-SA 4.0";
this.description = "Console Log MFD main script";
/*
	Logic: console log messages received will be displayed in an MFD.
	Content of MFD is not scrollable. Content is recycled after docking. 
	Content is not saved between sessions.
*/
this._messages = [];
this._msRows = 18;				// rows to display on the mission screen
this._msCols = 32;				// columns to display on the mission screen
this._linePad = "";
this._hairSpace = String.fromCharCode(31);
this._lastText = "";
this._holdText = [];
//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
	// add interface to HUD selector
	var h = worldScripts.hudselector;
	if (h) h.$HUDSelectorAddMFD(this.name, this.name, expandDescription("[consolelog_mfd_name]"));
	this.shipDockedWithStation(player.ship.dockedStation);
	var w = defaultFont.measureString("> ");
	while (defaultFont.measureString(this._linePad) < w) {
		this._linePad += this._hairSpace;
	}
}
//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function (station) {
	this.$initInterface(station);
	this._holdText.length = 0;
}
//-------------------------------------------------------------------------------------------------------------
this.viewDirectionChanged = function (to, from) {
	this._holdText.shift();
}
//-------------------------------------------------------------------------------------------------------------
this.consoleMessageReceived = function (text) {
	// consoleMessageReceived happens before the viewDirectionChanged event
	// so in order to work out if the view has changed so we can ignore the message, we have to wait a moment
	// so each time a console message appears we start a 0.25 sec timer, which gives the viewDirectionChanged
	// event time to fire and potentially clear the message
	// Note: this probably isn't foolproof. A lot of messages arriving simultaneously might result in the wrong
	// text being dropped, or the change view message might get logged anyway.
	this._holdText.push(text);
	if (this._timer && this._timer.isRunning) return;
	this._timer = new Timer(this, this.$pushChange.bind(this), 0.25, 0);
}
//-------------------------------------------------------------------------------------------------------------
this.$pushChange = function (text) {
	if (text == undefined && this._holdText.length > 0) text = this._holdText.shift();
	if (text == "" || text == undefined) return;
	text = text.trim();
	// make arrival message slightly more meaningful.
	if (text == system.name + ".") text = expandDescription("[consolelog_arrived]") + " " + system.name + ".";
	// we don't need to record game paused messages
	if (text == expandDescription("[game-paused]", { pauseKey: expandDescription("[oolite_key_pausebutton]") })) return;
	// the forward view message gets past the defence onb launch, so whack it here.
	if (text == expandDescription("[forward-view-string]")) return;
	// remove any line breaks
	do {
		text = text.replace(/\n/gm, " ");
	} while (text.indexOf("\n") >= 0);
	// remove any double spaces
	do {
		text = text.replace(/  /gm, " ");
	} while (text.indexOf("  ") >= 0);
	// if the final text is the same as the last one, skip it.
	if (text == this._lastText) return;
	// update the last text variable for later checking
	this._lastText = text;
	this._messages.push("> " + text);
	if (!player.ship.docked) {
		this.$updateMFD();
	}
	if (this._holdText.length > 0) this._timer = new Timer(this, this.$pushChange.bind(this), 0.25, 0);
}
//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function () {
	this._messages.length = 0;
	this.$updateMFD();
}
//-------------------------------------------------------------------------------------------------------------
this.missionScreenEnded = function () {
	if (player.ship.hudHidden == true) player.ship.hudHidden = false;
}
//-------------------------------------------------------------------------------------------------------------
this.$updateMFD = function () {
	var p = player.ship;
	if (p.equipmentStatus("EQ_CONSOLELOGMFD") === "EQUIPMENT_OK") {
		var output = [];
		this.$reformatData(this._messages, output, 14.8);
		var text = "";
		var start = output.length - 9;
		if (start < 0) start = 0;
		for (var i = start; i < output.length; i++) {
			text += (text == "" ? "" : "\n") + output[i];
		}
		if (text == "") {
			text = expandDescription("[consolelog_header]") + ":\n\n" + expandDescription("[consolelog_none]");
		} else {
			text = expandDescription("[consolelog_header]") + ":\n" + text;
		}
		// send output to MFD
		p.setMultiFunctionText("ConsoleLogMFD", text, false);
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$initInterface = function (station) {
	station.setInterface(this.name, {
		title: expandDescription("[consolelog_interface_title]"),
		category: expandDescription("[interfaces-category-logs]"),
		summary: expandDescription("[consolelog_interface_summary]"),
		callback: this.$showConsoleLog.bind(this)
	});
}
//-------------------------------------------------------------------------------------------------------------
this.$showConsoleLog = function $showConsoleLog() {
	this._syslines = [];
	this.$reformatData(this._messages, this._syslines, this._msCols);
	this._maxpage = Math.ceil(this._syslines.length / this._msRows);
	this._curpage = 0;
	this.$showPage();
}
//-------------------------------------------------------------------------------------------------------------
this.$consoleLogPage = function (cpage, lines) {
	var output = "";
	var iStart = 0;
	var iEnd = 0;
	var textCount = 0;
	// set out initial end point
	iEnd = iStart + lines;
	if (cpage != 0) {
		iStart = cpage * lines;
		iEnd = iStart + lines;
	}
	if (iEnd > this._syslines.length) iEnd = this._syslines.length;
	for (var i = iStart; i < iEnd; i++) {
		textCount += this._syslines[i].trim().length;
		output += this._syslines[i] + "\n";
	}
	if (cpage === 0 && iStart === 0 && iEnd === 0 && (this._syslines[0] == null || textCount == 0)) output = expandDescription("[consolelog_empty]");
	return output;
}
//-------------------------------------------------------------------------------------------------------------
this.$showPage = function $showPage() {
	var p = player.ship;
	if (this.$isBigGuiActive() === false) p.hudHidden = true;
	this._consoleLogOpen = true;
	var text = this.$consoleLogPage(this._curpage, this._msRows);
	var opts;
	var curChoices = {};
	var def = "09_NEXT";
	if (this._maxpage <= 1) {
		def = "99_EXIT";
		curChoices["01_PREV"] = { text: "[consolelog_prevpage]", color: "darkGrayColor", unselectable: true };
		curChoices["09_NEXT"] = { text: "[consolelog_nextpage]", color: "darkGrayColor", unselectable: true };
		curChoices["10_FIRST"] = { text: "[consolelog_firstpage]", color: "darkGrayColor", unselectable: true };
		curChoices["11_LAST"] = { text: "[consolelog_lastpage]", color: "darkGrayColor", unselectable: true };
	} else {
		if (this._curpage === 0) {
			curChoices["01_PREV"] = { text: "[consolelog_prevpage]", color: "darkGrayColor", unselectable: true };
			curChoices["09_NEXT"] = { text: "[consolelog_nextpage]", color: this._menuColor };
			curChoices["10_FIRST"] = { text: "[consolelog_firstpage]", color: "darkGrayColor", unselectable: true };
			curChoices["11_LAST"] = { text: "[consolelog_lastpage]", color: "orangeColor" };
		} else {
			curChoices["01_PREV"] = { text: "[consolelog_prevpage]", color: this._menuColor };
			if (this._curpage + 1 === this._maxpage) {
				def = "01_PREV";
				curChoices["09_NEXT"] = { text: "[consolelog_nextpage]", color: "darkGrayColor", unselectable: true };
				curChoices["10_FIRST"] = { text: "[consolelog_firstpage]", color: this._menuColor };
				curChoices["11_LAST"] = { text: "[consolelog_lastpage]", color: "darkGrayColor", unselectable: true };
			} else {
				curChoices["09_NEXT"] = { text: "[consolelog_nextpage]", color: this._menuColor };
				curChoices["10_FIRST"] = { text: "[consolelog_firstpage]", color: this._menuColor };
				curChoices["11_LAST"] = { text: "[consolelog_lastpage]", color: this._menuColor };
			}
		}
	}
	if (text != "(empty)") {
		curChoices["15_DUMPLOG"] = { text: "[consolelog_write]", color: this._menuColor };
		curChoices["20_CLEARLOG"] = { text: "[consolelog_clear]", color: this._menuColor };
	} else {
		curChoices["15_DUMPLOG"] = { text: "[consolelog_write]", color: "darkGrayColor", unselectable: true };
		curChoices["20_CLEARLOG"] = { text: "[consolelog_clear]", color: "darkGrayColor", unselectable: true };
	}
	curChoices["99_EXIT"] = { text: "[consolelog_exit]", color: this._exitColor };
	var opts = {
		screenID: "oolite-consolelog-main-map",
		title: expandDescription("[consolelog_screen_title]", { page: (this._curpage + 1).toString(), max: (this._maxpage == 0 ? "1" : this._maxpage.toString()) }),
		allowInterrupt: true,
		choices: curChoices,
		exitScreen: "GUI_SCREEN_INTERFACES",
		overlay: { name: "consolelog_overlay.png", height: 546 },
		initialChoicesKey: this._lastchoice ? this._lastchoice : def,
		message: text
	};
	mission.runScreen(opts, this.$logHandler.bind(this));
}
//-------------------------------------------------------------------------------------------------------------
this.$logHandler = function $logHandler(choice) {
	delete this._lastchoice;
	var newChoice = null;
	if (!choice) {
		return; // launched while reading?
	} else if (choice === "01_PREV") {
		if (this._curpage > 0) this._curpage -= 1;
	} else if (choice === "09_NEXT") {
		if (this._curpage < this._maxpage - 1) this._curpage += 1;
	} else if (choice === "10_FIRST") {
		this._curpage = 0;
	} else if (choice === "11_LAST") {
		this._curpage = this._maxpage - 1;
	} else if (choice === "15_DUMPLOG") {
		this.$dumpLog();
	} else if (choice === "20_CLEARLOG") {
		this._scrollLaunch = 0;
		this._message = [];
		this._syslines = [];
		this._maxpage = 1;
		this._curpage = 0;
	}
	this._lastchoice = choice;
	if (newChoice) this._lastchoice = newChoice;
	if (choice != "99_EXIT") {
		this.$showPage();
	}
}
//-------------------------------------------------------------------------------------------------------------
// output the console log to the latest.log
this.$dumpLog = function () {
	var msg = "";
	for (var i = 0; i < this._messages.length; i++) {
		log(this.name, "> " + this._messages[i]);
	}
}
//-------------------------------------------------------------------------------------------------------------
this.$reformatData = function (srcArray, destArray, lineWidth) {
	//rebuild data into wide screen format
	var msg = "";
	for (var i = 0; i < srcArray.length; i++) {
		if (srcArray[i].substring(0, 1) != " " && msg != "") {
			// this is a new message, so output the current one and reset
			this.$addMsgToArray(msg, destArray, lineWidth);
			msg = "";
		}
		// add the message line to the current message
		// we'll trim off any leading and trailing spaces and add them back manually
		msg += srcArray[i].trim() + " ";
	}
	if (msg != "") {
		// output the remaining message
		this.$addMsgToArray(msg, destArray, lineWidth);
	}
}
//-------------------------------------------------------------------------------------------------------------
// adds a message to an array, splitting the lines based on a line width
this.$addMsgToArray = function (msg, ary, linewidth) {
	var prt = "";
	if (defaultFont.measureString(msg) > linewidth) {
		var words = msg.split(" ");
		var iPoint = 0
		var nextWord = "";
		do {
			// add the space in (after the first word)
			if (prt != "") {
				prt += " ";
			}
			// add the word on
			prt = prt + words[iPoint];
			// get the next word in the array
			if (iPoint < words.length - 1) {
				nextWord = " " + words[iPoint + 1];
			} else {
				nextWord = ""
			}
			// check the width of the next with the nextword added on
			if (defaultFont.measureString(prt + nextWord) > linewidth) {
				// hit the max width - add the line and start again
				ary.push(prt);
				// subsequent lines of a message will be indented slightly
				prt = this._linePad;
			}
			iPoint += 1;
			// check for the end of the array, and output remaining text
			if ((iPoint >= words.length) && (prt.trim() != "")) {
				ary.push(prt);
			}
		} while (iPoint < words.length);
		words = [];
	} else {
		ary.push(msg);
	}
}
//-------------------------------------------------------------------------------------------------------------
// returns true if a HUD with allowBigGUI is enabled, otherwise false
this.$isBigGuiActive = function () {
	if (oolite.compareVersion("1.83") <= 0) {
		return player.ship.hudAllowsBigGui;
	} else {
		var bigGuiHUD = ["XenonHUD.plist", "coluber_hud_ch01-dock.plist"]; 	// until there is a property we can check, I'll be listing HUD's that have the allow_big_gui property set here
		if (bigGuiHUD.indexOf(player.ship.hud) >= 0) {
			return true;
		} else {
			return false;
		}
	}
}
 |