#include "game.lua" #include "options.lua" #include "score.lua" #include "map.lua" #include "script/common.lua" #include "platform.lua" #include "components/photomode_view.lua" #include "components/active_mods_view.lua" #include "components/end_screen.lua" #include "components/common_components.lua" #include "ui/ui_helpers.lua" gSelectedToolFlash = nil gSelectedToolGroup = nil function init() gPaused = false gPauseBarWidth = 300 gPauseBarScroll = 0 gPauseBarScrollSmooth = 0 gAlarm = false gToolSelectWidth = 200 gToolSelectAlpha = 0 gToolGroupScrollPos = {1, 1, 1, 1, 1, 1} gToolGroupScrollSmooth = {1, 1, 1, 1, 1, 1} gPhotoMode = false gPhotoHideSettings = false gPhotoHideUi = false gPhotoDofDepth = 30 gPhotoDofScale = 1 gPhotoExposure = 1 gPhotoBloom = 1 gPhotoFov = 90 gPhotoTool = false gPhotoTransform = Transform() gPhotoRotYaw = 0 gPhotoRotPitch = 0 gPhotoRotRoll = 0 gPhotoSpeed = 1.0 gPhotoTimeFromLastZoomChange = 0.0 gActiveMods = false gNotification = "" gNotificationIcons = {} gNotificationTimer = 0 gPickInfo = false gPickInfoText = "" gPickInfoAlpha = 0 gPickInfoTimer = 0 gPickInfoChars = 0 gHurtFade = 0 gLastHealth = 1 gCashDisplay = GetInt("savegame.cash") gCashScale = 0 gCashCount = 0 gCashFlash = 0 endfade = 0 pauseMenuAlpha = 0 optionsAlpha = 0 notificationAlpha = 0 intentConfirmAlpha = 0 showHealth = false healthFade = 0 local gShowTitle = false gMissionMode = isInMission() gHubMode = false gHintInfoScale = 0 gHintInfo = "" gMissionId = GetString("game.levelid") gSelectedTool = nil local showLargeUI = GetBool("game.largeui") gUiScaleUpFactor = 1.0 if showLargeUI then gUiScaleUpFactor = 1.3 end gEndingSequence = 0.0 if GetString("game.mod") ~= "" then gMissions[gMissionId] = {} gMissions[gMissionId].title=GetString("game.mod.title") gMissions[gMissionId].desc=GetString("game.mod.description") gMissions[gMissionId].securityTitle="" gMissions[gMissionId].securityDesc="" gMissions[gMissionId].primary=0 gMissions[gMissionId].secondary=0 gMissions[gMissionId].required=0 gMissions[gMissionId].bonus = {} elseif gMissionId == "about" or string.sub(gMissionId,1,6) == "ending" then gMissions[gMissionId] = {} gMissions[gMissionId].title="" gMissions[gMissionId].desc="" gMissions[gMissionId].securityTitle="" gMissions[gMissionId].securityDesc="" gMissions[gMissionId].primary=0 gMissions[gMissionId].secondary=0 gMissions[gMissionId].required=0 gMissions[gMissionId].bonus = {} elseif gMissionId == "" then gMissionId = "tmp" gMissions[gMissionId] = {} gMissions[gMissionId].title="loc@TITLE_MISSION_TITLE" gMissions[gMissionId].desc="loc@DESC_MISSION_TITLE" gMissions[gMissionId].securityTitle="loc@SECURITYTITLE_UNKNOWN" gMissions[gMissionId].securityDesc="loc@SECURITYDESC_UNKNOWN" gMissions[gMissionId].primary=GetInt("level.primary") gMissions[gMissionId].secondary=GetInt("level.secondary") gMissions[gMissionId].required=GetInt("level.required") gMissions[gMissionId].bonus = {} elseif string.sub(gMissionId,1,3)=="hub" then gMissions[gMissionId] = {} gMissions[gMissionId].title="loc@TITLE_LCKELLE_TEARDOWN_SERVICES" gMissions[gMissionId].desc="loc@DESC_LCKELLE_TEARDOWN_SERVICES" if string.sub(gMissionId,1,4)=="hub3" and string.len(gMissionId)==5 then gMissions[gMissionId].title="loc@TITLE_MURATORI_BEACH" gMissions[gMissionId].desc="loc@DESC_MURATORI_BEACH_1" end if string.sub(gMissionId,1,5)=="hub_c" then gMissions[gMissionId].title="loc@TITLE_MURATORI_BEACH" gMissions[gMissionId].desc="loc@DESC_MURATORI_BEACH_2" end gMissions[gMissionId].securityTitle="" gMissions[gMissionId].securityDesc="" gMissions[gMissionId].primary=0 gMissions[gMissionId].secondary=0 gMissions[gMissionId].required=0 gMissions[gMissionId].bonus = {} gHubMode = not GetBool("level.sandbox") elseif string.sub(gMissionId,1,3)=="ch_" then gMissions[gMissionId] = {} gMissions[gMissionId].title=gChallenges[gMissionId].title gMissions[gMissionId].desc=gChallenges[gMissionId].desc gMissions[gMissionId].securityTitle="" gMissions[gMissionId].securityDesc="" gMissions[gMissionId].primary=0 gMissions[gMissionId].secondary=0 gMissions[gMissionId].required=0 gMissions[gMissionId].bonus = {} elseif gMissions[gMissionId] then if gMissions[gMissionId].primary~=GetInt("level.primary") then print(GetTranslatedStringByKey("WARNING_PRIMARY")) end if gMissions[gMissionId].secondary~=GetInt("level.secondary") then print(GetTranslatedStringByKey("WARNING_SECONDARY")) end if gMissions[gMissionId].required~=GetInt("level.required") then print(GetTranslatedStringByKey("WARNING_REQUIRED")) end gShowTitle = true else for i=1,#gSandbox do if gSandbox[i].id == gMissionId then gMissions[gMissionId] = {} gMissions[gMissionId].title = gSandbox[i].name gMissions[gMissionId].desc = "loc@DESC_FREE_ROAM" gMissions[gMissionId].securityTitle="" gMissions[gMissionId].securityDesc="" gMissions[gMissionId].primary=0 gMissions[gMissionId].secondary=0 gMissions[gMissionId].required=0 gMissions[gMissionId].bonus = {} end end end gMissionTitle = gMissions[gMissionId].title spawnPos = GetPlayerPos() titleFade = 0 titleTimer = 0 if gShowTitle then titleFade = 1 titleTimer = 3 end gState = "" mapInit(gMissionId) Options.Tabs:Init() RegisterListenerTo("LanguageChanged", "onLanguageChanged") RegisterListenerTo("HudLoaded", "onHudLoaded") end function onLanguageChanged() if GetString("game.mod") ~= "" then gMissions[gMissionId].title = GetString("game.mod.title") gMissions[gMissionId].desc = GetString("game.mod.description") end end function onHudLoaded() if GetString("game.mod") ~= "" then gMissions[gMissionId].title = GetString("game.mod.title") gMissions[gMissionId].desc = GetString("game.mod.description") end end function drawTool() local isDesktop = IsRunningOnDesktop() --and not (LastInputDevice() == UI_DEVICE_GAMEPAD) UiPush() UiScale(gUiScaleUpFactor) UiTranslate(0, UiHeight()/gUiScaleUpFactor) UiAlign("top left") local currentTool = string.lower(GetString("game.player.tool")) if not gSelectedTool then gSelectedTool = currentTool gSelectedToolFlash = 1 gSelectedToolGroup = 0 else if gSelectedTool ~= currentTool then gSelectedTool = currentTool gSelectedToolFlash = 1 SetValue("gSelectedToolFlash", 0, "easeout", 0.3) end end gSelectedToolGroup = GetInt("game.player.tool.group") local toolSelect = GetBool("game.player.toolselect") if gToolSelectAlpha == 0 and toolSelect then SetValue("gToolSelectAlpha", 1, "easeout", 0.25) end if gToolSelectAlpha == 1 and not toolSelect then SetValue("gToolSelectAlpha", 0, "easein", 0.25) end if gToolSelectAlpha > 0 then UiPush() UiColorFilter(1,1,1, gToolSelectAlpha) UiScale(1, gToolSelectAlpha) local w = 180 local windowToolWidth = 200/gUiScaleUpFactor local toolSelectWidth = gToolSelectWidth local groupIdFrameWidth = 28 local currentToolItemHeight = 0 gToolSelectWidth = 0 for g=1, 6 do local empty = true UiPush() local offset = not isDesktop and groupIdFrameWidth + 15 or 0 UiTranslate(0, -106 - 15 + offset) UiTranslate(UiCenter() / gUiScaleUpFactor - toolSelectWidth / 2 + windowToolWidth + 20, 0) local toolList = {} local tools = ListKeys("game.tool") for i=1, #tools do local toolId = tools[i] local group = GetInt("game.tool."..toolId..".group") local enabled = GetBool("game.tool."..toolId..".enabled") if group == g and enabled then toolList[#toolList+1] = { id=toolId, index=GetInt("game.tool."..toolId..".index")} end end table.sort(toolList, function(a, b) return a.index < b.index end) Ui:MediumFont(isDesktop and 16 or 22) UiTextOutline(1, 1, 1, 1, 0.1) UiAlign("center") UiTextAlignment("center") UiWordWrap(isDesktop and (windowToolWidth - 10) or windowToolWidth) local toolsNames = {} local namesSizes = { w = {}, h = {} } for i=1,#toolList do local id = toolList[i].id local key = GetString("game.tool."..id..".name") local name = key if HasTranslationByKey(key) then name = GetTranslatedStringByKey(key) end toolsNames[i] = name namesSizes.w[i], namesSizes.h[i] = UiGetTextSize(name) end local currentToolIdx = 1 local windowHeight = 0 local renderFrom = 1 local renderTo = 5 if #toolList > 5 then for i=1, #toolList do if currentTool == toolList[i].id then currentToolIdx = i end end if currentToolIdx < gToolGroupScrollPos[g]+2 then gToolGroupScrollPos[g] = math.max(1, gToolGroupScrollPos[g] - 1) end if currentToolIdx > gToolGroupScrollPos[g]+2 then gToolGroupScrollPos[g] = math.min(#toolList-4, gToolGroupScrollPos[g] + 1) end if currentToolIdx < 3 then renderFrom = 1 renderTo = 5 elseif currentToolIdx > #toolList - 2 then renderFrom = #toolList - 4 renderTo = #toolList else renderFrom = currentToolIdx - 2 renderTo = currentToolIdx + 2 end for i=renderFrom,renderTo do currentToolItemHeight = namesSizes.h[i] + (isDesktop and 8 or 15) windowHeight = windowHeight + currentToolItemHeight end else gToolGroupScrollPos[g] = 1 windowHeight = 45 * 5 renderTo = #toolList end local toolItemWidth = 230 local toolItemHeight = 45 local isListOverscrolled = math.abs(currentToolIdx-gToolGroupScrollPos[g]) > 5 if isListOverscrolled then gToolGroupScrollPos[g] = currentToolIdx if currentToolIdx > gToolGroupScrollPos[g] then gToolGroupScrollSmooth[g] = currentToolIdx - 1 else gToolGroupScrollSmooth[g] = currentToolIdx + 1 end end UiPush() local groupNumberPos = -106 - groupIdFrameWidth -- window size UiTranslate(-0.5 * toolItemWidth, -windowHeight - (isDesktop and 0.7 * toolItemHeight or 0) - offset) UiWindow(toolItemWidth, windowHeight + toolItemHeight * 0.1, #toolList > 5) UiTranslateToScreenBottomEdge(0, groupNumberPos + 20) -- list scroll UiTranslate(0.5 * toolItemWidth, 0) local diff = gToolGroupScrollPos[g] - gToolGroupScrollSmooth[g] gToolGroupScrollSmooth[g] = gToolGroupScrollSmooth[g] + diff * 0.2 local bottomPadding = isDesktop and (-130 + diff*60) or ((-80 + diff*60) / gUiScaleUpFactor) UiTranslateToScreenBottomEdge(0, bottomPadding) for i=renderTo, renderFrom, -1 do local id = toolList[i].id -- distance between tools in list currentToolItemHeight = namesSizes.h[i] + (isDesktop and 8 or 15) UiTranslate(0, -currentToolItemHeight) if namesSizes.w[i] > windowToolWidth then windowToolWidth = namesSizes.w[i] end if #toolList <= 5 or (i >= renderFrom and i <= renderTo) then UiPush() local linesCount = math.floor(namesSizes.h[i] / UiFontHeight()) if (namesSizes.h[i] / UiFontHeight()) % 1.0 > 0.5 then linesCount = linesCount + 1 end linesCount = linesCount < 1 and 1 or linesCount if id == currentTool then UiTranslate(-0.75 * windowToolWidth, -0.75 * currentToolItemHeight) local clipHeight = isDesktop and 2 * currentToolItemHeight or 1.5 * currentToolItemHeight UiClipRect(1.5 * windowToolWidth, clipHeight) UiTranslate(0.75 * windowToolWidth, 0.75 * currentToolItemHeight) UiScale(1 + gSelectedToolFlash * 0.4) UiPush() UiColor(0,0,0,0.2) if isDesktop then if linesCount == 1 then UiTranslate(0, -currentToolItemHeight / 2) else UiTranslate(0, -UiFontHeight() / 2) end UiImageBox("common/box-solid-10.png", windowToolWidth, currentToolItemHeight, 8, 8) else UiTranslate(0, -UiFontHeight()) UiImageBox("common/box-solid-10.png", windowToolWidth, currentToolItemHeight, 10, 10) end UiPop() UiColor(1,1,0.5,1) else if g ~= gSelectedToolGroup then UiColorFilter(1, 1, 1, 0.6) end UiColor(1,1,1) end UiAlign("center bottom") if isDesktop then if linesCount == 1 then UiTranslate(0, namesSizes.h[i] / 2) else UiTranslate(0, 2 * (namesSizes.h[i] - UiFontHeight()) + 0.25 * UiFontHeight() - (linesCount - 1) * UiFontHeight()) end else UiTranslate(0, 2 * (namesSizes.h[i] - UiFontHeight()) + 0.3 * UiFontHeight() - (linesCount - 1) * UiFontHeight()) end UiText(toolsNames[i]) UiPop() end end w = windowToolWidth + 20 UiPop() UiPop() if #toolList > 0 then empty = false UiPush() if isDesktop then if g ~= gSelectedToolGroup then UiColorFilter(1, 1, 1, 0.6) end UiTranslate(0, groupNumberPos) UiTranslate(UiCenter()/gUiScaleUpFactor-toolSelectWidth/2 + windowToolWidth + 20, 0) UiAlign("center") UiTextAlignment("center") UiTranslate(-toolItemWidth/2, 0) UiImageBox("common/box-solid-6.png", groupIdFrameWidth, groupIdFrameWidth, 6, 6) UiColorFilter(1,1,1,1) UiColor(0,0,0) UiTranslate(0, 20) UiWordWrap(groupIdFrameWidth) Ui:BoldFont(16) UiText(tostring(g)) end UiPop() end if not empty then UiTranslate(w + 20, 0) gToolSelectWidth = gToolSelectWidth + w + 20 end end UiPop() end local t = currentTool UiTranslate(UiCenter()/gUiScaleUpFactor, -20) if currentTool ~= "none" then Ui:BoldFont(20) UiAlign("center bottom") local key = GetString("game.tool."..t..".name") local name = string.upper(key) if HasTranslationByKey(key.."_UPPERCASE") then name = GetTranslatedStringByKey(key.."_UPPERCASE") end UiText(name) UiTranslate(0, -30) local display = "" local ammo = GetInt("game.tool."..t..".ammo") --In sandbox mode, ammo is set to 9999, therefore do not display very large values if ammo < 9990 then if HasKey("game.tool."..t..".ammo.display") then display = GetString("game.tool."..t..".ammo.display") else display = tostring(ammo) end if display ~= "" then Ui:BoldFont(30) UiText(display) end end end UiPop() end function crosshair() if GetBool("game.player.tool.scope") then UiPush() UiAlign("center middle") local w = UiWidth() if w > 1920 then UiPush() local lb = (w-1920)*0.5+1 UiColor(0,0,0) UiAlign("top left") UiRect(lb, UiHeight()) UiTranslate(UiWidth(), 0) UiAlign("top right") UiRect(lb, UiHeight()) UiPop() end local h = UiHeight() if h > 1080 then UiPush() local lb = (h-1080)*0.5+1 UiColor(0,0,0) UiAlign("top left") UiRect(UiWidth(), lb) UiTranslate(0, UiHeight()) UiAlign("bottom left") UiRect(UiWidth(), lb) UiPop() end UiTranslate(UiCenter(), UiMiddle()); UiImage("hud/scope.png") UiPop() else if GetFloat("game.player.tool.zoom") > 1.0 then UiPush() local alpha = (GetFloat("game.player.tool.zoom") - 1.0) / (GetFloat("game.player.tool.targetzoom") - 1.0) UiColor(0.0, 0.0, 0.0, 0.5 * alpha) local w,h = UiGetImageSize("hud/steroid.png") if w > 0 and h > 0 then UiScale(UiWidth()/w, UiHeight()/h) UiImage("hud/steroid.png") end UiPop() end UiPush() UiAlign("center middle") UiTranslate(UiCenter(), UiMiddle()); local grabbing = GetBool("game.player.grabbing") if grabbing then UiPush() UiColor(1,1,1,0.75) UiTranslate(-3, -6) UiImage("hud/crosshair-hand.png") UiPop() end if not grabbing then if GetBool("hud.aimdot") then if GetBool("game.player.picking") then if GetBool("game.player.canusetool") then UiImage("hud/crosshair-ring.png") end end UiPush() UiImage("hud/crosshair-dot.png") UiPop() if GetString("game.player.tool") == "gun" then UiImage("hud/crosshair-gun.png") end if GetString("game.player.tool") == "shotgun" then UiImage("hud/crosshair-shotgun.png") end if GetString("game.player.tool") == "rocket" then UiImage("hud/crosshair-launcher.png") end end end UiPop() end SetBool("hud.aimdot", true) end function drawPickInfo() UiPush() local currentInfo = "" local pickBody = GetPlayerPickBody() if pickBody > 0 then --Prefer shape desc over body desc local shapeDesc = GetDescription(GetPlayerPickShape()) if shapeDesc ~= "" then currentInfo = shapeDesc else currentInfo = GetDescription(GetPlayerPickBody()) end --Only use part up to first semi-colon local i = string.find(currentInfo, ";") if i then currentInfo = string.sub(currentInfo, 1, i-1) currentInfo = trim(currentInfo) end if currentInfo ~= "" then currentInfo = GetTranslatedStringByKey(currentInfo) end --Add special info for targets and valuables if HasTag(pickBody, "target") then if currentInfo ~= "" then currentInfo = currentInfo .. " - " end if HasTag(pickBody, "optional") then currentInfo = currentInfo .. GetTranslatedStringByKey("CURRENTINFO_TARGET") else currentInfo = currentInfo .. GetTranslatedStringByKey("CURRENTINFO_PRIMARY_TARGET") end elseif HasTag(pickBody, "valuable") then local value = tonumber(GetTagValue(pickBody, "value")) if value > 0 then if currentInfo ~= "" then currentInfo = currentInfo .. " - " end currentInfo = currentInfo .. GetTranslatedStringByKey("CURRENTINFO_VALUE_$," .. value) end end end if GetBool("game.map.enabled") or GetBool("game.player.grabbing") or GetBool("game.player.usescreen") then currentInfo = "" end if currentInfo == "" then gPickInfoTimer = 0.5 end if gPickInfoTimer > 0 then gPickInfoTimer = gPickInfoTimer - 0.01667 currentInfo = "" end if currentInfo ~= "" then if gPickInfoText ~= currentInfo then gPickInfoText = currentInfo gPickInfoChars = 0 end if GetBool("game.player.idling") then if not gPickInfo then SetValue("gPickInfoAlpha", 1, "linear", 0.2) end gPickInfo = true end else if gPickInfo then SetValue("gPickInfoAlpha", 0, "linear", 0.2) end gPickInfo = false end local alpha = gPickInfoAlpha if alpha > 0 then UiTranslate(UiCenter(), UiHeight()-150 - 120*gToolSelectAlpha) UiScale(gUiScaleUpFactor) UiFont("bold.ttf", 28) UiWordWrap(1000) local w, h = UiGetTextSize(gPickInfoText) UiAlign("center middle") UiColor(0.5, 0.5, 0.5, 0.5) UiScale(1, alpha*0.5 + 0.5) UiColor(1,1,1,alpha*0.8) UiImageBox("common/box-solid-shadow-50.png", w+10, h, -50, -50) UiWindow(w+10, h) UiAlign("left") UiColor(0,0,0) UiTranslate(0, 22) gPickInfoChars = gPickInfoChars + 1 UiText(gPickInfoText, false, gPickInfoChars) end UiPop() end local function count_in_string(s, p) local count = 0 local i = 0 local step = string.len(p) while true do i = string.find(s, p, i) if i == nil then break end i = i + step count = count + 1 end return count end local ico = {} ico.__index = function(_, key) local ico = Ui.ActionIcons[key] ico.style = "white" return ico end setmetatable(ico, ico) function drawVehicle() UiPush() UiFont("bold.ttf", 20) UiTranslate(UiCenter(), UiHeight() - 40) local health = GetFloat("game.vehicle.health") UiTranslate(-100, 0) progressBar(200, 20, health) UiColor(1,1,1) UiTranslate(100, -12) UiAlign("center middle") UiText("loc@UI_TEXT_VEHICLE_CONDITION") UiPop() local interact = ico["driving:interact"] local handbrake = ico["driving:handbrake"] local action = ico["driving:vehicle_action"] local lmb = ico["driving:vehicle_raise"] local rmb = ico["driving:vehicle_lower"] local fwd = ico["driving:up"] local bwd = ico["driving:down"] local left = ico["driving:left"] local right = ico["driving:right"] local leftStick = ico["gamepad_left_stick"] if left == right then right = "" end local lmbrmb = lmb / rmb local vehicle = GetPlayerVehicle() local isGamepad = LastInputDevice() == UI_DEVICE_GAMEPAD local info = {} if HasTag(vehicle, "CustomVehicleAction") then if HasTag(vehicle, "CustomVehicleActionKey") then info[#info+1] = {icon = GetTagValue(vehicle, "CustomVehicleActionKey"), text =GetTranslatedStringByKey(GetTagValue(vehicle, "CustomVehicleAction"))} else info[#info+1] = {icon = action, text = GetTranslatedStringByKey(GetTagValue(vehicle, "CustomVehicleAction"))} end end if HasTag(vehicle, "cranetop") then info[#info+1] = {icon = left / right, text = "loc@UI_HUD_INFO_TURN"} info[#info+1] = {icon = fwd / bwd, text = "loc@UI_HUD_INFO_EXTEND"} info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_RAISE_LOWER"} info[#info+1] = {icon = action, text = "loc@UI_HUD_INFO_HOOK"} else info[#info+1] = {icon = fwd, text = "loc@UI_HUD_INFO_ACCELERATE"} info[#info+1] = {icon = bwd, text = "loc@UI_HUD_INFO_REVERSE"} if isGamepad then info[#info+1] = {icon = leftStick, text = "loc@UI_HUD_INFO_STEER"} else info[#info+1] = {icon = left / right, text = "loc@UI_HUD_INFO_STEER"} end if HasTag(vehicle, "cranebottom") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_SUPPORT_LEGS"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "crane") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_ARM"} info[#info+1] = {icon = action, text = "loc@UI_HUD_INFO_HOOK"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "dumptruck") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_BED"} if not HasTag(vehicle, "boat") then info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} end elseif HasTag(vehicle, "combine") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_THRESHER"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "frontloader") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_SHOVEL"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "skylift") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_LIFT"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "forklift") then info[#info+1] = {icon = lmbrmb, text = "loc@UI_HUD_INFO_FORK"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "tank") then info[#info+1] = {icon = lmb, text = "loc@UI_HUD_INFO_FIRE"} info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_BRAKE"} elseif HasTag(vehicle, "boat") then else info[#info+1] = {icon = handbrake, text = "loc@UI_HUD_INFO_HANDBRAKE"} end end if not HasTag(vehicle, "noexit") then info[#info+1] = {icon = interact, text = "loc@UI_HUD_INFO_EXIT_VEHICLE"} end UiPush() UiAlign("top left") local kw = 0 local vw = 0 for i=1, #info do UiFont("bold.ttf", 27) local w0 = 0 w0 = info[i].icon.w + 5 UiFont("regular.ttf", 27) local w1 = Ui.GetTextSize(info[i].text) kw = math.max(kw, w0) vw = math.max(vw, w1) end local iconSize = 42 local w = kw + vw + 60 local h = #info * (iconSize + 6) + 20 SetInt("game.hud.hintH", h + GetInt("game.hud.hintH")) UiTranslate(UiWidth() - w - 20, UiHeight() - h - 20 - healthFade * 50) UiColor(0,0,0,0.5) UiImageBox("common/box-solid-6.png", w, h, 6, 6) UiTranslate(kw + 25, 10) UiColor(1,1,1) for i=1, #info do local icon = info[i].icon local func = info[i].text do UiPush() UiTranslate(0, iconSize / 2) UiAlign("right middle") UiFont("regular.ttf", 30) UiTranslate(-icon.w - 10, -info[i].icon.h / 2) icon:draw() UiPop() end UiPush() UiTranslate(0, iconSize / 2) UiAlign("left middle") UiFont("regular.ttf", 27) UiColorFilter(1, 1, 1, 0.7) UiText(func) UiPop() UiTranslate(0, iconSize+6) end UiPop() end function photoModeGamepad() if not GetBool("game.paused") then gPhotoMode = false end if gPhotoMode then local firstFrame = false if GetInt("game.screenshot") == 0 then SetInt("game.screenshot", 1) gPhotoFov = GetFloat("options.gfx.fov") gPhotoTransform = GetCameraTransform() gPhotoRotPitch, gPhotoRotYaw, _ = GetQuatEuler(gPhotoTransform.rot) gPhotoRotRoll = 0 firstFrame = true end UiMute(1, GetBool("level.alarm")) if not gPhotoHideUi then drawPhotomode() end -- for photomode we do not want to draw a cursor if LastInputDevice() == UI_DEVICE_GAMEPAD then UiSetCursorState(UI_CURSOR_HIDE_AND_LOCK) end -- update parameters of generated UI PhotomodeOptions.showOptions = not gPhotoHideSettings gPhotoDofScale = PhotomodeOptions.DOFValue gPhotoBloom = PhotomodeOptions.BloomValue gPhotoExposure = PhotomodeOptions.ExposureValue gPhotoTool = PhotomodeOptions.showTool PhotomodeOptions.yawValue = tonumber(string.format("%.1f", gPhotoRotYaw)) PhotomodeOptions.pitchValue = tonumber(string.format("%.1f", gPhotoRotPitch)) PhotomodeOptions.rollValue = tonumber(string.format("%.1f", gPhotoRotRoll)) PhotomodeOptions.heightValue = tonumber(string.format("%.1f", gPhotoTransform.pos[2])) PhotomodeOptions.scaleValue = tonumber(string.format("%.1f", -(math.log(gPhotoFov/45)-2*math.log(2))/math.log(2))) if InputPressed("menu_accept") then if IsRunningOnPC() or IsRunningOnMac() then UiSound("hud/photo-shot.ogg") Command("game.screenshot") else gPhotoHideUi = not gPhotoHideUi end end if InputPressed("menu_cancel") then gPhotoMode = false gPhotoHideUi = false SetPaused(false) end -- because photomode is running in MODE_MENU, engine disables "lmb" and "rmb" actions for InputPressed and InputReleased check. -- so, we are using InputDown, for zoom. if GetTime() - gPhotoTimeFromLastZoomChange > 0.15 then -- zoom in if InputDown("lmb") then local fov = gPhotoFov local zoom = -(math.log(gPhotoFov/45)-2*math.log(2))/math.log(2) zoom = clamp(zoom + 0.1, 0.5, 3.0) fov = 180 / math.pow(2,zoom) gPhotoFov = clamp(fov, 20, 140) end -- zoom out if InputDown("rmb") then local fov = gPhotoFov local zoom = -(math.log(gPhotoFov/45)-2*math.log(2))/math.log(2) zoom = clamp(zoom - 0.1, 0.5, 3.0) fov = 180 / math.pow(2,zoom) gPhotoFov = clamp(fov, 20, 140) end gPhotoTimeFromLastZoomChange = GetTime() end if InputPressed("mmb") then gPhotoHideSettings = not gPhotoHideSettings end if InputPressed("interact") then --- TBD: focus local ct = GetCameraTransform() local x,y = UiWidth() / 2, UiHeight() / 2 local dir = UiPixelToWorld(x, y) local hit, dist = QueryRaycast(ct.pos, dir, 1000) local oldDepth = gPhotoDofDepth local newDepth if hit then local fwd = TransformToParentVec(ct, Vec(0,0, -1)) newDepth = VecDot(VecScale(dir, dist), fwd) else newDepth = 300 end SetValue("gPhotoDofDepth", newDepth, "cosine", 0.25) local vol = clamp(math.abs(1.0/newDepth - 1.0/oldDepth)*10.0, 0.1, 1.0) UiSound("hud/photo-focus"..math.random(0, 2)..".ogg", vol) end local exposure = math.pow(gPhotoExposure, 5) local bloom = math.pow(gPhotoBloom, 5) if math.abs(GetFloat("game.screenshot.dof.min")-gPhotoDofDepth) > 0.01 or math.abs(GetFloat("game.screenshot.dof.max")-gPhotoDofDepth) > 0.01 or math.abs(GetFloat("game.screenshot.dof.scale")-gPhotoDofScale) > 0.01 or math.abs(GetFloat("game.screenshot.exposure")-exposure) > 0.01 or math.abs(GetFloat("game.screenshot.bloom")-bloom) > 0.01 or math.abs(GetFloat("game.screenshot.fov")-gPhotoFov) > 0.001 or GetBool("game.screenshot.tool") ~= gPhotoTool then SetInt("game.screenshot", 1) end SetFloat("game.screenshot.dof.min", gPhotoDofDepth) SetFloat("game.screenshot.dof.max", gPhotoDofDepth) SetFloat("game.screenshot.dof.scale", gPhotoDofScale) SetFloat("game.screenshot.exposure", exposure) SetFloat("game.screenshot.bloom", bloom) SetFloat("game.screenshot.fov", gPhotoFov) SetBool("game.screenshot.tool", gPhotoTool) -- move local move = Vec() move[1] = InputValue("mousedx") * 0.03 / (math.max(GetInt("options.input.controller_sensitivity_x"), 1) / 100.0) move[3] = InputValue("mousedy") * 90 / gPhotoFov * 0.03 / (math.max(GetInt("options.input.controller_sensitivity_y"), 1) / 100.0) if InputPressed("zoom") and gPhotoSpeed == 1.0 then gPhotoSpeed = 2.5 elseif InputPressed("zoom") and gPhotoSpeed == 2.5 then gPhotoSpeed = 1.0 end if VecLength(move) > 0 then local speed = 5.0 speed = speed * gPhotoSpeed move = VecScale(move, speed * GetTimeStep()) gPhotoTransform.pos = VecAdd(gPhotoTransform.pos, TransformToParentVec(gPhotoTransform, move)) SetInt("game.screenshot", 1) end local mdx = 0 local mdy = 0 -- rotate if InputDown("up") then mdy = mdy - 1 * gPhotoFov/90 end if InputDown("down") then mdy = mdy + 1 * gPhotoFov/90 end if InputDown("left") then mdx = mdx - 1 * gPhotoFov/90 end if InputDown("right") then mdx = mdx + 1 * gPhotoFov/90 end if mdx ~= 0 or mdy ~= 0 then gPhotoRotYaw = gPhotoRotYaw - mdx gPhotoRotPitch = clamp(gPhotoRotPitch - mdy, -80, 80) SetInt("game.screenshot", 1) end -- tilt mdx = 0 if InputDown("menu_next") then mdx = mdx + 1 end if InputDown("menu_prev") then mdx = mdx - 1 end if mdx ~= 0 then gPhotoRotRoll = clamp(gPhotoRotRoll - mdx, -30, 30) SetInt("game.screenshot", 1) end gPhotoTransform.rot = QuatEuler(gPhotoRotPitch, gPhotoRotYaw, 0) gPhotoTransform.rot = QuatRotateQuat(gPhotoTransform.rot, QuatEuler(0, 0, gPhotoRotRoll)) if not firstFrame then SetCameraTransform(gPhotoTransform, gPhotoFov) end else SetInt("game.screenshot", 0) end end function photoModeMouse() if not GetBool("game.paused") then gPhotoMode = false end if gPhotoMode then local firstFrame = false if GetInt("game.screenshot") == 0 then SetInt("game.screenshot", 1) gPhotoFov = GetFloat("options.gfx.fov") gPhotoTransform = GetCameraTransform() gPhotoRotPitch, gPhotoRotYaw, _ = GetQuatEuler(gPhotoTransform.rot) gPhotoRotRoll = 0 firstFrame = true end UiMute(1, GetBool("level.alarm")) drawPhotomode() -- update parameters of generated UI PhotomodeOptions.showOptions = not gPhotoHideSettings gPhotoDofScale = PhotomodeOptions.DOFValue gPhotoBloom = PhotomodeOptions.BloomValue gPhotoExposure = PhotomodeOptions.ExposureValue gPhotoTool = PhotomodeOptions.showTool PhotomodeOptions.yawValue = tonumber(string.format("%.1f", gPhotoRotYaw)) PhotomodeOptions.pitchValue = tonumber(string.format("%.1f", gPhotoRotPitch)) PhotomodeOptions.rollValue = tonumber(string.format("%.1f", gPhotoRotRoll)) PhotomodeOptions.heightValue = tonumber(string.format("%.1f", gPhotoTransform.pos[2])) PhotomodeOptions.scaleValue = tonumber(string.format("%.1f", -(math.log(gPhotoFov/45)-2*math.log(2))/math.log(2))) local width = 840 local height = 390 -- settings if InputPressed("space") then gPhotoHideSettings = not gPhotoHideSettings end -- screenshot if InputPressed("return") then UiSound("hud/photo-shot.ogg") Command("game.screenshot") end -- zoom local deltaZoom = InputValue("mousewheel") if deltaZoom ~= 0 then local fov = gPhotoFov local zoom = -(math.log(gPhotoFov/45)-2*math.log(2))/math.log(2) zoom = clamp(zoom + deltaZoom * 0.1, 0.5, 3.0) fov = 180 / math.pow(2,zoom) gPhotoFov = clamp(fov, 20, 140) end -- focus local mouseInRect = false UiPush() UiTranslateToPositionOnScreen(60, 70) mouseInRect = UiIsMouseInRect(width, height) UiPop() if InputPressed("lmb") and not (mouseInRect and not gPhotoHideSettings) then local ct = GetCameraTransform() local x,y = UiGetMousePos() local dir = UiPixelToWorld(x, y) local hit, dist = QueryRaycast(ct.pos, dir, 1000) local oldDepth = gPhotoDofDepth local newDepth if hit then local fwd = TransformToParentVec(ct, Vec(0,0, -1)) newDepth = VecDot(VecScale(dir, dist), fwd) else newDepth = 300 end SetValue("gPhotoDofDepth", newDepth, "cosine", 0.25) local vol = clamp(math.abs(1.0/newDepth - 1.0/oldDepth)*10.0, 0.1, 1.0) UiSound("hud/photo-focus"..math.random(0, 2)..".ogg", vol) end local exposure = math.pow(gPhotoExposure, 5) local bloom = math.pow(gPhotoBloom, 5) if math.abs(GetFloat("game.screenshot.dof.min")-gPhotoDofDepth) > 0.01 or math.abs(GetFloat("game.screenshot.dof.max")-gPhotoDofDepth) > 0.01 or math.abs(GetFloat("game.screenshot.dof.scale")-gPhotoDofScale) > 0.01 or math.abs(GetFloat("game.screenshot.exposure")-exposure) > 0.01 or math.abs(GetFloat("game.screenshot.bloom")-bloom) > 0.01 or math.abs(GetFloat("game.screenshot.fov")-gPhotoFov) > 0.001 or GetBool("game.screenshot.tool") ~= gPhotoTool then SetInt("game.screenshot", 1) end SetFloat("game.screenshot.dof.min", gPhotoDofDepth) SetFloat("game.screenshot.dof.max", gPhotoDofDepth) SetFloat("game.screenshot.dof.scale", gPhotoDofScale) SetFloat("game.screenshot.exposure", exposure) SetFloat("game.screenshot.bloom", bloom) SetFloat("game.screenshot.fov", gPhotoFov) SetBool("game.screenshot.tool", gPhotoTool) local move = Vec() if InputDown("up") then move[3] = move[3] - 90/gPhotoFov end if InputDown("down") then move[3] = move[3] + 90/gPhotoFov end if InputDown("left") then move[1] = move[1] - 1 end if InputDown("right") then move[1] = move[1] + 1 end if InputDown("shift") then gPhotoSpeed = 2.5 else gPhotoSpeed = 1.0 end if VecLength(move) > 0 then local speed = 5.0 speed = speed * gPhotoSpeed move = VecScale(move, speed * GetTimeStep()) gPhotoTransform.pos = VecAdd(gPhotoTransform.pos, TransformToParentVec(gPhotoTransform, move)) SetInt("game.screenshot", 1) end if InputDown("rmb") then local mdx = InputValue("mousedx") * 0.15 * gPhotoFov/90 local mdy = InputValue("mousedy") * 0.15 * gPhotoFov/90 if mdx ~= 0 or mdy ~= 0 then gPhotoRotYaw = gPhotoRotYaw - mdx gPhotoRotPitch = clamp(gPhotoRotPitch - mdy, -80, 80) SetInt("game.screenshot", 1) end elseif InputDown("mmb") then local mdx = InputValue("mousedx") * 0.15 if mdx ~= 0 then gPhotoRotRoll = clamp(gPhotoRotRoll - mdx, -30, 30) SetInt("game.screenshot", 1) end end gPhotoTransform.rot = QuatEuler(gPhotoRotPitch, gPhotoRotYaw, 0) gPhotoTransform.rot = QuatRotateQuat(gPhotoTransform.rot, QuatEuler(0, 0, gPhotoRotRoll)) if not firstFrame then SetCameraTransform(gPhotoTransform, gPhotoFov) end else SetInt("game.screenshot", 0) gPhotoHideUi = false end end function photoMode() local curinput = GetString("game.input.curdevice") if curinput == "mouse" then photoModeMouse() else photoModeGamepad() end end local SPACE = 10 local SPACE_PC = 6 local BUTTON_HEIGHT = 42 local HINT_OFFSET = 55 local BUTTON_WIDTH = 300 local SIDE_PADDING = 35 local BOTTOM_PADDING = 50 local TOP_TEXT_PADDING = 25 local BOTTOM_TEXT_PADDING = 25 local TITLE_HEIGHT = 36 local SUBTITLE_HEIGHT = 27 local PAUSE_WIDTH = BUTTON_WIDTH + SIDE_PADDING * 2 function drawPauseMenu(items, subtitle) if IsRunningOnPC() or IsRunningOnMac() then SPACE = SPACE_PC end local height = 0 for index = 1,#items do local value = items[index] if value.text ~= nil and value.onPressed ~= nil then height = height + BUTTON_HEIGHT end if index > 1 then height = height + SPACE end end local bottomSpace = BOTTOM_PADDING local topSpace = TITLE_HEIGHT + TOP_TEXT_PADDING + BOTTOM_TEXT_PADDING if LastInputDevice() == UI_DEVICE_GAMEPAD then bottomSpace = bottomSpace + HINT_OFFSET end if subtitle then topSpace = topSpace + SUBTITLE_HEIGHT end UiPush() UiAlign("center middle") UiTranslateToScreenCenter(0, 0) UiImageBox("common/box-solid-shadow-50.png", PAUSE_WIDTH, height + topSpace + bottomSpace, -50, -50) UiWindow(PAUSE_WIDTH, height + topSpace + bottomSpace) UiAlign("center top") UiColor(0.96, 0.96, 0.96) UiTranslate(PAUSE_WIDTH / 2, 0) UiPush() UiFont("bold.ttf", 36) UiTranslate(0, TOP_TEXT_PADDING) UiText("loc@PAUSE_UPPERCASE") if subtitle then UiTranslate(0, TITLE_HEIGHT) UiFont("bold.ttf", 27) UiText(subtitle) end UiPop() UiTranslate(0, topSpace) UiButtonImageBox("common/box-outline-fill-6.png", 6, 6, 0.96, 0.96, 0.96, 0.8) UiFont("regular.ttf", 27) for index = 1,#items do local value = items[index] if value.text ~= nil and value.onPressed ~= nil then UiPush() if value.disabled ~= nil and value.disabled then UiDisableInput() UiIgnoreNavigation() UiColorFilter(1,1,1,0.5) end if (value.backgroundColor) then UiPush() UiColorFilter(value.backgroundColor.r, value.backgroundColor.g, value.backgroundColor.b, value.backgroundColor.a) UiImageBox("common/rect_c#ffffff_o1.00_cr3.png", BUTTON_WIDTH - 4, BUTTON_HEIGHT - 2, 4, 4) UiPop() end if UiTextButton(value.text, BUTTON_WIDTH, BUTTON_HEIGHT) then UiSound("common/click.ogg") value.onPressed() end if value.hint then UiPush() UiTranslate(BUTTON_WIDTH / 2 + 5, BUTTON_HEIGHT / 2) drawHintArrow(GetTranslatedStringByKey(value.hint)) UiPop() elseif value.tooltip then UiTranslate(PAUSE_WIDTH / 2 + 20, BUTTON_HEIGHT / 2) drawTooltip(value.tooltip) end UiPop() UiTranslate(0, BUTTON_HEIGHT) elseif value == "skip" then -- space between buttons groups ("skip" items) UiTranslate(0, SPACE * 2) end UiTranslate(0, SPACE) end if LastInputDevice() == UI_DEVICE_GAMEPAD then UiFont("regular.ttf", 27) local textSelect = "loc@UI_BUTTON_SELECT" local textBack = "loc@UI_BUTTON_BACK" UiTranslate(0, HINT_OFFSET) UiDrawHintsCentered({ { ico = "[[menu:menu_accept;iconsize=42,42]]", txt = textSelect }, { ico = "[[menu:menu_cancel;iconsize=42,42]]", txt = textBack } }, 20) end UiPop() -- close pause menu if user clicked outside of it UiPush() local isMouseCursorOverMenu = not UiIsMouseInRect(PAUSE_WIDTH + 20, height + topSpace + bottomSpace + 20) UiTranslateToScreenBottomEdge(0, -59) local isMouseCursorOverBottomMenu = not UiIsMouseInRect(UiWidth(), 118) if InputPressed("lmb") and isMouseCursorOverMenu and isMouseCursorOverBottomMenu then SetPaused(false) end UiPop() end function pauseMenu() local paused = GetBool("game.paused") local gamepadControl = false if GetString("game.input.curdevice") == "gamepad" then gamepadControl = true end if gActiveMods then paused = false elseif gPhotoMode then paused = false else gPhotoHideSettings = false end if GetBool("hud.disablepausemenu") then paused = false ClearKey("hud.disablepausemenu") end if paused and not gPaused then SetValue("pauseMenuAlpha", 1, "easeout", 0.25) gPaused = true UiSound("hud/pause-on.ogg") optionsAlpha = 0 UiForceFocus("") end if not paused and gPaused then if optionsAlpha == 0 then SetValue("pauseMenuAlpha", 0, "easein", 0.25) gPaused = false UiSound("hud/pause-off.ogg") else SetPaused(true) end end local visible = pauseMenuAlpha if visible == 0 then return end -- for pause menu we do not want to draw a cursor if LastInputDevice() == UI_DEVICE_GAMEPAD then UiSetCursorState(UI_CURSOR_HIDE_AND_LOCK) end UiModalBegin() --Mute world sound. Also mute music if in alarm mode UiMute(visible, GetBool("level.alarm")) UiBlur(visible) drawLocalUserTag{withBackground=true} UiPush() if visible < 1 then UiDisableInput() UiIgnoreNavigation() end UiColor(0.7,0.7,0.7, 0.25*visible) UiRect(UiWidth(), UiHeight()) UiColorFilter(1,1,1,visible*(1-optionsAlpha-intentConfirmAlpha)) local mouseInBottomBar = false UiPush() UiTranslate(0, UiHeight()-80) mouseInBottomBar = UiIsMouseInRect(UiWidth(), 80) UiPop() UiTranslate(UiCenter(), UiMiddle()) UiAlign("center middle") UiColor(.0, .0, .0, 0.6*visible) UiScale(gUiScaleUpFactor) UiScale(1, visible) local top_ids = ListKeys("game.pausemenu.mainmenu.main_top") local bottom_ids = ListKeys("game.pausemenu.mainmenu.main_top") local modPrimary = GetString("game.pausemenu.primary") if (optionsAlpha == 0 and not UiIsMouseInRect(UiWidth(), UiHeight()) and not mouseInBottomBar and (InputPressed("menu_cancel") or (InputPressed("lmb") and GetBool("game.cursor.enabled")))) then SetPaused(false) end local table = {} table[#table + 1] = { text = "loc@UI_BUTTON_CONTINUE", backgroundColor = { r = 0.56, g = 0.87, b = 0.66, a = 0.38 }, onPressed = function () SetPaused(false) end } table[#table+1] = { disabled = GetBool("level.alarm") or GetBool("level.dispatch") or GetBool("level.disablequicksave") or GetBool("game.quicksaveinprogress") or GetFloat("game.player.health") == 0, text = "loc@UI_BUTTON_QUICK_SAVE", onPressed = function () Command("game.quicksave") SetPaused(false) end, hint = GetBool("hud.quicksavehint") and not GetBool("game.canquickload") and not GetBool("game.quicksaveinprogress") and "loc@USE_QUICKSAVE_BEFORE_ALARM" or nil } if IsRunningOnPC() or IsRunningOnMac() then table[#table+1] = "skip" end local canDrawQlTooltip = not GetBool("game.canquickload") and GetString("game.quickloaderror") == "ErrorModsUsed" and optionsAlpha ~= 1 table[#table+1] = { disabled = not GetBool("game.canquickload"), text = "loc@UI_BUTTON_QUICK_LOAD", tooltip = canDrawQlTooltip and { colorFilter = { 1, 1, 1, visible * (1 - optionsAlpha - intentConfirmAlpha) }, scale = { 1, visible }, header = "", description = "loc@UI_SAVE_UNAVAILABLE", adaptWidth = true, anchorInMiddle = true, showProtrusion = true } or nil, onPressed = function () stopRecording() Command("game.quickload") end } if gMissionMode then table[#table+1] = { text = "loc@UI_BUTTON_RESTART_MISSION", onPressed = function () stopRecording() Restart() end } table[#table+1] = { text = "loc@UI_BUTTON_ABORT_MISSION", onPressed = function () stopRecording() exitMission() end } else table[#table+1] = { text = "loc@UI_BUTTON_RESTART", onPressed = function () stopRecording() Restart() end } end if IsRunningOnPC() or IsRunningOnMac() then table[#table+1] = "skip" end table[#table+1] = { text = "loc@UI_BUTTON_OPTIONS", onPressed = function () SetValue("optionsAlpha", 1, "easeout", 0.25) end } for i = 1, #bottom_ids do local id = bottom_ids[i] local mainText = GetString("game.pausemenu.mainmenu.main_top." .. id) if mainText ~= "" then table[#table+1] = { text = mainText, onPressed = function () SetPaused(false) Command("game.pausemenu", id, "main_top") end } end end table[#table+1] = { text = "loc@UI_BUTTON_MAIN_MENU", onPressed = Menu } if modPrimary ~= "" then local id = modPrimary local title = GetTranslatedStringByKey(GetString("game.pausemenu.items."..id)) if #title > 16 then title = string.sub(title, 1, 14).."..." end table[#table+1] = { text = title, onPressed = function () SetPaused(false) Command("game.pausemenu", id) end } end drawPauseMenu(table, #top_ids > 0 and "loc@UI_TEXT_ART_VANDALS" or nil) UiPop() UiPush() if optionsAlpha < 0.1 and intentConfirmAlpha < 0.1 then gPauseBarScroll = gPauseBarScroll - InputValue("mousewheel") * 100 if InputPressed("left") then gPauseBarScroll = gPauseBarScroll - 100 end if InputPressed("right") then gPauseBarScroll = gPauseBarScroll + 100 end if gPauseBarWidth < UiWidth() then gPauseBarScroll = 0 else gPauseBarScroll = clamp(gPauseBarScroll, 0, gPauseBarWidth-UiWidth()) end gPauseBarScrollSmooth = gPauseBarScrollSmooth + (gPauseBarScroll - gPauseBarScrollSmooth) * 0.3 UiTranslate(0, UiHeight()-(gUiScaleUpFactor*80*visible)) if LastInputDevice() == UI_DEVICE_GAMEPAD then UiColor(0,0,0, 0.6) UiTranslate(40, 0) UiPush() UiTranslate(0, -17) UiImageBox("ui/common/box-solid-10.png", UiWidth()-80, 58, 10, 10) UiPop() UiTranslate(0, -10) else UiPush() UiTranslate(0, -38) UiImageBox("common/pause-bottom-gradient.png", gUiScaleUpFactor*UiWidth(), gUiScaleUpFactor*118, 0, 0) UiPop() end local left = math.min(UiWidth()*0.5, gPauseBarWidth*0.5) + gPauseBarScrollSmooth UiTranslate(UiCenter()-left, 0) UiPush() UiScale(gUiScaleUpFactor) UiAlign("top left") UiFont("regular.ttf", 27) UiColor(0.96, 0.96, 0.96) local maxWidth = 240 local textPhotoMode = "loc@UI_BUTTON_PHOTO_MODE" local buttonSize = GetTextSize(textPhotoMode) local textActiveMods = GetTranslatedStringByKey("UI_TEXT_ACTIVE_MODS") local sizeActiveMods = UiGetTextSize(textActiveMods) if buttonSize > maxWidth then maxWidth = buttonSize end if sizeActiveMods > maxWidth then maxWidth = sizeActiveMods end local itemTitleLengthLimit = 25 modPrimary = GetString("game.pausemenu.primary") local items = ListKeys("game.pausemenu.items") local titles = {} for i=1, #items do local id = items[i] if id ~= modPrimary then titles[i] = GetTranslatedStringByKey(GetString("game.pausemenu.items."..id)) if UiGetSymbolsCount(titles[i]) > itemTitleLengthLimit then titles[i] = UiTextSymbolsSub(titles[i], 1, itemTitleLengthLimit) .. "..." end local textWidth,_ = UiGetTextSize(titles[i]) local itemWidth = textWidth + 20 if itemWidth > maxWidth then maxWidth = itemWidth end end end UiTranslate(20, 0) UiButtonImageBox("common/box-outline-fill-6.png", 6, 6, 0.96, 0.96, 0.96, 0.8) local hints = {} if gamepadControl then if InputPressed("photomode") then gPhotoMode = true end hints[#hints + 1] = { ico = "[[menu:gamepad_button_l3;iconsize=32,32]] + [[menu:gamepad_button_r3;iconsize=32,32]]", txt = textPhotoMode } else local panelWidth = ((#items + 2) * (maxWidth + 20) + 40) * gUiScaleUpFactor if panelWidth > UiWidth() then local maxPanelWidth = (((7 * (maxWidth + 20)) + 40) * gUiScaleUpFactor) local scale = UiWidth() / maxPanelWidth UiScale(scale) end if UiTextButton(textPhotoMode, maxWidth, BUTTON_HEIGHT) then gPhotoMode = true end UiTranslate(maxWidth+20, 0) end local mods = ListKeys("mods.available") local activeMod = false for i=1, #mods do local active = GetBool("mods.available."..mods[i]..".active") if active then activeMod = true break end end if activeMod then if gamepadControl then if InputPressed("map") then gActiveMods = true end hints[#hints + 1] = { ico = "[[menu:map;iconsize=42,42]]", txt = textActiveMods } else if UiTextButton(textActiveMods, maxWidth, BUTTON_HEIGHT) then gActiveMods = true end UiTranslate(maxWidth+20, 0) end end local actionNames = { ["loc@CHANGE_WEATHER"] = "interact", ["loc@SPAWN"] = "mmb", ["loc@UI_TEXT_CREATIVE_MODE"] = "menu_prev", ["loc@SCREEN_RECORDER"] = "menu_next", ["loc@RETURN_HOME"] = "zoom"} -- localization isn't calculated for table keys automatically, so we do it manually for key, value in pairs(actionNames) do actionNames[GetTranslatedStringByKey(key)] = value actionNames[key] = nil end local isButtonsUsed = { ["interact"] = false, ["mmb"] = false, ["menu_prev"] = false, ["menu_next"] = false, ["zoom"] = false } local modsToAdd = {} for i=1, #items do local id = items[i] if id ~= modPrimary then local textButton = titles[i] if gamepadControl then if actionNames[titles[i]] ~= nil then if InputPressed(actionNames[titles[i]]) then SetPaused(false) Command("game.pausemenu", id) end hints[#hints + 1] = { ico = "[[menu:" .. actionNames[titles[i]] .. ";iconsize=42,42]]", txt = textButton } isButtonsUsed[actionNames[titles[i]]] = true else modsToAdd[#modsToAdd + 1] = {text = textButton, cmd = id} end else if UiTextButton(textButton, maxWidth, BUTTON_HEIGHT) then SetPaused(false) Command("game.pausemenu", id) end UiTranslate(maxWidth+20, 0) end end end -- for i=1, #modsToAdd do -- find avaliable button local button = "" for bt, st in pairs(isButtonsUsed) do if st == false then button = bt break end end -- if no button found - break if button == "" then break end isButtonsUsed[button] = true -- otherwise - map button if InputPressed(button) then SetPaused(false) Command("game.pausemenu", modsToAdd[i].cmd) end hints[#hints + 1] = { ico = "[[menu:" .. button .. ";iconsize=42,42]]", txt = modsToAdd[i].text } end if gamepadControl then UiTranslate(0, 20) UiDrawHintsCentered(hints) end gPauseBarWidth,_ = UiGetRelativePos() UiPop() end UiPop() SetBool("game.menu.active", false) if not drawOptions(optionsAlpha) and optionsAlpha == 1.0 then SetValue("optionsAlpha", 0, "easein", 0.25) end drawIntentConfirm() UiModalEnd() end function notify(str, t) SetValue("notificationAlpha", 1, "linear", 0.25) gNotification = str gNotificationIcons = {} gNotificationTimer = t gHidingNotifications = false end function notifyIcons(str, icons, t) SetValue("notificationAlpha", 1, "linear", 0.25) gNotification = str gNotificationIcons = icons gNotificationTimer = t gHidingNotifications = false end function hideNotifications() if notificationAlpha > 0 and not gHidingNotifications then SetValue("notificationAlpha", 0, "linear", 0.25) gHidingNotifications = true end end function notifications() local n = notificationAlpha SetBool("hud.hasnotification", n > 0) if n > 0 then UiPush() UiAlign("center middle") UiFont("bold.ttf", 27) UiScale(gUiScaleUpFactor) local iconSize = 42 local padding = 5 if #gNotificationIcons > 0 then local w1, _ = UiGetTextSize(gNotification) local s1 = "" local s2 = "" local idx = string.find(gNotification, "#") if idx ~= nil then if idx > 1 then s1 = string.sub(gNotification, 1, idx - 1) end if idx < string.len(gNotification) then s2 = string.sub(gNotification, idx + 1, -1) end else s1 = gNotification s2 = "" end w1 = 0 if s1 ~= "" then w1 = UiGetTextSize(s1) end local w2 = 0 if s2 ~= "" then w2 = UiGetTextSize(s2) end local totalw = w1 + w2 + 35 + iconSize * #gNotificationIcons local margin = (totalw - 422)/4 UiTranslateToWindowCenter(-margin, 100) UiColor(0, 0, 0, 0.7 * n) UiImageBox("common/box-solid-10.png", totalw, iconSize + 2 * padding, 10, 10) UiAlign("left middle") UiColor(1, 1, 1, n) UiTranslate(-totalw / 2 + 12, 0) if s1 ~= "" then UiText(s1) end UiTranslate(w1 + 5, 0) for i=1, #gNotificationIcons do UiPush() UiColor(1, 1, 1, n) local iconSizeScale = 1.0 if IsRunningOnSteamDeck() then iconSizeScale = 0.9 end local specialCase = string.sub(gNotificationIcons[i], 1, 1) == "$" if specialCase then local icoSize = iconSize * iconSizeScale local specialString = string.format( "[[%s;iconsize=%i,%i]]", string.sub(gNotificationIcons[i], 2, #gNotificationIcons[i]), icoSize, icoSize) UiText(specialString) else UiImageBox(gNotificationIcons[i], iconSize * iconSizeScale, iconSize * iconSizeScale, 0, 0) end UiPop() UiTranslate(iconSize + 2, 0) end --UiTranslate(0, 16) if s2 ~= "" then UiText(s2) end else UiTranslateToWindowCenter(0, 100) local w,h = UiGetTextSize(gNotification) UiColor(0, 0, 0, 0.7 * n) UiImageBox("common/box-solid-10.png", w + 32, h + 16, 10, 10) UiColor(1,1,1, n) UiText(gNotification) end UiPop() end if gNotificationTimer > 0 then gNotificationTimer = gNotificationTimer - GetTimeStep() if gNotificationTimer <= 0 then gNotificationTimer = 0 SetValue("notificationAlpha", 0, "linear", 0.5) end end end function interactInfo() -- if we have interactive UI on the screen there is no point to draw hints for objects if GetBool("game.ui.interactive") then return end local info = "" local body = GetPlayerInteractBody() if body ~= 0 then local pos local shape = GetPlayerInteractShape() if HasTag(shape, "interact") then local mi, ma = GetShapeBounds(shape) pos = VecLerp(mi, ma, 0.5) info = GetTagValue(shape, "interact") else pos = GetBodyTransform(body).pos info = GetTagValue(body, "interact") end if HasTag(body, "valuable") then --Special case for valuables, place interact info in body center local mi, ma = GetBodyBounds(body) pos = VecLerp(mi, ma, 0.5) end if not HasTag(shape, "interact") and not HasTag(body, "interact") then local v = GetBodyVehicle(body) if v ~= 0 then --Special case for vehicles, place interact info on driver position info = GetTranslatedStringByKey("INFO_DRIVE_VEHICLE") pos = TransformToParentPoint(GetVehicleTransform(v), GetVehicleDriverPos(v)) end end UiPush() UiScale(gUiScaleUpFactor) local x, y, d = UiWorldToPixel(pos) if d > 0 then UiFont("bold.ttf", 24) local str = GetString("game.input.interact.icon") if str ~= "" then UiAlign("center middle") UiTranslate(x, y) UiColor(1,1,1) UiImageBox(str, 42, 42, 0, 0) UiColor(0,0,0) if info ~= "" then UiFont("bold.ttf", 22) UiColor(1,1,1) UiTranslate(0, 40) UiText(info) end else str = string.upper(GetString("game.input.interact")) local w = 23 + UiGetTextSize(str) UiAlign("center middle") UiTranslate(x, y) UiImageBox("common/box-solid-6.png", w, 34, 6, 6) UiColor(0,0,0) UiText(str) if info ~= "" then UiFont("bold.ttf", 22) UiColor(1,1,1) UiTranslate(0, 34) UiText(info) end end end UiPop() end end function handleCommand(cmd, arg0, arg1, arg2) if cmd == "quicksave" then notify(GetTranslatedStringByKey("PROGRESS_SAVED"), 1.4) end if cmd == "quickload" then notify(GetTranslatedStringByKey("PROGRESS_RESTORED"), 1.4) titleFade = 0 titleTimer = 0 end if cmd == "intent" then handleIntent() end end function handleIntent() if GetString("options.intent") == "campaign" and GetBool("level.campaign") then return elseif GetString("options.intent") == "challenge_mansion_race" and GetString("game.levelid") == "mansion_race" then return end intentConfirmAlpha = 0 SetValue("intentConfirmAlpha", 1, "easeout", 0.25) SetPaused(true) end function drawIntentConfirm() if intentConfirmAlpha and intentConfirmAlpha > 0 then UiBlur(intentConfirmAlpha) UiPush() UiColor(1, 1, 1, 0.1*intentConfirmAlpha) UiRect(UiWidth(), UiHeight()) UiPop() UiTranslate(UiCenter(), UiMiddle()) UiModalBegin() UiPush() UiTranslate(0, 400*(1-intentConfirmAlpha)) UiScale(intentConfirmAlpha) UiColorFilter(1, 1, 1, intentConfirmAlpha) UiColor(.0, .0, .0, 0.55) UiAlign("center middle") UiImageBox("common/box-solid-shadow-50.png", 500, 200, -50, -50) UiWindow(500, 200) UiColor(0.9, 0.9, 0.9) UiPush() UiTranslate(UiCenter(), 30) UiFont("bold.ttf", 32) UiText("loc@UI_TEXT_ARE_YOU") UiPop() UiPush() UiTranslate(50, 70) UiAlign("left") UiWordWrap(400) UiColor(1,1,1) UiFont("regular.ttf", 20) UiText("loc@UI_TEXT_WARNING_UNSAVED") UiPop() UiFont("regular.ttf", 24) UiButtonImageBox("common/box-outline-6.png", 6, 6, 1, 1, 1, 0.5) UiPush() UiTranslate(130, UiHeight()-40) UiColor(1, 1, 1) if UiTextButton("loc@UI_BUTTON_CANCEL", 200, 40) or InputPressed("esc") then SetValue("intentConfirmAlpha", 0, "easein", 0.25) SetPaused(false) end UiPop() UiPush() UiTranslate(UiWidth()-130, UiHeight()-40) UiColor(1, 1, 1) if UiTextButton("loc@UI_BUTTON_OK", 200, 40) then SetBool("options.intentConfirmed", true) SetValue("intentConfirmAlpha", 0, "easein", 0.25) SetPaused(false) end UiPop() UiPop() UiModalEnd() end end function drawCash() local currentCash = GetInt("savegame.cash") if currentCash > 0 then if gCashDisplay > currentCash then gCashDisplay = currentCash end if currentCash > gCashDisplay then if gCashScale == 0 then SetValue("gCashScale", 1, "easeout", 1) gCashCount = 0 end if gCashScale == 1 and gCashCount==0 then SetValue("gCashCount", 1, "linear", 1.2) UiSound("terminal/cash-counter.ogg") gCashFlash = 1 SetValue("gCashFlash", 0, "easeout", 1.0) end if gCashCount == 1 then gCashDisplay = currentCash end else if gCashScale == 1 then SetValue("gCashScale", 0, "easein", 1) end end if not gAlarm and gEndScreenScale == 0 and not GetBool("game.player.usescreen") then if gHubMode then gCashScale = 1 end if gCashScale > 0 then UiPush() UiScale(gUiScaleUpFactor) UiTranslate(UiWidth()/gUiScaleUpFactor-180, -50+60*gCashScale) UiColor(1,1,1, 0.5+gCashFlash) local width = 160 UiImageBox("common/box-solid-10.png", width, 34, 10, 10) UiColor(0,0,0) UiFont("bold.ttf", 22) local textCash = GetTranslatedStringByKey("UI_TEXT_CASH_$," .. math.floor(gCashDisplay + (currentCash-gCashDisplay)*gCashCount)) local sizeCash = UiGetTextSize(textCash) UiTranslate((width - sizeCash) / 2, 24) UiText(textCash) UiPop() end end end end function drawHealth() local health = GetFloat("game.player.health") local show = health < 1 if show and not showHealth then SetValue("healthFade", 1, "easeout", 0.5) elseif not show and showHealth then SetValue("healthFade", 0, "easein", 0.5) end showHealth = show if healthFade == 0 then return end UiPush() UiScale(gUiScaleUpFactor) UiTranslate(UiWidth()/gUiScaleUpFactor - 144, UiHeight()/gUiScaleUpFactor - 44*healthFade) UiColor(0,0,0, 0.5) UiPush() UiColor(1,1,1) UiFont("bold.ttf", 24) UiTranslate(0, 22) if health < 0.1 then if math.fmod(GetTime(), 1.0) < 0.5 then UiColor(1, 0, 0, 1.0) else UiColor(1, 0, 0, 0.1) end elseif health < 0.5 then UiColor(1, 0, 0) end UiAlign("right") UiText("loc@UI_TEXT_HEALTH") UiPop() UiTranslate(10, 4) local w = 110 local h = 20 UiPush() UiAlign("left top") UiColor(0, 0, 0, 0.5) UiImageBox("common/box-solid-10.png", w, h, 6, 6) if health > 0 then UiTranslate(2, 2) w = (w-4)*health if w < 12 then w = 12 end h = h-4 UiColor(1,health*2,health,1) UiImageBox("common/box-solid-6.png", w, h, 6, 6) end UiPop() UiPop() end function drawHurt() UiPush() local health = GetFloat("game.player.health") if health < gLastHealth then local hurt = gLastHealth - health SetValue("gHurtFade", math.min(hurt*2, 1)) SetValue("gHurtFade", 0, "linear", math.min(hurt*5)) end gLastHealth = health if gHurtFade > 0 then UiColor(1, 0, 0, gHurtFade * 0.5) UiRect(UiWidth(), UiHeight()) end UiPop() end function drawSteroid() UiPush() local steroid = GetFloat("game.player.steroid") if steroid > 0 then if HasKey("game.tool.steroid.color") then local color = GetColor("game.tool.steroid.color") UiColor(color[1], color[2], color[3], steroid * 0.5) else UiColor(0.2, 0.4, 1.0, steroid * 0.5) end local w,h = UiGetImageSize("hud/steroid.png") if w > 0 and h > 0 then UiScale(UiWidth()/w, UiHeight()/h) UiImage("hud/steroid.png") end end UiPop() end function tick() if gMissionMode and gMissions[gMissionId].alarmPath then --Start recording when alarm goes off if not gAlarm and GetBool("level.alarm") then startRecording() end --Stop recording if play state changes if gAlarm and GetString("level.state")~="" then stopRecording() end --Stop recording if alarm was disabled if gAlarm and not GetBool("level.alarm") and GetBool("game.path.recording") then stopRecording() end end gAlarm = GetBool("level.alarm") end local hintFrame = Ui.Frame { hintLayout = Ui.HorizontalLayout { vAlign = Ui.ContentLayout.Vertical.Top, hAlign = Ui.ContentLayout.Horizontal.Center, alignment = Ui.ContentLayout.Vertical.Middle, }, } function drawHint() -- dont show tutorial hints while map is shown if GetFloat("game.map.fade") > 0 then return end UiPush() -- raise all tutorial hints higher from bottom edge of the screen if heist stats are shown if GetBool("game.hud.heistStatsVisible") then UiTranslate(0, -50) end UiScale(gUiScaleUpFactor) local hintImage = GetString("hud.hintimage") if hintImage ~= "" then UiPush() local imgWidth, imgHeight = UiGetImageSize(hintImage) SetInt("hud.hintimage.size.width", imgWidth+20) SetInt("hud.hintimage.size.height", imgHeight) UiAlign("bottom right") UiTranslate(UiWidth()/gUiScaleUpFactor - 20, UiHeight()/gUiScaleUpFactor - 60) UiImageBox("hud/infobox.png", imgWidth+20, imgHeight, 12, 12) UiImage(hintImage) UiPop() SetString("hud.hintimage", "") end local hintImageText = GetString("hud.hintimage.text") if hintImageText ~= "" then UiPush() UiFont("bold.ttf", 20) local x = GetInt("hud.hintimage.text.offsetx") local y = GetInt("hud.hintimage.text.offsety") local imgWidth = GetInt("hud.hintimage.size.width") x = imgWidth + 20 --align text in the middle of image UiTranslate(UiWidth()/gUiScaleUpFactor - x, UiHeight()/gUiScaleUpFactor - y) UiColor(1,1,1) UiAlign("bottom center") UiTextAlignment("center") UiWordWrap(imgWidth) UiTranslate(imgWidth / 2, 0) UiText(hintImageText) UiPop() SetString("hud.hintimage.text", "") end -- to avoid manual calculation of offsets... do local hintInputLength = GetInt("hud.hintimage.input.length") local padding = GetInt("hud.hintimage.input.padding") if hintInputLength > 0 then SetInt("hud.hintimage.input.length", 0) local bw = GetInt("hud.hintimage.size.width") local bh = GetInt("hud.hintimage.size.height") local hintLayout = hintFrame.hintLayout hintFrame.w = bw hintFrame.h = bh hintLayout.margin = { top = GetInt("hud.hintimage.margin.top"), } hintLayout:clear() hintLayout.padding = padding for i = 1, hintInputLength, 1 do local hintInput = "hud.hintimage.input"..i local hintInputStr = GetString(hintInput) local iw, ih = UiGetImageSize(hintInputStr) hintLayout:append( Ui.Image { path=hintInputStr, w = iw, h = ih } ) SetString(hintInput, "") end UiPush() UiAlign("top left") UiTranslate(UiWidth()/gUiScaleUpFactor - 20 - bw, UiHeight()/gUiScaleUpFactor - 60 - bh) UiPush() UiColorFilter(0,0,0,0) hintLayout:draw() UiPop() hintFrame:draw() UiPop() SetString("hud.hintimage.input", "") end end local hintInput = GetString("hud.hintimage.input") if hintInput ~= "" then UiPush() UiAlign("bottom right") local x = GetInt("hud.hintimage.offsetx") local y = GetInt("hud.hintimage.offsety") UiTranslate(UiWidth()/gUiScaleUpFactor - x, UiHeight()/gUiScaleUpFactor - y) UiImage(hintInput) UiPop() SetString("hud.hintimage.input", "") end local hintInput1 = GetString("hud.hintimage.input1") if hintInput1 ~= "" then UiPush() UiAlign("bottom right") local x = GetInt("hud.hintimage.offsetx1") local y = GetInt("hud.hintimage.offsety1") UiTranslate(UiWidth()/gUiScaleUpFactor - x, UiHeight()/gUiScaleUpFactor - y) UiImage(hintInput1) UiPop() SetString("hud.hintimage.input1", "") end local hintInput2 = GetString("hud.hintimage.input2") if hintInput2 ~= "" then UiPush() UiAlign("bottom right") local x = GetInt("hud.hintimage.offsetx2") local y = GetInt("hud.hintimage.offsety2") UiTranslate(UiWidth()/gUiScaleUpFactor - x, UiHeight()/gUiScaleUpFactor - y) UiImage(hintInput2) UiPop() SetString("hud.hintimage.input2", "") end local hintInput3 = GetString("hud.hintimage.input3") if hintInput3 ~= "" then UiPush() UiAlign("bottom right") local x = GetInt("hud.hintimage.offsetx3") local y = GetInt("hud.hintimage.offsety3") UiTranslate(UiWidth()/gUiScaleUpFactor - x, UiHeight()/gUiScaleUpFactor - y) UiImage(hintInput2) UiPop() SetString("hud.hintimage.input3", "") end local hintInfo = GetString("hud.hintinfo") if hintInfo ~= gHintInfo then if gHintInfoScale == 1 then SetValue("gHintInfoScale", 0, "easein", 0.5) end if gHintInfoScale == 0 and GetTime() > 5 then gHintInfo = hintInfo if gHintInfo ~= "" then SetValue("gHintInfoScale", 1, "easeout", 0.5) end end end if gHintInfo ~= "" then UiPush() UiFont("bold.ttf", 32) local w, _ = UiGetTextSize(gHintInfo) UiTranslate(UiCenter()/gUiScaleUpFactor, -40+gHintInfoScale*80) UiAlign("center middle") UiColor(0,0,0,0.75) UiImageBox("common/box-solid-10.png", w+50, 50, 10, 10) UiColor(1,1,1) UiText(gHintInfo) UiPop() SetString("hud.hintinfo", "") end UiPop() end function draw() if GetBool("hud.disable") then SetBool("hud.disable", false) return end UiButtonHoverColor(1.0,1.0,0.5,1) if gEndScreenScale == 0 then drawMap(gMissionId) end local mapFade = GetFloat("game.map.fade") if mapFade > 0 and mapFade < 1 then hideNotifications() end if GetBool("game.map.enabled") then if titleTimer > 0.1 then titleTimer = 0.1 end end if titleFade > 0 then if titleTimer > 0 then if VecLength(VecSub(GetPlayerPos(), spawnPos)) > 0.5 and gMissionId ~= "frustrum_chase" then titleTimer = 0 end titleTimer = titleTimer - GetTimeStep() if titleTimer <= 0 then SetValue("titleFade", 0, "easein", 0.3) end end UiPush() UiTranslate(0, -(1-titleFade)*140) UiColor(0,0,0,0.7*titleFade) UiRect(UiWidth(), 140) UiFont("bold.ttf", 64) UiTranslate(UiCenter(), 70) UiAlign("center middle") UiScale(1.5) UiColor(1,1,1, titleFade) UiText(GetTranslatedStringByKey(gMissionTitle.."_UPPERCASE")) UiPop() end if GetBool("hud.hide") then SetBool("hud.hide", false) return end local n = GetString("hud.notification") if n~="" then if n == "hide" then hideNotifications() else local numIcons = GetInt("hud.notification.numicons") if numIcons > 0 then local icons = {} for i=1,numIcons do local s = "hud.notification.icon"..i local icon = GetString(s) if icon ~= "" then icons[#icons + 1] = icon SetString(s, "") end end notifyIcons(n, icons, 4) SetInt("hud.notification.numicons", 0) else notify(n, 4) end end SetString("hud.notification", "") end notifications() if not GetBool("game.map.enabled") and not gPhotoMode then interactInfo() end if GetString("level.state") ~= "win" then local pathAlpha = GetFloat("game.path.alpha") if pathAlpha > mapFade then SetFloat("game.path.alpha", mapFade) end end --SetString("level.state", "win") SetInt("game.hud.hintH", healthFade * 50) if GetBool("game.vehicle.interactive") and gState == "" and GetFloat("game.map.fade") == 0 then drawVehicle() end if GetBool("game.player.interactive") and gState == "" then crosshair() drawTool() drawPickInfo() end if not GetBool("level.end") then drawHurt() end if GetBool("game.player.interactive") then drawSteroid() end if gState == "" and GetFloat("game.map.fade") == 0 then if GetBool("game.player.interactive") or GetBool("game.vehicle.interactive") then drawHealth() end end if GetFloat("game.map.fade") == 0 then drawCash() end drawHint() if gMissionMode then local s = GetString("level.state") if gState ~= s then gState = s if s ~= "" then endfade = 0 SetValue("endfade", 1, "linear", 4) gState = s else endfade = 0 end end if endfade == 1 and not GetBool("game.paused") then SetPaused(true) end if endfade > 0.8 and not gPaused then if gEndScreenScale == 0 then SetValue("gEndScreenScale", 1, "easeout", 0.5) resetEndScreenTime() hideNotifications() initDrawScore() end else SetValue("gEndScreenScale", 0) end if endfade > 0 then if endfade >= 1.0 then flyover() else UiPush() local a = clamp((endfade - 0.5)*2, 0, 1) UiColor(0,0,0,a) UiRect(UiWidth(), UiHeight()) UiPop() end UiMute(endfade) end end if gEndScreenScale == 0.0 then pauseMenu() photoMode() else -- for end screen we do not want to draw a cursor if LastInputDevice() == UI_DEVICE_GAMEPAD then UiSetCursorState(UI_CURSOR_HIDE_AND_LOCK) end EndScreen.draw(gEndScreenScale, gState) end if gEndingSequence > 0 then UiPush() UiColor(0,0,0,gEndingSequence) UiRect(UiWidth(), UiHeight()) UiPop() if gEndingSequence == 1.0 then if gMissionId == "frustrum_chase" then startCinematic("ending1", true) else startCinematic("ending2", true) end end end if gActiveMods then UiMakeInteractive() SetBool("game.disablepause", true) drawActiveMods() if not GetBool("game.paused") then gActiveMods = false end if InputPressed("menu_cancel") or (InputPressed("lmb") and not UiIsMouseInRect(484, 890)) then gActiveMods = false SetPaused(false) end end end ------------------------------------------------------------------------------------------- -- PATH AND FLYOVER END SEQUENCE ------------------------------------------------------------------------------------------- function startRecording() Command("game.path.record") end function stopRecording() if GetBool("game.path.recording") then Command("game.path.stop") Command("game.path.save", gMissionId.."-last") end end local flyoverFirst local targetPos local eyePos local flyoverHasPath local flyoverAngle function flyoverInit(usePath) if not flyoverFirst then if usePath then flyoverHasPath = GetBool("game.path.loaded") if flyoverHasPath then Command("game.path.load", gMissionId.."-last") SetFloat("game.path.alpha", 1) end end flyoverFirst = true end flyoverUsePath = usePath if flyoverUsePath and not flyoverHasPath then flyoverUsePath = false end if flyoverUsePath then flyoverPos = 0 flyoverLength = GetFloat("game.path.length") else if gState=="fail_dead" then flyoverBase = GetPlayerPos() else local loc = FindLocation("flyover", true) if loc ~= 0 then flyoverBase = GetLocationTransform(loc).pos flyoverBase = VecAdd(flyoverBase, Vec(math.random(-5,5), math.random(0,0), math.random(-5, 5))) else flyoverBase = VecAdd(flyoverBase, Vec(math.random(-40,40), math.random(0,0), math.random(-40,40))) end end local dir = VecNormalize(Vec(math.random(-100, 100), 0.0, math.random(-100, 100))) flyoverOffsetStart = VecScale(dir, 30) flyoverOffsetEnd = VecAdd(flyoverOffsetStart, Vec(math.random(-10, 10), math.random(-10,10), math.random(-10,10))) flyoverPos = 0 flyoverLength = 8 end flyoverAngle = math.random()*6.28 flyoverAngVel = (math.random()-0.5)*0.2 flyoverPos = 0 flyoverFrame = 0 end function flyover() if not flyoverFirst or flyoverPos == flyoverLength then flyoverInit(gState=="win") end flyoverPos = math.min(flyoverLength, flyoverPos + GetTimeStep()) local alpha = 1.0 if flyoverPos < 1.0 then alpha = flyoverPos end if flyoverPos > flyoverLength-1.0 then alpha = flyoverLength-flyoverPos end if alpha < 1 then UiPush() UiColor(0,0,0,1-alpha) UiRect(UiWidth(), UiHeight()) UiPop() end local target, eye flyoverAngle = flyoverAngle + GetTimeStep()*flyoverAngVel if flyoverUsePath then target = Vec(GetFloat("game.path.current.x"), GetFloat("game.path.current.y"), GetFloat("game.path.current.z")) eye = VecAdd(target, Vec(math.sin(flyoverAngle)*20, 30, math.cos(flyoverAngle)*20)) local loc = FindLocation("flyover", true) if loc ~= 0 then eye = GetLocationTransform(loc).pos end SetFloat("game.path.pos", flyoverPos) else local t = flyoverPos / flyoverLength target = VecCopy(flyoverBase) eye = VecAdd(target, VecScale(flyoverOffsetStart, 1-t)) eye = VecAdd(eye, VecScale(flyoverOffsetEnd, t)) eye = VecAdd(eye, Vec(math.sin(flyoverAngle)*0, 30, math.cos(flyoverAngle)*0)) end local t if flyoverFrame < 2 then t = 1.0 else t = 0.02 end flyoverFrame = flyoverFrame + 1 targetPos = VecAdd(VecScale(targetPos, 1-t), VecScale(target, t)) eyePos = VecAdd(VecScale(eyePos, 1-t), VecScale(eye, t)) SetCameraTransform(Transform(eyePos, QuatLookAt(eyePos, targetPos))) end