XRWS         0  º    È   ¬    ¶ext_01.cat subst_01.cat ext_01.dat subst_01.dat aiscripts/move.flee.xml 199 1523932088 b92fd0f30aadc90d6fe2d8cb69b442b2
md/Notifications.xml 395 1523934107 428ea7731a90cf8281cb5abf9b674cff
t/0001.xml 196 1523663156 afec5be43dc4477caa78b55850cfd192
ui/addons/ego_helpText/helptext.lua 23307 1527059661 2aad112c81c9b3fbc49ef707cb0a212f
ui/addons/ego_helpText/helptext.xpl 23307 1527059661 2aad112c81c9b3fbc49ef707cb0a212f
ï»¿<?xml version="1.0" encoding="utf-8"?>
<diff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <replace sel="//do_if[do_if and do_else]/write_to_logbook/@category">tips</replace>
</diff><?xml version="1.0" encoding="utf-8"?>
<diff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <replace sel="//cue[@name='PlayerOwnedKilled']//write_to_logbook/@category">tips</replace>
  <replace sel="//cue[@name='PlayerShipAttacks']//write_to_logbook/@category">tips</replace>
  <replace sel="//cue[@name='PlayerOwnedAttacked']//write_to_logbook/@category">tips</replace>
</diff>
ï»¿<?xml version="1.0" encoding="utf-8"?>
<diff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <replace sel="//page[@id='1001']/t[@id='5708']/text()">{30152,601}</replace>
</diff>

ï»¿local gameversion = tonumber(GetVersionString():match("^(%d*%.%d+)%s.*$"))

-- Ingame Help Text Display

-- ffi setup
local ffi = require("ffi")
local C = ffi.C

if gameversion < 4.10 then

	ffi.cdef[[
		typedef uint64_t UniverseID;
		typedef struct {
			const char* objectiveText;
			float timeout;
			size_t numTargets;
		} MissionObjective;
		typedef struct {
			const char* missionName;
			const char* missionDescription;
			int difficulty;
			const char* mainType;
			const char* subType;
			const char* faction;
			int64_t reward;
			const char* rewardText;
			size_t numBriefingObjectives;
			int activeBriefingStep;
			const char* opposingFaction;
			const char* license;
			float timeLeft;
			double duration;
			bool abortable;
			bool hasObjective;
			UniverseID associatedComponent;
		} MissionDetails2;
		uint64_t GetActiveMissionID(void);
		MissionDetails2 GetMissionIDDetails2(uint64_t missionid);
		MissionObjective GetMissionIDObjective(uint64_t missionid, const char* fontname, uint32_t fontsize, uint32_t textwidth);
		bool IsGamePaused(void);
		bool IsPlayerValid(void);
	]]

elseif gameversion < 4.30 then

	ffi.cdef[[
		typedef uint64_t UniverseID;
		typedef struct {
			const char* objectiveText;
			float timeout;
			size_t numTargets;
		} MissionObjective;
		typedef struct {
			const char* missionName;
			const char* missionDescription;
			int difficulty;
			const char* mainType;
			const char* subType;
			const char* faction;
			int64_t reward;
			const char* rewardText;
			size_t numBriefingObjectives;
			int activeBriefingStep;
			const char* opposingFaction;
			const char* license;
			float timeLeft;
			double duration;
			bool abortable;
			bool hasObjective;
			UniverseID associatedComponent;
		} MissionDetails2;
		uint64_t GetActiveMissionID(void);
		MissionDetails2 GetMissionIDDetails2(uint64_t missionid);
		MissionObjective GetMissionIDObjective(uint64_t missionid, const char* fontname, uint32_t fontsize, uint32_t textwidth);
		bool IsGamePaused(void);
		bool IsPlayerValid(void);
		bool IsVRVersion(void);
	]]

else

	ffi.cdef[[
		typedef uint64_t UniverseID;
		typedef struct {
			const char* objectiveText;
			float timeout;
			size_t numTargets;
		} MissionObjective;
		typedef struct {
			const char* missionName;
			const char* missionDescription;
			int difficulty;
			const char* mainType;
			const char* subType;
			const char* faction;
			int64_t reward;
			const char* rewardText;
			size_t numBriefingObjectives;
			int activeBriefingStep;
			const char* opposingFaction;
			const char* license;
			float timeLeft;
			double duration;
			bool abortable;
			bool hasObjective;
			UniverseID associatedComponent;
		} MissionDetails2;
		uint64_t GetActiveMissionID(void);
		MissionDetails2 GetMissionIDDetails2(uint64_t missionid);
		MissionObjective GetMissionIDObjective(uint64_t missionid, const char* fontname, uint32_t fontsize, uint32_t textwidth);
		bool IsGamePaused(void);
		bool IsPlayerValid(void);
		bool IsVRMode(void);
	]]
end

local menu = {
	name = "HelpTextMenu",
}

local config = {
	height = 400,
	width = 230,
	offsetx = 0,
	offsety = 0,
	textoffsetx = 5,
	textoffsety = 2,
	backgroundColor = { r = 37, g = 48, b = 54, a = 70 },
	font = "Zekton_unfiltered",
	fontsize = 14
}

if gameversion < 4.10 then

	config.tableoffsetx = 5
	config.tableoffsety = 5

