#include "game.lua" #include "options.lua" #include "score.lua" #include "promo.lua" #include "ui_helpers.lua" #include "script/challenge.lua" #include "components/mod_browser_view.lua" #include "components/challenges_view.lua" #include "components/sandbox_view.lua" #include "components/expansions_view.lua" #include "components/mod_viewer.lua" #include "components/eula.lua" #include "components/mod_manager.lua" #include "debug.lua" slideshowImages = {} bgItems = {nil, nil} bgCurrent = 0 bgPromoIndex = {} -- Context Menu showContextMenu = false showBuiltinContextMenu = false showSubscribedContextMenu = false getContextMousePos = false contextItem = "" contextPosX = 0 contextPosY = 0 contextScale = 0 gActivations = 0 promo_full_initiated = false local gForcedFocus = nil local gPlayFocus = nil gDeploy = nil -- Yes-No popup yesNoPopup = { show = false, yes = false, text = "", item = "", yes_fn = nil } function yesNoInit(text,item,fn) yesNoPopup.show = true yesNoPopup.yes = false yesNoPopup.text = text yesNoPopup.item = item yesNoPopup.yes_fn = fn end function yesNo() local clicked = false UiModalBegin() UiPush() local w = 500 local h = 160 UiTranslate(UiCenter()-250, UiMiddle()-85) UiAlign("top left") UiWindow(w, h) UiColor(0.2, 0.2, 0.2) UiImageBox("common/box-solid-6.png", w, h, 6, 6) UiColor(1, 1, 1) UiImageBox("common/box-outline-6.png", w, h, 6, 6) if InputPressed("esc") then yesNoPopup.yes = false return true end UiColor(1,1,1,1) UiTranslate(16, 16) UiPush() UiTranslate(60, 20) UiFont("regular.ttf", 22) UiColor(1,1,1) UiText(yesNoPopup.text) UiPop() UiButtonImageBox("common/box-outline-6.png", 6, 6, 1, 1, 1) UiTranslate(77, 70) UiFont("regular.ttf", 22) UiColor(0.6, 0.2, 0.2) UiImageBox("common/box-solid-6.png", 140, 40, 6, 6) UiFont("regular.ttf", 26) UiColor(1,1,1,1) if UiTextButton("loc@UI_BUTTON_YES", 140, 40) then yesNoPopup.yes = true clicked = true end UiTranslate(170, 0) if UiTextButton("loc@UI_BUTTON_NO", 140, 40) then yesNoPopup.yes = false clicked = true end UiPop() UiModalEnd() return clicked end function bgLoad(i) local bg = {} bg.i = i+1 bg.t = 0 bg.x = 0 bg.y = 0 bg.vx = 0 bg.vy = 0 return bg end function bgDraw(bg) if bg then UiPush() local dt = GetTimeStep() bg.t = bg.t + dt local a = math.min(bg.t*0.6, 1.0) UiColor(1,1,1,a) UiScale(1.03 + bg.t*0.01) UiTranslate(bg.x, bg.y) if UiHasImage(slideshowImages[bg.i].image) then UiImage(slideshowImages[bg.i].image) end UiPop() end end bgIndex = 0 bgInterval = 6 bgTimer = bgInterval function initSlideShowLevel(level) local i=1 while UiHasImage("menu/slideshow/"..level..i..".jpg") do local item = {} item.image = "menu/slideshow/"..level..i..".jpg" item.promo = "" slideshowImages[#slideshowImages+1] = item i = i + 1 end end function initSlideShowPromo() local groups = ListKeys("promo.groups") for i=1, #groups do local groupKey = "promo.groups."..groups[i] local items = ListKeys(groupKey.. ".items") for j=1, #items do local img = GetString(groupKey..".items."..items[j]..".full_image") if img ~= "" then local item = {} item.image = img item.promo = groupKey..".items."..items[j] slideshowImages[#slideshowImages+1] = item --promoInitFull(item.promo) end end end bgPromoIndex[0] = #slideshowImages-1 bgPromoIndex[1] = 1 end function initSlideshow() slideshowImages = {} initSlideShowLevel("hub") if isLevelUnlocked("lee") then initSlideShowLevel("lee") end if isLevelUnlocked("marina") then initSlideShowLevel("marina") end if isLevelUnlocked("mansion") then initSlideShowLevel("mansion") end if isLevelUnlocked("mall") then initSlideShowLevel("mall") end if isLevelUnlocked("caveisland") then initSlideShowLevel("caveisland") end if isLevelUnlocked("frustrum") then initSlideShowLevel("frustrum") end if isLevelUnlocked("carib") then initSlideShowLevel("carib") end if isLevelUnlocked("factory") then initSlideShowLevel("factory") end if isLevelUnlocked("cullington") then initSlideShowLevel("cullington") end if HasKey("savegame.mod.builtin-artvandals.cinematic.complete") then initSlideShowLevel("tillaggaryd") end --Scramble order for i=1, #slideshowImages do local j = math.random(1, #slideshowImages) local tmp = slideshowImages[j] slideshowImages[j] = slideshowImages[i] slideshowImages[i] = tmp end --Reset the slideshow ticker to point at first image with no previous image bgPromoIndex[0] = -1 bgPromoIndex[1] = -1 bgIndex = 0 bgCurrent = 0 bgItems[0] = bgLoad(bgIndex) bgItems[1] = nil bgTimer = bgInterval end function resetAllWindows() gOptionsScale = 0 gSandboxScale = 0 gChallengesScale = 0 gExpansionsScale =0 gPlayScale = 0 gModBrowserScale = 0 gPlayDropdownShowRequest = false gPlayDropdownFullyOpened = false gPlayDropdownCouldBeFastClosed = false gCreateScale = 0 ChallengesView:init() SandboxView:init() ExpansionsView:init() UiForceFocus(gForcedFocus) ModManager.Window:hide() ModManager.Window.animator:reset(0) ModViewer:hide() ModViewer.animator:reset(0) end function init() initSlideshow() resetAllWindows() gDeploy = GetBool("game.deploy") RegisterListenerTo(UI_NAVLIST_SELECT_ITEM, "onListSelectItem") RegisterListenerTo(UI_NAVLIST_ACTIVATE_SELECTED, "onListActivateSelected") handleIntent() Options:Init() -- preload fonts with common sizes UiFont("arial.ttf", 27) UiFont("regular.ttf", 26) UiFont("regular.ttf", 52) UiFont("medium.ttf", 26) UiFont("medium.ttf", 52) UiFont("bold.ttf", 26) UiFont("bold.ttf", 52) end function UiTextAligned(str) UiPush() for line in str:gmatch("[^\n]+") do local _, h = UiGetTextSize(line) UiText(line) UiTranslate(0, h) end UiPop() end -- main menu is considered visible when none of windows is drawn above it function isMainMenuVisible() return gOptionsScale == 0 and gSandboxScale == 0 and gChallengesScale == 0 and gExpansionsScale == 0 and gModBrowserScale == 0 and gCreateScale == 0 and ModViewer.visible == false and Promo.Window.visible == false and ModManager.Window.visible == false end MainMenuButtons = {} MainMenuButtons.width = 250 MainMenuButtons.height = 48 MainMenuButtons.totalWidth = 820 MainMenuButtons.PlayDropDown = {} MainMenuButtons.PlayDropDown.contentHeight = 350 MainMenuButtons.drawGamepadHint = function() if not isMainMenuVisible() then return end UiPush() local safeCanvas = UiSafeCanvasSize() local realCanvas = UiCanvasSize() UiPush() UiTranslateToScreenBottomEdge(0, -160) UiColor(1,1,1) UiImage("menu/bottom-gradient.png") UiPop() UiTranslateToScreenBottomEdge(0, -(realCanvas.h - safeCanvas.h) / 2 - 70) UiTranslateToScreenLeftEdge(103, 0) UiColor(1, 1, 1, 1) UiDrawGamepadHintsLeftAlign({ { ico="[[menu:menu_accept;iconsize=42,42]]", txt="loc@UI_CREATIVE_SELECT" } }) UiPop() end function mainMenu() UiPush() local topMenuBackgroundHeight = 152 local logoOffsetX = 40 UiAlign("top left") UiColor(0,0,0, 0.75) UiRect(UiWidth(), topMenuBackgroundHeight) UiColor(1,1,1) UiPush() UiTranslate(logoOffsetX, 38) UiScale(0.5) UiImage("menu/logo.png") UiPop() UiFont("regular.ttf", 36) UiPush() UiAlign("middle left") UiButtonImageBox("common/box-outline-fill-6.png", 6, 6, 0.96, 0.96, 0.96) UiColor(0.96, 0.96, 0.96) local buttons = {} table.insert(buttons, { name = "loc@UI_BUTTON_PLAY", cmd = function() gPlayFocus = UiFocusedComponentId() if gPlayScale == 0 then SetValue("gPlayScale", 1.0, "easeout", 0.25) gPlayDropdownShowRequest = true gPlayDropdownCouldBeFastClosed = true elseif gPlayDropdownCouldBeFastClosed then SetValue("gPlayScale", 0.0, "easein", 0.25) gPlayDropdownCouldBeFastClosed = false end end }) table.insert(buttons, { name = "loc@UI_BUTTON_OPTIONS", cmd = function() SetValue("gOptionsScale", 1.0, "easeout", 0.25) SetValue("gPlayScale", 0.0, "easein", 0.25) reloadOptions() gForcedFocus = UiFocusedComponentId() end }) table.insert(buttons, { name = "loc@UI_BUTTON_CREDITS", cmd = function() StartLevel("about", "about.xml") SetValue("gPlayScale", 0.0, "easein", 0.25) end }) if IsRunningOnPC() or IsRunningOnMac() then table.insert(buttons, { name = "loc@UI_BUTTON_QUIT", cmd = function() Command("game.quit") SetValue("gPlayScale", 0.0, "easein", 0.25) end }) end local buttonsIndent = 36 local menuBarOffset = (UiWidth() - #buttons*MainMenuButtons.width - (#buttons-1)*buttonsIndent)/2 menuBarOffset = menuBarOffset + (#buttons-3)*36 if IsRunningOnPC() or IsRunningOnMac() then menuBarOffset = menuBarOffset + 150 else menuBarOffset = menuBarOffset + 166 end UiTranslate(menuBarOffset, topMenuBackgroundHeight/2) for index = 1,#buttons do UiPush() UiButtonHoverColor(1,1,0.5,1) local button = buttons[index] if UiTextButton(button.name, MainMenuButtons.width, MainMenuButtons.height) then UiSound("common/click.ogg") button.cmd() end UiPop() UiTranslate(MainMenuButtons.width + buttonsIndent, 0) end UiPop() UiPop() -- if we returning from window, which opened by any button, we must refocus to that button local canRestore = isMainMenuVisible() and (gPlayScale == 1.0 or gPlayScale == 0.0) and (not gPlayDropdownShowRequest) if canRestore then if gForcedFocus ~= nil and UiFocusedComponentId() ~= gForcedFocus then UiForceFocus(gForcedFocus) else gForcedFocus = nil end end if gPlayScale > 0 then local bw = 230 local bh = 40 local bo = 48 local padding = 25 local specialIndent = 22 local bgWidth = 280 local bgHeight = MainMenuButtons.PlayDropDown.contentHeight / gPlayScale + padding * 2 UiPush() if gPlayScale < 1.0 then UiNavSkipUpdate() gPlayDropdownFullyOpened = false end UiButtonHoverColor(1,1,0.5,1) local playMenuOffset = menuBarOffset - (bgWidth - MainMenuButtons.width) / 2 UiTranslate(playMenuOffset, topMenuBackgroundHeight + 10) UiScale(1, gPlayScale) UiColorFilter(1,1,1,gPlayScale) if gPlayScale < 0.5 then UiColorFilter(1, 1, 1, gPlayScale * 2) end UiColor(0,0,0,0.75) UiFont("regular.ttf", 26) UiImageBox("common/box-solid-10.png", bgWidth, bgHeight, 10, 10) UiColor(1,1,1) UiButtonImageBox("common/box-outline-6.png", 6, 6, 1, 1, 1) UiColor(0.96, 0.96, 0.96) UiAlign("top left") UiTranslate(padding, padding) local gId = UiNavGroupBegin() local navReleased = wasAnyActionReleased( { "menu_up", "menu_down", "menu_left", "menu_right" }) if gPlayDropdownFullyOpened then if (not UiIsComponentInFocus(gId) and navReleased) or InputPressed("menu_cancel") then -- hide the dropdown SetValue("gPlayScale", 0.0, "easein", 0.25) gForcedFocus = gPlayFocus end end UiBeginFrame() if UiTextButton("loc@UI_BUTTON_CAMPAIGN", bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") startHub() end UiTranslate(0, bo) if UiTextButton("loc@UI_BUTTON_SANDBOX", bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") openSandboxMenu() end UiTranslate(0, bo) if UiTextButton("loc@UI_BUTTON_CHALLENGES", bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") openChallengesMenu() end UiTranslate(0, bo) if UiTextButton(GetTranslatedStringByKey("UI_BUTTON_EXPANSIONS"), bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") openExpansionsMenu() end UiTranslate(0, bo) UiTranslate(0, specialIndent) if IsRunningOnPC() or IsRunningOnMac() or GetBool("options.debug.mods") then if GetBool("options.debug.mods") then if UiTextButton("Mod browser", bw, bh) then UiSound("common/click.ogg") SetValue("gModBrowserScale", 1, "cosine", 0.25) end UiTranslate(0, bo) end UiPush() if not GetBool("promo.available") then UiDisableInput() UiIgnoreNavigation() UiColorFilter(1,1,1,0.5) end if UiTextButton("loc@UI_BUTTON_FEATURED_MODS", bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") Promo.Window:show() end if GetBool("savegame.promoupdated") then UiPush() UiTranslate(bw, 0) UiAlign("center middle") UiImage("menu/promo-notification.png") UiPop() end UiPop() UiTranslate(0, bo) if UiTextButton("loc@UI_BUTTON_MOD_MANAGER", bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") openModsMenu() end UiTranslate(0, bo) if UiTextButton("loc@UI_BUTTON_MOD_EDITOR", bw, bh) then gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") Command("mods.openeditor") end UiTranslate(0, bo) end if IsRunningOnPlaystation() or IsRunningOnXbox() or IsRunningOnIOS() or GetBool("mods.pros_enabled") then if UiTextButton("loc@UI_MOD_BROWSER", bw, bh) then Command("mods.refresh") gForcedFocus = UiFocusedComponentId() UiForceFocus("") UiSound("common/click.ogg") ModViewer:show() end if IsRunningOnPlaystation() or IsRunningOnXbox() then local modsShown = ListKeys("mods.available") for i = 1, #modsShown do if not GetBool("mods.shown." .. modsShown[i]) then UiPush() UiTranslate(bw - 5, 7.5) UiAlign("center middle") UiImage("menu/mod-manager-notification.png") UiPop() break end end end end _, MainMenuButtons.PlayDropDown.contentHeight = UiEndFrame() UiNavGroupEnd() if gPlayScale == 1.0 and gPlayDropdownShowRequest == true then gPlayDropdownShowRequest = false gPlayDropdownFullyOpened = true UiForceFocus(gId) end UiPop() end if gSandboxScale > 0 then UiPush() UiBlur(gSandboxScale) UiColor(0.7,0.7,0.7, 0.25*gSandboxScale) UiRect(UiWidth(), UiHeight()) if not drawSandbox(gSandboxScale) then SetValue("gSandboxScale", 0, "cosine", 0.25) end UiPop() end if gChallengesScale > 0 then UiPush() UiBlur(gChallengesScale) UiColor(0.7,0.7,0.7, 0.25*gChallengesScale) UiRect(UiWidth(), UiHeight()) if not drawChallenges(gChallengesScale) then SetValue("gChallengesScale", 0, "cosine", 0.25) end UiPop() end if gExpansionsScale > 0 then UiPush() UiBlur(gExpansionsScale) UiColor(0.7,0.7,0.7, 0.25 * gExpansionsScale) UiRect(UiWidth(), UiHeight()) UiModalBegin() if not drawExpansions(gExpansionsScale) then SetValue("gExpansionsScale", 0, "cosine", 0.25) end UiModalEnd() UiPop() end if gOptionsScale > 0 then UiPush() UiBlur(gOptionsScale) UiColor(0.7,0.7,0.7, 0.25 * gOptionsScale) UiRect(UiWidth(), UiHeight()) UiModalBegin() SetBool("game.menu.active", true) if not drawOptions(gOptionsScale) and gOptionsScale == 1.0 then SetValue("gOptionsScale", 0, "cosine", 0.25) end UiModalEnd() UiPop() end if gModBrowserScale > 0 then UiPush() UiBlur(gModBrowserScale) UiColor(0.7, 0.7, 0.7, 0.25 * gModBrowserScale) UiRect(UiWidth(), UiHeight()) UiModalBegin() drawModBrowser() UiModalEnd() UiPop() end end function tick() if GetTime() > 0.1 then if gActivations >= 2 then PlayMusic("menu-long.ogg") else PlayMusic("menu.ogg") end end end function drawBackground() if promo_full_initiated == false and GetBool("promo.available") and GetInt("savegame.startcount") >= 5 then promo_full_initiated = true initSlideShowPromo() end UiPush() if bgTimer >= 0 then bgTimer = bgTimer - GetTimeStep() if bgTimer < 0 then bgIndex = math.fmod(bgIndex + 1, #slideshowImages) if bgPromoIndex[0] >= 0 then bgIndex = bgPromoIndex[0] bgPromoIndex[0] = bgPromoIndex[1] bgPromoIndex[1] = -1 end bgTimer = bgInterval bgCurrent = 1-bgCurrent local oldItem = bgItems[bgCurrent]; bgItems[bgCurrent] = bgLoad(bgIndex) if oldItem then UiUnloadImage(slideshowImages[oldItem.i].image) end end end UiTranslate(UiCenter(), UiMiddle()) UiAlign("center middle") bgDraw(bgItems[1-bgCurrent]) bgDraw(bgItems[bgCurrent]) UiPop() end function draw() local wasEulaShown = GetBool("savegame.waseulashown") if not wasEulaShown and EULA.Window.isClosed and not IsRunningOnPC() and not IsRunningOnApple() then EULA.Window:show() end if ModManager.Window.restoreOnFirstDraw and ModManager.Window.canRestore() then ModManager.Window:restore() end ModManager.Window.restoreOnFirstDraw = false UiButtonHoverColor(0.8,0.8,0.8,1) -- for main menu we do now want to draw a cursor if LastInputDevice() == UI_DEVICE_GAMEPAD then UiSetCursorState(UI_CURSOR_HIDE_AND_LOCK) end UiPush() --Create a safe 1920x1080 window that will always be visible on screen local x0,y0,x1,y1 = UiSafeMargins() UiTranslate(x0,y0) UiWindow(x1-x0,y1-y0, true) drawBackground() mainMenu() if LastInputDevice() == UI_DEVICE_GAMEPAD then MainMenuButtons.drawGamepadHint() end UiPop() if not gDeploy and mainMenuDebug then mainMenuDebug() end UiPush() UiIgnoreNavigation() local version = GetString("game.version") local patch = GetString("game.version.patch") if patch ~= "" then version = version .. " (" .. patch .. ")" end UiTranslate(UiWidth()-10, UiHeight()-10) UiFont("regular.ttf", 18) UiAlign("right") UiColor(1,1,1,0.5) if UiTextButton(version) then Command("game.openurl", "http://teardowngame.com/changelog/?version="..GetString("game.version")) end UiPop() --TODO: move to a different place if gCreateScale > 0 and GetBool("game.saveerror") then UiDrawLater({ draw = function(self) UiPush() UiColorFilter(1, 1, 1, gCreateScale) UiFont("bold.ttf", 20) UiTextOutline(0, 0, 0, 1, 0.1) UiColor(1,1,.5) UiAlign("center") UiTranslate(UiCenter(), UiHeight() - 100) UiWordWrap(600) UiTextAlignment("center") UiText("loc@UI_TEXT_TEARDOWN_WAS") UiPop() end }) end drawLocalUserTag() end function handleCommand(cmd) if cmd == "opendisplayoptions" then gOptionsScale = 1 reloadOptions() end if cmd == "activate" then initSlideshow() gActivations = gActivations + 1 SetPresence("main_menu") end if cmd == "updatemods" then ModManager.Window:refresh() end if cmd == "intent" then handleIntent() end if cmd == "start" then SetInt("savegame.startcount", GetInt("savegame.startcount")+1) resetActivities() end end function handleIntent() local intent = GetString("options.intent") local state = GetString("game.state") if intent ~= "" then if intent == "campaign" then resumeCampaign() elseif intent == "challenge_mansion_race" then tryStartMission("mansion_race") elseif intent == "sandbox" then if state ~= "MENU" then Menu() return else resetAllWindows() openSandboxMenu() end elseif intent == "challenges" or string.sub(intent, 1, 10) == "challenge_" then if state ~= "MENU" then Menu() return else resetAllWindows() openChallengesMenu() end elseif string.sub(intent, 1, 8) == "usedmods" or string.sub(intent, 1, 15) == "usedcustomtools" then if state ~= "MENU" then Menu() return else resetAllWindows() openModsMenu() end elseif string.sub(intent, 1, 3) == "ch_" then tryStartChallenge(intent) elseif string.sub(intent, -8, -1) == "_sandbox" then tryStartSandbox(intent) else tryStartMission(intent) end SetString("options.intent", "") end end function resumeCampaign() -- find next mission: opened but without required score -- otherwise start hub for id,mission in pairs(gMissions) do if GetInt("savegame.mission."..id) > 0 and GetInt("savegame.mission."..id..".score") < mission.required then ResumeLevel(id, mission.file, mission.layers, "quicksavecampaign") return end end startHub() end function tryStartMission(id) local suf = string.sub(id, -4, -1) if suf == "_opt" then id = string.sub(id, 1, -4) end if gMissions[id] and GetInt("savegame.mission."..id) > 0 then StartLevel(id, gMissions[id].file, gMissions[id].layers) end end function tryStartChallenge(id) local stars = string.sub(id, -2, -1) if stars == "_1" or stars == "_2" or stars == "_3" or stars == "_4" or stars == "_5" then id = string.sub(id, 1, -2) end if gChallenges[id] and isChallengeUnlocked(id) then StartLevel(id, gChallenges[id].file, gChallenges[id].layers) end end function tryStartSandbox(id) for i=1, #gSandbox do if id == gSandbox[i].id then StartLevel(id, gSandbox[i].file, gSandbox[i].layers) end end end function openChallengesMenu() ChallengesView:reset() SetValue("gChallengesScale", 1, "cosine", 0.25) end function openSandboxMenu() SandboxView:reset() SetValue("gSandboxScale", 1, "cosine", 0.25) end function openModsMenu() SetValue("gCreateScale", 1, "cosine", 0.25) ModManager.Window:show() end function openExpansionsMenu() ExpansionsView:init() SetValue("gExpansionsScale", 1, "cosine", 0.25) end