elseif gameversion < 4.30 then

	config.tableoffsetx = C.IsVRVersion() and 25 or 5
	config.tableoffsety = C.IsVRVersion() and 225 or 5

else

	config.tableoffsetx = C.IsVRMode() and 25 or 5
	config.tableoffsety = C.IsVRMode() and 225 or 5
	config.alertEffectColor = C.IsVRMode() and { r = 32, g = 50, b = 60, a = 100 } or nil

end

local function init()
	__CORE_HELPTEXT_DISPLAYEDHINTS = __CORE_HELPTEXT_DISPLAYEDHINTS or {}
	menu.queue = menu.queue or {}
	menu.missionqueue = menu.missionqueue or {}
	menu.lastupdate = 0

	RegisterEvent("helptext", menu.onShowHelp)
	RegisterEvent("missiontimer", menu.onShowMissionTimer)
	RegisterEvent("clearhelptext", menu.onClearHelp)
	registerForEvent("gameplanchange", getElement("Scene.UIContract"), menu.onGamePlanChange)
end

function menu.onGamePlanChange(_, mode)
	if mode == "extro" then
		UnregisterEvent("helptext", menu.onShowHelp)
		UnregisterEvent("missiontimer", menu.onShowMissionTimer)
		menu.cleanup()
	end
end

function menu.cleanup()
	if menu.frame then
		HideView(menu.frame)
	end
	menu.table = nil
	menu.frame = nil
	menu.duration = nil
	menu.hasMissionTimer = nil
end

function menu.onShowHelp(event, text)	
	local textid = tonumber(string.match(text, "%d+"))
	text = string.sub(text, string.find(text, ";") + 1)
	local force = tonumber(string.match(text, "%d")) == 1
	text = string.sub(text, string.find(text, ";") + 1)
	local writelog = tonumber(string.match(text, "%d")) == 1
	text = string.sub(text, string.find(text, ";") + 1)
	local silent = tonumber(string.match(text, "%d")) == 1
	text = string.sub(text, string.find(text, ";") + 1)
	local duration = string.match(text, "[0-9.]+")
	text = string.sub(text, string.find(text, ";") + 1)
	local position = tonumber(string.match(text, "%d+"))
	text = string.sub(text, string.find(text, ";") + 1)

	if force or not __CORE_HELPTEXT_DISPLAYEDHINTS[textid] then
		if textid ~= 0 then
			__CORE_HELPTEXT_DISPLAYEDHINTS[textid] = true
		end
		table.insert(menu.queue, {text = text, textid = textid, duration = duration, position = position, silent = silent})
		if not menu.duration then
			if menu.hasMissionTimer then
				RemoveScript("onUpdate", menu.onUpdate)
			end
			menu.displayHelp()
		end
	end
end

function menu.onClearHelp(event, id)
	if menu.queue[1] then
		if id == "current" then
			table.remove(menu.queue, 1)
		elseif id == "all" then
			menu.queue = {}
		else
			local textid = tonumber(id)
			if textid > 0 then
				local idx = 0
				for i, entry in ipairs(menu.queue) do
					if entry.textid == textid then
						idx = i
						break
					end
				end
				if idx > 0 then
					table.remove(menu.queue, idx)
				end
				if idx ~= 1 then
					return
				end
			end
		end
		RemoveScript("onUpdate", menu.onUpdate)
		if menu.queue[1] then
			-- display next hint
			menu.duration = nil
			menu.displayHelp()
		else
			if menu.hasMissionTimer then
				-- has missiontimer -> update
				menu.duration = nil
				menu.displayHelp()
			else
				-- clean up
				menu.cleanup()
			end
		end
	end
end

function menu.onShowMissionTimer(event, id)
	-- important --- we must store the IDs string representation since id is a cdata (64-bit) and therefore could differ for indexing
	-- also note that since we are mixing LuaJIT 64-bit values and Lua 64-bit values we must convert the string-representation and add a "ULL" so the
	-- string-representation for both is the same
	menu.missionqueue[tostring(id).."ULL"] = true
	local activeMission = C.GetActiveMissionID()
	if activeMission == id then
		if menu.duration or menu.hasMissionTimer then
			RemoveScript("onUpdate", menu.onUpdate)
		end
		menu.displayHelp()
	end
end

if gameversion < 4.10 then

	menu.displayHelp = function()
		local realtime = GetCurRealTime()
		menu.lastupdate = realtime

		local activeMission = C.GetActiveMissionID()
		local content = {}
		if menu.missionqueue[tostring(activeMission)] then
			-- missiontimer found
			local missionDetails = C.GetMissionIDDetails2(activeMission)
			local missionObjective
			if missionDetails.hasObjective then
				missionObjective = C.GetMissionIDObjective(activeMission, config.font, menu.scaleFont(config.font, config.fontsize), menu.scaleX(config.width - config.tableoffsetx - config.textoffsetx))
			end
			if missionObjective ~= nil and missionObjective.timeout >= 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionObjective.objectiveText) .. " (" .. ConvertTimeString(missionObjective.timeout, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, menu.scaleX(config.width - config.tableoffsetx) - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			elseif ffi.string(missionDetails.missionName) ~= "" and missionDetails.timeLeft > 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionDetails.missionName) .. " (" .. ConvertTimeString(missionDetails.timeLeft, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, menu.scaleX(config.width - config.tableoffsetx) - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			else
				-- invalid -> remove
				menu.hasMissionTimer = false
				menu.missionqueue[tostring(activeMission)] = nil
			end
		else
			-- no missiontimer found
			menu.hasMissionTimer = false
		end

		local hintcontent
		if menu.queue[1] then
			-- hint found
			local hintstring = CreateFontString(menu.queue[1].text, "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, menu.scaleX(config.width - config.tableoffsetx) - menu.scaleX(config.textoffsetx))
			hintcontent = { [1] = { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = hintstring } } } }
			if (not menu.queue[1].silent) and (not menu.duration) then
				PlaySound("notification_hint")
			end
			menu.duration = menu.duration or (realtime + menu.queue[1].duration)
		end
	
		local view_x, view_y = GetViewSize()
		local view
		if menu.duration then
			-- get position data
			local position_x, position_y = GetHintPosition(menu.queue[1].position)
			local offsetx, offsety = 0, 0
			if view_x and view_y then
				offsetx = position_x / 100 * view_x
				offsety = position_y / 100 * view_y
			end
			if menu.hasMissionTimer then
				local desc1 = CreateTable("", content, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), menu.scaleY(config.height - config.tableoffsety))
				local desc2 = CreateTable("", hintcontent, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, offsetx, offsety, menu.scaleY(config.height - config.tableoffsety))
				view = CreateTwoTableView(desc1, desc2, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true)
			else
				local desc = CreateTable("", hintcontent, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, offsetx, offsety, menu.scaleY(config.height - config.tableoffsety))
				view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true)
			end
		elseif menu.hasMissionTimer then
			local desc = CreateTable("", content, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), menu.scaleY(config.height - config.tableoffsety))
			view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true)
		end

		if C.IsPlayerValid() and (menu.hasMissionTimer or menu.duration) then
			menu.frame = DisplayView(view)
			menu.table = GetChildren(menu.frame)
			SetScript("onUpdate", menu.onUpdate)
		else
			menu.cleanup()
		end
	end

elseif gameversion < 4.30 then

	menu.displayHelp = function()
		local realtime = GetCurRealTime()
		menu.lastupdate = realtime
		local width = menu.scaleX(config.width - config.tableoffsetx) * (C.IsVRVersion() and 2 or 1)
		local height = menu.scaleY(config.height - config.tableoffsety)

		local activeMission = C.GetActiveMissionID()
		local content = {}
		if menu.missionqueue[tostring(activeMission)] then
			-- missiontimer found
			local missionDetails = C.GetMissionIDDetails2(activeMission)
			local missionObjective
			if missionDetails.hasObjective then
				missionObjective = C.GetMissionIDObjective(activeMission, config.font, menu.scaleFont(config.font, config.fontsize), width - menu.scaleX(config.textoffsetx))
			end
			if missionObjective ~= nil and missionObjective.timeout >= 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionObjective.objectiveText) .. " (" .. ConvertTimeString(missionObjective.timeout, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			elseif ffi.string(missionDetails.missionName) ~= "" and missionDetails.timeLeft > 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionDetails.missionName) .. " (" .. ConvertTimeString(missionDetails.timeLeft, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			else
				-- invalid -> remove
				menu.hasMissionTimer = false
				menu.missionqueue[tostring(activeMission)] = nil
			end
		else
			-- no missiontimer found
			menu.hasMissionTimer = false
		end

		local hintcontent
		if menu.queue[1] then
			-- hint found
			local hintstring = CreateFontString(menu.queue[1].text, "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
			hintcontent = { [1] = { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = hintstring } } } }
			if (not menu.queue[1].silent) and (not menu.duration) then
				PlaySound("notification_hint")
			end
			menu.duration = menu.duration or (realtime + menu.queue[1].duration)
		end
	
		local view_x, view_y = GetViewSize()
		local view
		if menu.duration then
			-- get position data
			local position_x, position_y = GetHintPosition(menu.queue[1].position)
			local offsetx, offsety = 0, 0
			if view_x and view_y then
				offsetx = math.min(position_x / 100 * view_x, view_x - width)
				offsety = position_y / 100 * view_y
			end
			if menu.hasMissionTimer then
				local desc1 = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
				local desc2 = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height)
				view = CreateTwoTableView(desc1, desc2, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			else
				local desc = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height)
				view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			end
		elseif menu.hasMissionTimer then
			local desc = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
			view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
		end

		if C.IsPlayerValid() and (menu.hasMissionTimer or menu.duration) then
			menu.frame = DisplayView(view)
			menu.table = GetChildren(menu.frame)
			SetScript("onUpdate", menu.onUpdate)
		else
			menu.cleanup()
		end
	end

else

	menu.displayHelp = function()
		local realtime = GetCurRealTime()
		menu.lastupdate = realtime
		local width = menu.scaleX(config.width - config.tableoffsetx) * (C.IsVRMode() and 2 or 1)
		local height = menu.scaleY(config.height - config.tableoffsety)

		local activeMission = C.GetActiveMissionID()
		local content = {}
		if menu.missionqueue[tostring(activeMission)] then
			-- missiontimer found
			local missionDetails = C.GetMissionIDDetails2(activeMission)
			local missionObjective
			if missionDetails.hasObjective then
				missionObjective = C.GetMissionIDObjective(activeMission, config.font, menu.scaleFont(config.font, config.fontsize), width - menu.scaleX(config.textoffsetx))
			end
			if missionObjective ~= nil and missionObjective.timeout >= 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionObjective.objectiveText) .. " (" .. ConvertTimeString(missionObjective.timeout, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			elseif ffi.string(missionDetails.missionName) ~= "" and missionDetails.timeLeft > 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionDetails.missionName) .. " (" .. ConvertTimeString(missionDetails.timeLeft, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			else
				-- invalid -> remove
				menu.hasMissionTimer = false
				menu.missionqueue[tostring(activeMission)] = nil
			end
		else
			-- no missiontimer found
			menu.hasMissionTimer = false
		end

		local hintcontent
		if menu.queue[1] then
			-- hint found
			local hintstring = CreateFontString(menu.queue[1].text, "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
			hintcontent = { [1] = { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = hintstring } } } }
			if (not menu.queue[1].silent) and (not menu.duration) then
				PlaySound("notification_hint")
			end
			menu.duration = menu.duration or (realtime + menu.queue[1].duration)
		end

		local view_x, view_y = GetViewSize()
		local view
		if menu.duration then
			-- get position data
			local position_x, position_y = GetHintPosition(menu.queue[1].position)
			local offsetx, offsety = 0, 0
			if view_x and view_y then
				offsetx = math.min(position_x / 100 * view_x, view_x - width)
				offsety = position_y / 100 * view_y
			end
			if menu.hasMissionTimer then
				local desc1 = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
				local desc2 = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height)
				view = CreateTwoTableView(desc1, desc2, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			else
				local desc = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height, 0, 0, 0, false, config.alertEffectColor)
				view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			end
		elseif menu.hasMissionTimer then
			local desc = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
			view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
		end

		if C.IsPlayerValid() and (menu.hasMissionTimer or menu.duration) then
			menu.frame = DisplayView(view)
			menu.table = GetChildren(menu.frame)
			SetScript("onUpdate", menu.onUpdate)
		else
			menu.cleanup()
		end
	end
end

function menu.onUpdate()
	if not C.IsGamePaused() then
		local realtime = GetCurRealTime()
		-- only check every second
		if menu.lastupdate + 1 < realtime then
			menu.lastupdate = realtime
			if menu.duration and menu.duration < realtime then
				-- hint displayed, but expired
				table.remove(menu.queue, 1)
				RemoveScript("onUpdate", menu.onUpdate)
				if menu.queue[1] then
					-- display next hint
					menu.duration = nil
					menu.displayHelp()
				else
					if menu.hasMissionTimer then
						-- has missiontimer -> update
						menu.duration = nil
						menu.displayHelp()
					else
						-- clean up
						menu.cleanup()
					end
				end
				return
			end
			if menu.hasMissionTimer then
				-- has missiontimer -> update
				-- TODO Florian - change to use updateCellText when issues with debuglog are resolved
				RemoveScript("onUpdate", menu.onUpdate)
				menu.displayHelp()
			end
		end
	end
end

function menu.round(x, digits)
	local mult = 1
	if digits and digits > 0 then
		mult = 10^digits
	end
	return math.floor(x * mult + 0.5) / mult
end

function menu.scaleX(x)
	local xx = GetViewSize()
	if not xx then
		return x
	else
		return menu.round(x * xx / 1280)
	end
end

function menu.scaleY(y)
	local _, yy = GetViewSize()
	if not yy then
		return y
	else
		return menu.round(y * yy / 720)
	end
end

function menu.scaleFont(fontname, fontsize)
	fontsize = menu.scaleY(fontsize)
	return GetBestFontSize(fontname, fontsize)
end

init()
ï»¿local gameversion = tonumber(GetVersionString():match("^(%d*%.%d+)%s.*$"))

-- Ingame Help Text Display

-- ffi setup
local ffi = require("ffi")
local C = ffi.C

if gameversion < 4.10 then

	ffi.cdef[[
		typedef uint64_t UniverseID;
		typedef struct {
			const char* objectiveText;
			float timeout;
			size_t numTargets;
		} MissionObjective;
		typedef struct {
			const char* missionName;
			const char* missionDescription;
			int difficulty;
			const char* mainType;
			const char* subType;
			const char* faction;
			int64_t reward;
			const char* rewardText;
			size_t numBriefingObjectives;
			int activeBriefingStep;
			const char* opposingFaction;
			const char* license;
			float timeLeft;
			double duration;
			bool abortable;
			bool hasObjective;
			UniverseID associatedComponent;
		} MissionDetails2;
		uint64_t GetActiveMissionID(void);
		MissionDetails2 GetMissionIDDetails2(uint64_t missionid);
		MissionObjective GetMissionIDObjective(uint64_t missionid, const char* fontname, uint32_t fontsize, uint32_t textwidth);
		bool IsGamePaused(void);
		bool IsPlayerValid(void);
	]]

elseif gameversion < 4.30 then

	ffi.cdef[[
		typedef uint64_t UniverseID;
		typedef struct {
			const char* objectiveText;
			float timeout;
			size_t numTargets;
		} MissionObjective;
		typedef struct {
			const char* missionName;
			const char* missionDescription;
			int difficulty;
			const char* mainType;
			const char* subType;
			const char* faction;
			int64_t reward;
			const char* rewardText;
			size_t numBriefingObjectives;
			int activeBriefingStep;
			const char* opposingFaction;
			const char* license;
			float timeLeft;
			double duration;
			bool abortable;
			bool hasObjective;
			UniverseID associatedComponent;
		} MissionDetails2;
		uint64_t GetActiveMissionID(void);
		MissionDetails2 GetMissionIDDetails2(uint64_t missionid);
		MissionObjective GetMissionIDObjective(uint64_t missionid, const char* fontname, uint32_t fontsize, uint32_t textwidth);
		bool IsGamePaused(void);
		bool IsPlayerValid(void);
		bool IsVRVersion(void);
	]]

else

	ffi.cdef[[
		typedef uint64_t UniverseID;
		typedef struct {
			const char* objectiveText;
			float timeout;
			size_t numTargets;
		} MissionObjective;
		typedef struct {
			const char* missionName;
			const char* missionDescription;
			int difficulty;
			const char* mainType;
			const char* subType;
			const char* faction;
			int64_t reward;
			const char* rewardText;
			size_t numBriefingObjectives;
			int activeBriefingStep;
			const char* opposingFaction;
			const char* license;
			float timeLeft;
			double duration;
			bool abortable;
			bool hasObjective;
			UniverseID associatedComponent;
		} MissionDetails2;
		uint64_t GetActiveMissionID(void);
		MissionDetails2 GetMissionIDDetails2(uint64_t missionid);
		MissionObjective GetMissionIDObjective(uint64_t missionid, const char* fontname, uint32_t fontsize, uint32_t textwidth);
		bool IsGamePaused(void);
		bool IsPlayerValid(void);
		bool IsVRMode(void);
	]]
end

local menu = {
	name = "HelpTextMenu",
}

local config = {
	height = 400,
	width = 230,
	offsetx = 0,
	offsety = 0,
	textoffsetx = 5,
	textoffsety = 2,
	backgroundColor = { r = 37, g = 48, b = 54, a = 70 },
	font = "Zekton_unfiltered",
	fontsize = 14
}

if gameversion < 4.10 then

	config.tableoffsetx = 5
	config.tableoffsety = 5

elseif gameversion < 4.30 then

	config.tableoffsetx = C.IsVRVersion() and 25 or 5
	config.tableoffsety = C.IsVRVersion() and 225 or 5

else

	config.tableoffsetx = C.IsVRMode() and 25 or 5
	config.tableoffsety = C.IsVRMode() and 225 or 5
	config.alertEffectColor = C.IsVRMode() and { r = 32, g = 50, b = 60, a = 100 } or nil

end

local function init()
	__CORE_HELPTEXT_DISPLAYEDHINTS = __CORE_HELPTEXT_DISPLAYEDHINTS or {}
	menu.queue = menu.queue or {}
	menu.missionqueue = menu.missionqueue or {}
	menu.lastupdate = 0

	RegisterEvent("helptext", menu.onShowHelp)
	RegisterEvent("missiontimer", menu.onShowMissionTimer)
	RegisterEvent("clearhelptext", menu.onClearHelp)
	registerForEvent("gameplanchange", getElement("Scene.UIContract"), menu.onGamePlanChange)
end

function menu.onGamePlanChange(_, mode)
	if mode == "extro" then
		UnregisterEvent("helptext", menu.onShowHelp)
		UnregisterEvent("missiontimer", menu.onShowMissionTimer)
		menu.cleanup()
	end
end

function menu.cleanup()
	if menu.frame then
		HideView(menu.frame)
	end
	menu.table = nil
	menu.frame = nil
	menu.duration = nil
	menu.hasMissionTimer = nil
end

function menu.onShowHelp(event, text)	
	local textid = tonumber(string.match(text, "%d+"))
	text = string.sub(text, string.find(text, ";") + 1)
	local force = tonumber(string.match(text, "%d")) == 1
	text = string.sub(text, string.find(text, ";") + 1)
	local writelog = tonumber(string.match(text, "%d")) == 1
	text = string.sub(text, string.find(text, ";") + 1)
	local silent = tonumber(string.match(text, "%d")) == 1
	text = string.sub(text, string.find(text, ";") + 1)
	local duration = string.match(text, "[0-9.]+")
	text = string.sub(text, string.find(text, ";") + 1)
	local position = tonumber(string.match(text, "%d+"))
	text = string.sub(text, string.find(text, ";") + 1)

	if force or not __CORE_HELPTEXT_DISPLAYEDHINTS[textid] then
		if textid ~= 0 then
			__CORE_HELPTEXT_DISPLAYEDHINTS[textid] = true
		end
		table.insert(menu.queue, {text = text, textid = textid, duration = duration, position = position, silent = silent})
		if not menu.duration then
			if menu.hasMissionTimer then
				RemoveScript("onUpdate", menu.onUpdate)
			end
			menu.displayHelp()
		end
	end
end

function menu.onClearHelp(event, id)
	if menu.queue[1] then
		if id == "current" then
			table.remove(menu.queue, 1)
		elseif id == "all" then
			menu.queue = {}
		else
			local textid = tonumber(id)
			if textid > 0 then
				local idx = 0
				for i, entry in ipairs(menu.queue) do
					if entry.textid == textid then
						idx = i
						break
					end
				end
				if idx > 0 then
					table.remove(menu.queue, idx)
				end
				if idx ~= 1 then
					return
				end
			end
		end
		RemoveScript("onUpdate", menu.onUpdate)
		if menu.queue[1] then
			-- display next hint
			menu.duration = nil
			menu.displayHelp()
		else
			if menu.hasMissionTimer then
				-- has missiontimer -> update
				menu.duration = nil
				menu.displayHelp()
			else
				-- clean up
				menu.cleanup()
			end
		end
	end
end

function menu.onShowMissionTimer(event, id)
	-- important --- we must store the IDs string representation since id is a cdata (64-bit) and therefore could differ for indexing
	-- also note that since we are mixing LuaJIT 64-bit values and Lua 64-bit values we must convert the string-representation and add a "ULL" so the
	-- string-representation for both is the same
	menu.missionqueue[tostring(id).."ULL"] = true
	local activeMission = C.GetActiveMissionID()
	if activeMission == id then
		if menu.duration or menu.hasMissionTimer then
			RemoveScript("onUpdate", menu.onUpdate)
		end
		menu.displayHelp()
	end
end

if gameversion < 4.10 then

	menu.displayHelp = function()
		local realtime = GetCurRealTime()
		menu.lastupdate = realtime

		local activeMission = C.GetActiveMissionID()
		local content = {}
		if menu.missionqueue[tostring(activeMission)] then
			-- missiontimer found
			local missionDetails = C.GetMissionIDDetails2(activeMission)
			local missionObjective
			if missionDetails.hasObjective then
				missionObjective = C.GetMissionIDObjective(activeMission, config.font, menu.scaleFont(config.font, config.fontsize), menu.scaleX(config.width - config.tableoffsetx - config.textoffsetx))
			end
			if missionObjective ~= nil and missionObjective.timeout >= 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionObjective.objectiveText) .. " (" .. ConvertTimeString(missionObjective.timeout, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, menu.scaleX(config.width - config.tableoffsetx) - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			elseif ffi.string(missionDetails.missionName) ~= "" and missionDetails.timeLeft > 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionDetails.missionName) .. " (" .. ConvertTimeString(missionDetails.timeLeft, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, menu.scaleX(config.width - config.tableoffsetx) - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			else
				-- invalid -> remove
				menu.hasMissionTimer = false
				menu.missionqueue[tostring(activeMission)] = nil
			end
		else
			-- no missiontimer found
			menu.hasMissionTimer = false
		end

		local hintcontent
		if menu.queue[1] then
			-- hint found
			local hintstring = CreateFontString(menu.queue[1].text, "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, menu.scaleX(config.width - config.tableoffsetx) - menu.scaleX(config.textoffsetx))
			hintcontent = { [1] = { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = hintstring } } } }
			if (not menu.queue[1].silent) and (not menu.duration) then
				PlaySound("notification_hint")
			end
			menu.duration = menu.duration or (realtime + menu.queue[1].duration)
		end
	
		local view_x, view_y = GetViewSize()
		local view
		if menu.duration then
			-- get position data
			local position_x, position_y = GetHintPosition(menu.queue[1].position)
			local offsetx, offsety = 0, 0
			if view_x and view_y then
				offsetx = position_x / 100 * view_x
				offsety = position_y / 100 * view_y
			end
			if menu.hasMissionTimer then
				local desc1 = CreateTable("", content, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), menu.scaleY(config.height - config.tableoffsety))
				local desc2 = CreateTable("", hintcontent, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, offsetx, offsety, menu.scaleY(config.height - config.tableoffsety))
				view = CreateTwoTableView(desc1, desc2, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true)
			else
				local desc = CreateTable("", hintcontent, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, offsetx, offsety, menu.scaleY(config.height - config.tableoffsety))
				view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true)
			end
		elseif menu.hasMissionTimer then
			local desc = CreateTable("", content, { menu.scaleX(config.width - config.tableoffsetx) }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), menu.scaleY(config.height - config.tableoffsety))
			view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true)
		end

		if C.IsPlayerValid() and (menu.hasMissionTimer or menu.duration) then
			menu.frame = DisplayView(view)
			menu.table = GetChildren(menu.frame)
			SetScript("onUpdate", menu.onUpdate)
		else
			menu.cleanup()
		end
	end

elseif gameversion < 4.30 then

	menu.displayHelp = function()
		local realtime = GetCurRealTime()
		menu.lastupdate = realtime
		local width = menu.scaleX(config.width - config.tableoffsetx) * (C.IsVRVersion() and 2 or 1)
		local height = menu.scaleY(config.height - config.tableoffsety)

		local activeMission = C.GetActiveMissionID()
		local content = {}
		if menu.missionqueue[tostring(activeMission)] then
			-- missiontimer found
			local missionDetails = C.GetMissionIDDetails2(activeMission)
			local missionObjective
			if missionDetails.hasObjective then
				missionObjective = C.GetMissionIDObjective(activeMission, config.font, menu.scaleFont(config.font, config.fontsize), width - menu.scaleX(config.textoffsetx))
			end
			if missionObjective ~= nil and missionObjective.timeout >= 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionObjective.objectiveText) .. " (" .. ConvertTimeString(missionObjective.timeout, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			elseif ffi.string(missionDetails.missionName) ~= "" and missionDetails.timeLeft > 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionDetails.missionName) .. " (" .. ConvertTimeString(missionDetails.timeLeft, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			else
				-- invalid -> remove
				menu.hasMissionTimer = false
				menu.missionqueue[tostring(activeMission)] = nil
			end
		else
			-- no missiontimer found
			menu.hasMissionTimer = false
		end

		local hintcontent
		if menu.queue[1] then
			-- hint found
			local hintstring = CreateFontString(menu.queue[1].text, "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
			hintcontent = { [1] = { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = hintstring } } } }
			if (not menu.queue[1].silent) and (not menu.duration) then
				PlaySound("notification_hint")
			end
			menu.duration = menu.duration or (realtime + menu.queue[1].duration)
		end
	
		local view_x, view_y = GetViewSize()
		local view
		if menu.duration then
			-- get position data
			local position_x, position_y = GetHintPosition(menu.queue[1].position)
			local offsetx, offsety = 0, 0
			if view_x and view_y then
				offsetx = math.min(position_x / 100 * view_x, view_x - width)
				offsety = position_y / 100 * view_y
			end
			if menu.hasMissionTimer then
				local desc1 = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
				local desc2 = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height)
				view = CreateTwoTableView(desc1, desc2, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			else
				local desc = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height)
				view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			end
		elseif menu.hasMissionTimer then
			local desc = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
			view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
		end

		if C.IsPlayerValid() and (menu.hasMissionTimer or menu.duration) then
			menu.frame = DisplayView(view)
			menu.table = GetChildren(menu.frame)
			SetScript("onUpdate", menu.onUpdate)
		else
			menu.cleanup()
		end
	end

else

	menu.displayHelp = function()
		local realtime = GetCurRealTime()
		menu.lastupdate = realtime
		local width = menu.scaleX(config.width - config.tableoffsetx) * (C.IsVRMode() and 2 or 1)
		local height = menu.scaleY(config.height - config.tableoffsety)

		local activeMission = C.GetActiveMissionID()
		local content = {}
		if menu.missionqueue[tostring(activeMission)] then
			-- missiontimer found
			local missionDetails = C.GetMissionIDDetails2(activeMission)
			local missionObjective
			if missionDetails.hasObjective then
				missionObjective = C.GetMissionIDObjective(activeMission, config.font, menu.scaleFont(config.font, config.fontsize), width - menu.scaleX(config.textoffsetx))
			end
			if missionObjective ~= nil and missionObjective.timeout >= 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionObjective.objectiveText) .. " (" .. ConvertTimeString(missionObjective.timeout, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			elseif ffi.string(missionDetails.missionName) ~= "" and missionDetails.timeLeft > 0 then
				-- still valid -> display
				if not menu.hasMissionTimer then
					PlaySound("notification_hint")
				end
				menu.hasMissionTimer = true
				local missionstring = CreateFontString(ffi.string(missionDetails.missionName) .. " (" .. ConvertTimeString(missionDetails.timeLeft, "%m:%S") .. ")", "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
				table.insert(content, { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = missionstring } } })
			else
				-- invalid -> remove
				menu.hasMissionTimer = false
				menu.missionqueue[tostring(activeMission)] = nil
			end
		else
			-- no missiontimer found
			menu.hasMissionTimer = false
		end

		local hintcontent
		if menu.queue[1] then
			-- hint found
			local hintstring = CreateFontString(menu.queue[1].text, "left", 255, 255, 255, 100, config.font, menu.scaleFont(config.font, config.fontsize), true, menu.scaleX(config.textoffsetx), menu.scaleY(config.textoffsety), 0, width - menu.scaleX(config.textoffsetx))
			hintcontent = { [1] = { color = config.backgroundColor, selectable = false, cols = { [1] = { colspan = 1, content = hintstring } } } }
			if (not menu.queue[1].silent) and (not menu.duration) then
				PlaySound("notification_hint")
			end
			menu.duration = menu.duration or (realtime + menu.queue[1].duration)
		end

		local view_x, view_y = GetViewSize()
		local view
		if menu.duration then
			-- get position data
			local position_x, position_y = GetHintPosition(menu.queue[1].position)
			local offsetx, offsety = 0, 0
			if view_x and view_y then
				offsetx = math.min(position_x / 100 * view_x, view_x - width)
				offsety = position_y / 100 * view_y
			end
			if menu.hasMissionTimer then
				local desc1 = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
				local desc2 = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height)
				view = CreateTwoTableView(desc1, desc2, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			else
				local desc = CreateTable("", hintcontent, { width }, false, true, 0, #content, offsetx, offsety, height, 0, 0, 0, false, config.alertEffectColor)
				view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
			end
		elseif menu.hasMissionTimer then
			local desc = CreateTable("", content, { width }, false, true, 0, #content, menu.scaleX(config.tableoffsetx), menu.scaleY(config.tableoffsety), height)
			view = CreateTableView(desc, false, "", "", view_x, view_y, menu.scaleX(config.offsetx), menu.scaleY(config.offsety), "none", true, false, true)
		end

		if C.IsPlayerValid() and (menu.hasMissionTimer or menu.duration) then
			menu.frame = DisplayView(view)
			menu.table = GetChildren(menu.frame)
			SetScript("onUpdate", menu.onUpdate)
		else
			menu.cleanup()
		end
	end
end

function menu.onUpdate()
	if not C.IsGamePaused() then
		local realtime = GetCurRealTime()
		-- only check every second
		if menu.lastupdate + 1 < realtime then
			menu.lastupdate = realtime
			if menu.duration and menu.duration < realtime then
				-- hint displayed, but expired
				table.remove(menu.queue, 1)
				RemoveScript("onUpdate", menu.onUpdate)
				if menu.queue[1] then
					-- display next hint
					menu.duration = nil
					menu.displayHelp()
				else
					if menu.hasMissionTimer then
						-- has missiontimer -> update
						menu.duration = nil
						menu.displayHelp()
					else
						-- clean up
						menu.cleanup()
					end
				end
				return
			end
			if menu.hasMissionTimer then
				-- has missiontimer -> update
				-- TODO Florian - change to use updateCellText when issues with debuglog are resolved
				RemoveScript("onUpdate", menu.onUpdate)
				menu.displayHelp()
			end
		end
	end
end

function menu.round(x, digits)
	local mult = 1
	if digits and digits > 0 then
		mult = 10^digits
	end
	return math.floor(x * mult + 0.5) / mult
end

function menu.scaleX(x)
	local xx = GetViewSize()
	if not xx then
		return x
	else
		return menu.round(x * xx / 1280)
	end
end

function menu.scaleY(y)
	local _, yy = GetViewSize()
	if not yy then
		return y
	else
		return menu.round(y * yy / 720)
	end
end

function menu.scaleFont(fontname, fontsize)
	fontsize = menu.scaleY(fontsize)
	return GetBestFontSize(fontname, fontsize)
end

init()
<?xml version="1.0" encoding="iso-8859-1"?>
<content id="" name="" description="" author="eliseeff" version="100" date="2018-05-25" save="false" lastupdate="">
  <dependency version="400"/>
  <dependency id="ws_1390391214" optional="true" name="Improved Log"/>
  <text language="7" name="&#1059;&#1073;&#1080;&#1088;&#1072;&#1077;&#1084; &#1087;&#1086;&#1076;&#1089;&#1082;&#1072;&#1079;&#1082;&#1080; &#1080;&#1079; &#1078;&#1091;&#1088;&#1085;&#1072;&#1083;&#1072;" description="&#1059;&#1073;&#1088;&#1072;&#1085;&#1072; &#1079;&#1072;&#1087;&#1080;&#1089;&#1100; &#1087;&#1086;&#1076;&#1089;&#1082;&#1072;&#1079;&#1086;&#1082; &#1074; &#1078;&#1091;&#1088;&#1085;&#1072;&#1083;. &#1050;&#1072;&#1090;&#1077;&#1075;&#1086;&#1088;&#1080;&#1103; '&#1057;&#1086;&#1074;&#1077;&#1090;&#1099;' &#1087;&#1077;&#1088;&#1077;&#1080;&#1084;&#1077;&#1085;&#1086;&#1074;&#1072;&#1085;&#1072; &#1074; '&#1041;&#1086;&#1081;' &#1080; &#1090;&#1077;&#1087;&#1077;&#1088;&#1100; &#1074; &#1085;&#1077;&#1077; &#1079;&#1072;&#1087;&#1080;&#1089;&#1099;&#1074;&#1072;&#1102;&#1090;&#1089;&#1103; &#1090;&#1086;&#1083;&#1100;&#1082;&#1086; &#1089;&#1086;&#1086;&#1073;&#1097;&#1077;&#1085;&#1080;&#1103;, &#1082;&#1072;&#1089;&#1072;&#1102;&#1097;&#1080;&#1077;&#1089;&#1103; &#1073;&#1086;&#1077;&#1074;&#1099;&#1093; &#1089;&#1090;&#1099;&#1095;&#1077;&#1082;: &#1073;&#1088;&#1086;&#1096;&#1077;&#1085;&#1085;&#1099;&#1077; &#1082;&#1086;&#1088;&#1072;&#1073;&#1083;&#1080; &#1087;&#1088;&#1086;&#1090;&#1080;&#1074;&#1085;&#1080;&#1082;&#1072;, &#1086;&#1090;&#1089;&#1090;&#1091;&#1087;&#1080;&#1074;&#1096;&#1080;&#1077; &#1087;&#1088;&#1080; &#1072;&#1090;&#1072;&#1082;&#1077; &#1085;&#1072; &#1085;&#1080;&#1093; &#1082;&#1086;&#1088;&#1072;&#1073;&#1083;&#1080; &#1080;&#1075;&#1088;&#1086;&#1082;&#1072;, &#1072; &#1090;&#1072;&#1082;&#1078;&#1077; &#1073;&#1086;&#1077;&#1074;&#1099;&#1077; &#1087;&#1086;&#1090;&#1077;&#1088;&#1080;."/>
</content>
