#include "game.lua" #include "score.lua" #include "ui/debug.lua" #include "script/common.lua" #include "ui/ui_helpers.lua" #include "ui/ui_extensions.lua" #include "starthub.lua" gUiScaleUpFactor = 0 gSubjectPos = 0 gCurrentMessage = 0 gSelectedMessage = 0 function clamp(value, mi, ma) if value < mi then value = mi end if value > ma then value = ma end return value end function UiListEx(w, h, items, state, scrollUp, scrollDown) local contentHeight = state.contentSpan if isGamepadInUse() then if InputDown(scrollUp) then state:addOffset(InputValue(scrollUp) * 8, h, contentHeight, true) end if InputDown(scrollDown) then state:addOffset(-InputValue(scrollDown) * 8, h, contentHeight, true) end end UiIgnoreNavigation() UiList(w, h, items, state) end Terminal = { currentPanel = {}, SetTab = function (self, tab) UiSound("terminal/tab.ogg") Terminal.currentPanel = tab SetValue("gTabScroll", tab.TabScroll, "cosine", 0.4) end, Button = function(self, text, callback) local w,_ = GetTextSize((text)) local rx, ry = UiGetRelativePos() if UiTextButton(text) then callback() end UiTranslate(w + self.Tabs.marginRight,0) end, TabButton = function (self, tab) if self.currentPanel == tab then Ui.Color(Ui.Style.Terminal.Tabs.TextSelected) else Ui.Color(Ui.Style.Terminal.Tabs.Text) end self:Button(tab.Name, function() self:SetTab(tab) end) end, ShowMessage = function(self, id) Terminal:SetTab(self.messages) Terminal.messages.rightPanel:Select(id) end } function Terminal:draw() UiPush() UiIgnoreNavigation() local x = -1920 * gTabScroll UiTranslate(x, 0) if gTabScroll < 1 then self.messages:Draw() end UiTranslate(1920, 0) if gTabScroll > 0 and gTabScroll < 2 then self.missions:Draw() end UiTranslate(1920, 0) UiPop() if InputPressed("menu_cancel") and (Terminal.currentPanel ~= self.missions or gLevelScale == 0) then SetPlayerScreen(0) end self.Top:Draw() if true then if not GetBool("game.player.usescreen") then if hasUnreadMessages() then UiPush() if math.mod(GetTime(), 1.0) > 0.4 then UiTranslate(UiCenter(), UiMiddle()-100) UiAlign("center middle") UiWindow(700, 150) UiTranslate(UiCenter(), UiMiddle()) UiColor(.4,.4,.4) UiImageBox("ui/terminal/levelbox.png", UiWidth(), UiHeight(), 5, 17) setFont("h1") UiScale(2.0) UiColor(1,1,1) UiText("loc@UI_TEXT_NEW_MESSAGE") end UiPop() end else UiSetCursorState(UI_CURSOR_HIDE) -- we draw mouse by ourselves, but not lock it end end local index = indexOf(self.Top.Tabs.content, self.currentPanel) if InputPressed("menu_prev") then if index ~= nil and index > 1 then self.SetTab(self, self.Top.Tabs.content[index - 1]) end end if InputPressed("menu_next") then if index ~= nil and index < #self.Top.Tabs.content then self.SetTab(self, self.Top.Tabs.content[index + 1]) end end end function indexOf(array, value) for i, v in ipairs(array) do if v == value then return i end end return nil end function isGamepadInUse() return GetString("game.input.curdevice") == "gamepad" end function forGamepad(f) if isGamepadInUse() then UiPush() UiResetColor() f() UiPop() return true end return false end Terminal.Top = {} Terminal.Top.Rank = { Draw = function() end } Terminal.Top.Tabs = { content = {}, wholeSize = 0, marginRight = 50, Draw = function (self) UiFont("bold.ttf", 42) self.wholeSize = self.marginRight * (#self.content - 1) for _, button in pairs(self.content) do self.wholeSize = self.wholeSize + GetTextSize(button.Name) end for _, tab in pairs(self.content) do Terminal:TabButton(tab) end end } Terminal.Top.Cash = { Draw = function() end } function Terminal.Top:Draw() UiPush() local topHeight = 58 UiScale(gUiScaleUpFactor) UiColor(0.1,0.1,0.1) UiRect(UiWidth(), topHeight) gHoverTop = UiIsMouseInRect(UiWidth(), topHeight) setFont("h1") UiTranslate(UiCenter()/gUiScaleUpFactor - self.Tabs.wholeSize / 2, 25) UiAlign("middle") if hasUnreadMessages() and math.mod(GetTime(), 1.0) > 0.5 then UiPush() UiColor(.5, .8, 1) UiTranslate(-20, 5) UiImage("ui/terminal/new-message-dot.png") UiPop() end UiTranslate(0, 5) forGamepad(function () UiTranslate(-45, 0) UiAlign("right middle") UiText("[[menu:menu_prev;iconsize=42,42]]") end) self.Tabs:Draw() forGamepad(function () UiAlign("left middle") UiText("[[menu:menu_next;iconsize=42,42]]") end) UiPop() UiPush() UiScale(gUiScaleUpFactor) setFont("name") local score = getTotalScore() local strScore = tostring(score) local strScoreWidth = GetTextSize(strScore) local cash = GetInt("savegame.mod.cash") gCash = gCash + (cash-gCash)*GetTimeStep()*5 if math.abs(gCash - cash) < 1 then gCash = cash end local rw,_ = GetTextSize(getRank(score)) local scorePos = 0 local textScore = GetTranslatedStringByKey("UI_TEXT_SCORE") local rankFrameWidth = 12 + GetTextSize(textScore) + 8 + strScoreWidth + 40 + rw + 40 local rankFrameHeight = 38 UiPush() local Align = function(a) UiAlign((a or "").." middle") end UiTranslate(40, 10) UiColor(.2,.2,.2) UiImageBox("ui/common/box-solid-10.png", rankFrameWidth, rankFrameHeight, 10, 10) if gHoverScore then UiColor(1,1,1) else UiColor(.8,.8,.8) end UiTranslate(12, rankFrameHeight/2) Align("left") local w = UiText(textScore) UiTranslate(w + 8, 0) scorePos = UiGetRelativePos() + strScoreWidth scorePos = scorePos - 40 UiText(strScore) UiTranslate(strScoreWidth+40, 0) UiText(getRank(score)) UiPop() local overScore = UiIsMouseInRect(195+rw, topHeight) if overScore and not gHoverScore then SetValue("gRankInfoScale", 1, "bounce", 0.4) elseif not overScore and gHoverScore then SetValue("gRankInfoScale", 0, "easein", 0.2) end gHoverScore = overScore if gRankInfoScale > 0 then UiPush() UiTranslate(40, 55) UiScale(1, gRankInfoScale) UiColor(.2,.2,.2) local imageBoxWidth = scorePos + 40 + maxRankTextWidth() + 20 if imageBoxWidth < rankFrameWidth then imageBoxWidth = rankFrameWidth end UiImageBox("ui/common/box-solid-10.png", imageBoxWidth, gRankInfoHeight, 10, 10) UiPush() UiColor(.8, .8, .8) UiTranslate(0, 30) local didNext = false local didCurrent = false for i=#gRanks, 1, -1 do local valid = true local s = gRanks[i].score local n = gRanks[i].name if score < s then if score < gRanks[i-1].score then valid = false else didNext = true n = GetTranslatedStringByKey("N_NEXT_RANK") UiColor(0.4, 0.4, 0.4) end else if didCurrent then UiColor(.7,.7,.7) else UiColor(1,1,1) didCurrent = true end end if valid then UiPush() UiTranslate(scorePos, 0) UiAlign("right") UiText(s) UiTranslate(40, 0) UiAlign("left") UiText(n) UiPop() UiTranslate(0, 24) end end UiTranslate(10, 10) setFont("p") UiColor(.6, .6, .6) UiWordWrap(imageBoxWidth - 20) UiText(GetTranslatedStringByKey("UI_TEXT_COMPLETE_MISSIONS"), true) _,gRankInfoHeight = UiGetRelativePos() UiPop() UiPop() end UiPop() end -- TODO: It is necessary to set the height of the scroll bar --in a more appropriate way, this is a dynamic value that -- depends on the scaling of the interface. function Terminal:CreateScrollBar(height) --assert(height ~= nil) height = height or 650 return { w = 8.0, h = 127.0, draw = function(self) UiPush() UiTranslate(2.0, 0) UiColor(1, 1, 1, 1) UiImageBox("ui/common/rect_c#ffffff_o1.00_cr3.png", self.w, self.h, 3, 3) UiPop() end, }, { w = 12.0, h = height, draw = function(self) UiPush() UiColor(1, 1, 1, 1) UiImageBox("ui/common/rect_c#525252_o1.00_cr4.png", self.w, self.h, 4, 4) UiPop() end, } end Terminal.Tabs = Terminal.Top.Tabs Terminal.messages = { Name = "loc@UI_BUTTON_MESSAGES", TabScroll = 0, leftPanel = {}, rightPanel = {}, ----------------------------------- scaleYMessageWindow = 1, ----------------------------------- } table.insert(Terminal.Tabs.content, 1, Terminal.messages) Terminal.missions = { Name = "loc@UI_BUTTON_MISSIONS", TabScroll = 1, LeftPanelWidth = 630, RightPanelWidth = 800, Height = 800, } table.insert(Terminal.Tabs.content, 2, Terminal.missions) for _, tab in ipairs(Terminal.Tabs.content) do tab.SetCurrent = function(self) Terminal:SetTab(self) end end Terminal.currentPanel = Terminal.messages function Terminal.messages:showByMission(mission) for id,_ in pairs(gMessages) do if gMessages[id].mission == mission then self.Select(id) self:SetCurrent() return end end end function controllerHint() forGamepad(function () UiResetColor() UiTranslate(UiWidth() / 2, UiHeight() - 60) local hints = {} if Terminal.currentPanel == Terminal.messages and Terminal.messages.rightPanel.ScrollBar and Terminal.messages.rightPanel.ScrollBar.isShown then table.insert(hints, { ico = "[[menu:gamepad_right_stick;iconsize=42,42]]", txt = "loc@UI_TEXT_SCROLL"}) end table.insert(hints, { ico = "[[menu:menu_cancel;iconsize=42,42]]", txt = "loc@UI_BUTTON_EXIT" }) UiDrawHintsCentered(hints) end) end function Terminal.messages:Draw() controllerHint() UiPush() self.largerUIscale = 1.0 self.scaleYMessageWindow = 1.0 local windowTop = 140 if isGamepadInUse() then windowTop = 98 end local w = self.leftPanel.WindowWidth + self.rightPanel.WindowWidth*gMessageScale + 20 UiTranslate((UiCenter()/self.largerUIscale-w/2), windowTop) gSelectedMessage = 0 self.leftPanel:Draw() self.rightPanel:Draw() if gSelectedMessage == 0 then gSubjectPos = 0 end UiPop() end Terminal.messages.leftPanel = { WindowWidth = 548, WindowHeight = 860, ScrollBar = UiCreateScrollBar(Terminal:CreateScrollBar()), Draw = function(self) local scaleYMessageWindow = Terminal.messages.scaleYMessageWindow if self.ScrollBar.isShown then self.WindowWidth = 560 end UiPush() UiColor(.2, .2, .2) UiImageBox( "ui/common/box-solid-10.png", self.WindowWidth, self.WindowHeight / scaleYMessageWindow, 10, 10 ) setFont("h1.5") local messageFrameWidth = 508 local messageFrameHeight = 72 UiTranslate(20,50) UiPush() UiFont("medium.ttf", 36) UiText("loc@UI_TEXT_INBOX") UiPop() UiTranslate(0, 24) local itemWidth = 508 local itemHeight = 80 local messages = ToListItems( ReverseTable(ListKeys("savegame.mod.message")), function() return itemWidth, itemHeight end, function(message, i) local id = message.data local msg = gMessages[id] local client = gClients[msg.from] if not client then print(GetTranslatedStringByKey("UNKNOWN_CLIENT") .. msg.from) end local fromName = client.name local image = client.image local subject = GetTranslatedStringByKey(msg.subject) local isRead = isMessageRead(id) local mission = msg.mission local action = msg.action local hub = msg.hub UiPush() local selected = Terminal.messages.rightPanel.Selected == id local hovered = false if (self.mouseOver and UiIsMouseInRect(messageFrameWidth, messageFrameHeight)) then hovered = true if (InputPressed("lmb") or InputPressed("menu_accept")) then UiSound("terminal/message-select.ogg") Terminal.messages.rightPanel:Select(id) gMessageSelected = id end end if selected or hovered or not isRead then Ui.Color(Ui.Style.Terminal.Messages.Selected) else Ui.Color(Ui.Style.Terminal.Messages.Background) end UiImageBox("ui/common/box-solid-6.png", messageFrameWidth-2, messageFrameHeight, 6, 6) UiColor(1, 1, 1) local imageWidth,imageHeight = UiGetImageSize(image) local imageScale = 0.875 UiPush() UiTranslate(9, 9) if isRead then UiPush() UiScale(imageScale, imageScale) UiBeginFrame() UiImage(image) imageWidth,imageHeight = UiEndFrame() UiPop() else imageWidth = imageWidth * imageScale imageHeight = imageHeight * imageScale UiPush() UiColor(.5, .8, 1) UiPush() local dot = "ui/terminal/new-message-dot.png" local dw, dh = UiGetImageSize(dot) UiTranslate((imageWidth - dw) / 2, (imageHeight - dh) / 2) UiImage(dot) UiPop() --DebugQuad(imageWidth, imageHeight) UiPop() end UiPop() UiPush() UiTranslate(imageWidth / imageScale+10, 10) local color = selected and "sel" or "" setFont("name", color) UiText(fromName) local width = 370 local height = 32 local subjectTextWidth, _ = GetTextSize(subject) local subjectDeltaWidth = subjectTextWidth - width if subjectDeltaWidth > 0 then UiWindow(width, itemHeight, true, true) end if UiIsMouseInRect(width, itemHeight) then if gCurrentMessage ~= id then gSubjectPos = 0 gCurrentMessage = id end if subjectDeltaWidth > 0 then UiTranslate(-gSubjectPos, height) if gSubjectPos < subjectDeltaWidth then gSubjectPos = gSubjectPos + 1 end else UiTranslate(0, height) end gSelectedMessage = id else UiTranslate(0, height) end setFont("p") UiText(subject) UiPop() if mission then UiPush() UiAlign("middle") UiTranslate(messageFrameWidth - 50, messageFrameHeight / 2) local completed = isMissionCompleted(mission) if completed then UiPush() Ui.Color(0x66D166) UiImage("ui/terminal/checkmark.png") UiPop() else UiPush() if isSideMission(mission) then Ui.Color(0x9AB4CDCD) UiScale(0.9) else Ui.Color(0xE6E6E6FF) end UiImage("ui/terminal/attachment.png") UiPop() end UiPop() end if hub then UiPush() UiAlign("middle") UiTranslate(messageFrameWidth - 50, messageFrameHeight / 2) UiColor(0.8, 0.7, 0.6, 1) UiImage("ui/terminal/attachment.png") UiPop() end if action then UiPush() UiAlign("middle") UiTranslate(messageFrameWidth - 50, messageFrameHeight / 2) UiColor(0.8, 0.7, 0.6, 1) UiImage("ui/terminal/attachment.png") UiPop() end UiPop() end ) self.mouseOver = UiIsMouseInRect(508, 765/scaleYMessageWindow) local contentHeight = 765/scaleYMessageWindow self.ScrollBar.background.h = contentHeight UiIgnoreNavigation() UiListEx(messageFrameWidth+3, contentHeight, messages, self.ScrollBar, "menu_up", "menu_down") UiPop() end } RightPanelInputOverride = { Forced = true } Terminal.messages.rightPanel = { WindowWidth = 970, WindowHeight = 860, Selected = "", MessagePart = 1, MessageOutput = 0, MessageOutputSpeed = 1, MessagePause = 0, DebugMessageContentHeight = false, ScrollBar = nil, Draw = function(self) local scaleYMessageWindow = Terminal.messages.scaleYMessageWindow if self.ScrollBar and self.ScrollBar.isShown then self.WindowWidth = 982 end local windowWidth = self.WindowWidth local windowHeight = self.WindowHeight local messageOffsetX = Terminal.messages.leftPanel.WindowWidth do UiPush() UiTranslate(messageOffsetX + 20, 0) UiScale(gMessageScale, 1) UiColor(.23, .23, .23) UiScale(gMessageRightPanelScaleOnSelect) UiImageBox("ui/common/box-solid-10.png", windowWidth, windowHeight, 10, 10) UiTranslate(20, 50) local mission = "" local tool = "" local hub = "" if self.Selected ~= "" then local msg = gMessages[self.Selected] local name = gClients[msg.from].name local image = gClients[msg.from].image local subject = msg.subject local body = msg.body mission = msg.mission tool = msg.tool hub = msg.hub action = msg.action UiPush() UiTranslate(46, 16) UiAlign("center middle") UiColor(1,1,1) UiPush() UiScale(1.4, 1.4) UiImage(image) UiPop() UiTranslate(60, -10) UiAlign("left") setFont("h1") UiText(name) UiTranslate(0, 45) UiColorFilter(1,1,1,0.8) UiFont("regular.ttf", 36) UiText(subject) UiPop() UiTranslate(0, 80) local partCount = #body local partsHeight = 0 local symbolCount = 930 UiFont("regular.ttf", 27) UiWordWrap(symbolCount) UiAlign("top left") UiScale(1,1) for i=1,partCount do local type = UiTextSymbolsSub(body[i][1], 1, 3) if type == "div" then partsHeight = partsHeight + 4 elseif type == "img" then local _,h = UiGetImageSize(body[i][2]) partsHeight = partsHeight + h elseif type == "txt" then local w,h = GetTextSize(body[i][2]) partsHeight = partsHeight + h + 30 if i == partCount then partsHeight = partsHeight - 20 end end end if self.MessagePause > 0 then self.MessagePause = self.MessagePause - GetTimeStep() else if self.MessageOutput < 1.0 then self.MessageOutput = self.MessageOutput + GetTimeStep() * self.MessageOutputSpeed if self.MessageOutput >= 1.0 then self.MessagePause = 0.15 end else if self.MessagePart < partCount then self.MessagePart = self.MessagePart + 1 self.MessageOutput = 0 local type = UiTextSymbolsSub(body[self.MessagePart][1], 1, 3) if type == "div" then self.MessageOutput = 1 elseif type == "img" then self.MessageOutput = 0 self.MessageOutputSpeed = 2 UiSound("terminal/image.ogg") elseif type == "txt" then local l = string.len(GetTranslatedStringByKey(body[self.MessagePart][2])) if l < 100 then self.MessageOutputSpeed = 4 UiSound("terminal/printout250.ogg") elseif l < 400 then self.MessageOutputSpeed = 2 UiSound("terminal/printout500.ogg") else self.MessageOutputSpeed = 1 UiSound("terminal/printout1000.ogg") end end else if gMessageAttachmentScale == 0 and (mission or tool or hub or action) then UiSound("terminal/message-mission.ogg") SetValue("gMessageAttachmentScale", 1, "bounce", 0.6) end end end end self.MessageOutputSpeed = self.DebugMessageContentHeight and 100 or self.MessageOutputSpeed local contentWidth = windowWidth - 40 if self.ScrollBar and self.ScrollBar.isShown then contentWidth = contentWidth - 8 end local contentHeight = 632/scaleYMessageWindow self.ScrollBar.background.h = contentHeight UiListEx( contentWidth, contentHeight, {{ w = 900, h = partsHeight, draw = function(_) if self.DebugMessageContentHeight then UiPush() UiColor(1,0,0,0.5) UiRect(self.w, self.h) UiTranslate(0,self.h) UiColor(0,1,0,0.5) UiRect(self.w, 40) UiPop() end for i=1,self.MessagePart do local type = UiTextSymbolsSub(body[i][1], 1, 3) local extra = UiTextSymbolsSub(body[i][1], 5, #body[i][1]) local arg = GetTranslatedStringByKey(body[i][2]) if string.find(extra, "gray") then UiColor(.7, .7, .7) elseif string.find(extra, "green") then UiColor(.3, 1, .3) elseif string.find(extra, "red") then UiColor(1, .3, .3) elseif string.find(extra, "blue") then UiColor(.3, .3, 1) elseif string.find(extra, "yellow") then UiColor(1, 1, .3) elseif string.find(extra, "cyan") then UiColor(.3, 1, 1) elseif string.find(extra, "magenta") then UiColor(1, .3, 1) else UiColor(1, 1, 1) end if type == "txt" then UiPush() UiColorFilter(1,1,1,0.8) if string.find(extra, "h1") then setFont("h1", "nocolor") elseif string.find(extra, "h2") then setFont("h2", "nocolor") else --UI.Color(Color.White) UiFont("regular.ttf", 27) end UiFont("regular.ttf", 27) local maxChars = 10000 if i == self.MessagePart then maxChars = self.MessageOutput * string.len(arg) end UiScale(1,1) if self.DebugMessageContentHeight then UiPush() local w1,h1 = GetTextSize(arg) UiColor(1,1,1,0.5) UiRect(w1,h1) UiPop() end local w, h = UiText(arg, false, maxChars) UiPop() UiTranslate(0, h + 30) elseif type == "img" then UiPush() UiAlign("top center") UiTranslate(contentWidth/2 - 8, 8) UiPush() local imgScale = 0.895 UiScale(imgScale) if gShowLargeUI then UiScale(0.8*imgScale) end local w,h = UiImage(arg) UiColor(.25, .25, .25) UiImageBox("ui/common/circle_10x10.png", w, h, 10, 10) if gShowLargeUI then h = h*0.8*imgScale end UiPop() if i == self.MessagePart then UiPush() local bh = h*self.MessageOutput UiTranslate(0, bh) UiColor(0.25,0.25,0.25) UiRect(w, h-bh+1) UiPop() end UiPop() UiTranslate(0, h) elseif type == "div" then UiPush() UiTranslate(0, -8) Ui.Color(0xD9D9D9) UiRect(windowWidth-40, 2) UiTranslate(0, 4) UiPop() end end end }}, self.ScrollBar, "up", "down") end UiPop() if (mission or tool or hub or action) and gMessageAttachmentScale > 0 then UiPush() setFont("button") UiTranslate(messageOffsetX - 15 + windowWidth / 2.0, windowHeight - 30 / scaleYMessageWindow) UiScale(gMessageAttachmentScale * (1 + math.sin(GetTime()*10)*0.03)) UiButtonImageBox("ui/common/box-outline-6.png", 6, 6, 0.96, 0.96, 0.96) UiButtonHoverColor(hexrgb(Ui.Style.Color.Yellow)) local buttonText = "" if mission and gMissions[mission] then local txt = GetTranslatedStringByKey("TXT_MISSION") if isSideMission(mission) then txt = GetTranslatedStringByKey("TXT_SIDE_MISSION") --UiColor(0.9, 0.95, 1.0) end buttonText = txt .. " - " .. GetTranslatedStringByKey(gMissions[mission].title) UiTranslate(-(UiGetTextSize(buttonText) / 2.0), 0) if UiTextButton(buttonText) then showMission(mission) end elseif hub then if hub == "carib" then buttonText = GetTranslatedStringByKey("UI_BUTTON_TRAVEL_TO_ISLANDS") UiTranslate(-(UiGetTextSize(buttonText) / 2.0), 0) if UiTextButton(buttonText) then gDisableEvaluation = true StartLevel("hub30", "hub_carib.xml", "v30") end else buttonText = GetTranslatedStringByKey("UI_BUTTON_TRAVEL_BACK") UiTranslate(-(UiGetTextSize(buttonText) / 2.0), 0) if UiTextButton(buttonText) then gDisableEvaluation = true StartLevel("hub40", "hub.xml", "part2 v40") end end elseif action then if action == "travelbackintime" and not GetBool("savegame.mod.backintime") then UiColor(0.9, 1, 0.95) buttonText = GetTranslatedStringByKey("UI_TERMINAL_TIMETRAVEL") UiTranslate(-(UiGetTextSize(buttonText) / 2.0), 0) if UiTextButton(buttonText) then gDisableEvaluation = true StartLevel("timetravel", "MOD/time_travel.xml", "landscape ravine_timetravel") --StartLevel("timetravel", "MOD/timetravel.xml") end end end UiPop() end end end, Select = function(self, id) if gMessages[id].music and GetInt("savegame.mod.message."..id) == 1 then PlayMusic(gMessages[id].music) --gNoLoadingScreen = true -- Would be nice, but there's an audio glitch somewhere end SetInt("savegame.mod.message."..id, 2) SetValue("gMessageScale", 1, "cosine", 0.3) if gMessages[id].mission then unlockMission(gMessages[id].mission) end if self.Selected == id then return end self.ScrollBar = UiCreateScrollBar(Terminal:CreateScrollBar(590)) self.Selected = id self.MessagePart = 0 self.MessageOutput = 1 if gMessageScale == 1 then gMessageRightPanelScaleOnSelect = 0.95 SetValue("gMessageRightPanelScaleOnSelect", 1, "bounce", 0.5) else gMessageRightPanelScaleOnSelect = 1 end SetValue("gMessageAttachmentScale", 0) gTimeSinceMessageRead = 0 end } function Terminal.missions:Draw() local level = gLevelScale UiPush() if level > 0 then UiDisableInput() UiIgnoreNavigation() end setFont("h2") --PART 2 UiImage("terminal/map.jpg") --end UiPush() if inCarib() then UiTranslate(1680, 725) else UiTranslate(839, 570) end UiAlign("center middle") --UiImage("terminal/map_x.png") UiPop() local levels = getUnlockedLevels() for i=1,#levels do local id = levels[i] local title = GetTranslatedStringByKey(gLevels[id].map_title) local x = gLevels[id].map_x local y = gLevels[id].map_y local unfinishedCount = getUnfinishedMissionCount(id) UiPush() local levelScore = getLevelScore(id) local textScores = GetTranslatedStringByKey("UI_TEXT_SCORE") .. " " ..levelScore UiTranslate(x, y) UiScale(gUiScaleUpFactor) local w,_ = UiGetTextSize(title) local scoresWidth = UiGetTextSize(textScores) if scoresWidth > w then w = scoresWidth end w = w + 20 if w < 100 then w = 100 end local h = 48 if levelScore > 0 then h = h + 34 end --if unfinishedCount > 0 then -- h = h + 20 --end UiAlign("center middle") local over = UiIsMouseInRect(40, 40) UiPush() UiTranslate(0, 20+h/2) over = over or UiIsMouseInRect(w, h) UiPop() if over and gLevelSelected == "" then if (InputPressed("lmb") or InputPressed("menu_accept")) then UiSound("terminal/level.ogg", 1, 1.1) gLevelSelected = id gLevelViewed = id gMissionSelected = "" gLevelHighlighted = "" SetValue("gMissionScale", 0) SetValue("gLevelScale", 1, "easeout", 0.3) Terminal.missions.detail.ScrollBar:reset() end end UiPush() if over then UiColor(1, 1, 0.5) end if id == gLevelHighlighted then UiPush() UiTranslate(-20-w/2+ math.sin(GetTime()*10)*8, 40) UiImage("ui/terminal/arrow.png") UiPop() end if unfinishedCount > 0 then UiScale(1) UiImage("ui/terminal/level-dot.png") UiFont("bold.ttf", 27) UiColor(0,0,0) UiText(unfinishedCount) else UiScale(1) UiImage("ui/terminal/level-dot.png") end UiPop() UiTranslate(0, 20) UiColor(0.2, 0.2, 0.2, 0.8) UiAlign("center") UiImageBox("ui/common/box-solid-11.png", w, h, 10, 10) if over then UiColor(1, 1, 0.5) else UiColor(1,1,1) end UiTranslate(0, 32) UiFont("bold.ttf", 27) UiText(title) if levelScore > 0 then UiTranslate(0, 34) UiFont("regular.ttf", 27) UiText(textScores) end UiPop() end UiPop() if level > 0 then self.detail:Draw(gLevelViewed) end controllerHint() end Terminal.missions.detail = { ScrollBar = UiCreateScrollBar(Terminal:CreateScrollBar(230)), leftPanelWidth = 817, leftPanelHeight = 860, rightPanelWidth = 610, rightPanelHeight = 860, Draw = function (self, id) self.ScrollBar.itemsPadding = 10 local levelFade = gLevelScale UiPush() UiColor(0, 0, 0, 0.7*levelFade) UiRect(UiWidth(), UiHeight()) UiPop() local hoverLevel = false local hoverMission = false UiPush() local width = self.leftPanelWidth local height = self.leftPanelHeight local x = gLevels[id].map_x*(1-levelFade) + UiCenter()*levelFade local y = gLevels[id].map_y*(1-levelFade) + UiMiddle()*levelFade UiTranslate(x,y) UiColorFilter(1,1,1, levelFade) UiScale(levelFade) local margin = 30 if isGamepadInUse() then margin = -12 end UiTranslate(-width/2 - (self.rightPanelWidth/2+10)*gMissionScale,-height/2+margin) hoverLevel = UiIsMouseInRect(width, height) UiPush() UiColor(0.2, 0.2, 0.2) UiImageBox("ui/common/box-solid-10.png", width, height, 10, 10) UiPop() UiPush() UiPush() UiAlign("center top") UiTranslate(width/2,20) UiPush() UiScale(0.947, 1.0) UiImage(gLevels[id].image) UiPop() UiTranslate(0, 212) fixedWidthText(GetTranslatedStringByKey(gLevels[id].title.."_UPPERCASE"), 550, "bold.ttf", 36) UiPop() UiTranslate(20, 295) UiPush() UiTextAlignment("center") UiColor(1, 1, 1, 0.5) UiWordWrap(777) h, _ = fixedWidthText(gLevels[id].desc, 777, "medium.ttf", 27) UiPop() UiTranslate(0, h-4) missions = getLevelMissions(gLevelViewed) local missionList = ToListItems(missions, function() return 780, 48 end, Terminal.missions.detail.DrawMissionItem) local contentHeight = 460 self.ScrollBar.background.h = contentHeight UiListEx(777, contentHeight, missionList, self.ScrollBar, "menu_up", "menu_down") UiPop() local levelScore = getLevelScore(id) if levelScore > 0 then UiPush() UiTranslate(width/2, height-32) UiFont("bold.ttf", 27) UiAlign("center middle") UiText(GetTranslatedStringByKey("UI_TEXT_SCORE") .. " " .. levelScore) UiPop() end local missionScale = gMissionScale if missionScale > 0 then local width = self.rightPanelWidth local height = self.rightPanelHeight UiTranslate(self.leftPanelWidth + 18, 0) UiScale(missionScale, 1) hoverMission = UiIsMouseInRect(width, height) UiColorFilter(1,1,1,missionScale) UiWindow(width, height, true) UiPush() UiColor(0.23, 0.23, 0.23) UiImageBox("ui/common/box-solid-10.png", width, height, 10, 10) UiPop() UiPush() UiTranslate(20,50) UiPush() UiAlign("left") UiColor(1, 1, 1) fixedWidthText(gMissions[gMissionViewed].title, 450, "bold.ttf", 36) UiPop() UiPush() UiTranslate(0, 48) UiFont("regular.ttf", 27) UiWordWrap(570) UiColor(1, 1, 1, 0.8) local _, h = UiText(gMissions[gMissionViewed].desc) UiTranslate(0, h + 24) UiColor(1, 1, 1, 0.6) local message = getMessageForMission(gMissionViewed) if message then local messageText = GetTranslatedStringByKey("UI_BUTTON_VIEW_EMAIL," .. gClients[message.from].name) local _, h = UiGetTextSize(messageText) UiButtonHoverColor(hexrgb(Ui.Style.Color.Yellow)) if UiTextButton(messageText) then Terminal:ShowMessage(gMissionViewed) end UiTranslate(0, h + 5) end local score = getMissionScore(gMissionViewed) local timeLeft = GetFloat("savegame.mod.mission."..gMissionViewed..".timeleft") local missionTime = GetFloat("savegame.mod.mission."..gMissionViewed..".missiontime") local details = getScoreDetails(gMissionViewed, score); if gSkipMissionJustPressed then score = 0 end if score > 0 then UiTranslate(0, 42) UiPush() UiFont("medium.ttf", 32) UiColor(1, 1, 1) UiText(GetTranslatedStringByKey("UI_TEXT_HIGHSCORE").." "..getLevelScore(id)) UiPop() UiTranslate(0, 14) drawScore(gMissionViewed, score, timeLeft, missionTime, false, 570, true) end UiPop() UiPop() UiPush() UiAlign("left middle") setFont("button") UiButtonImageBox("ui/common/box-outline-6.png", 6, 6, 0.96, 0.96, 0.96) UiTranslate(0, height-64) if gSkipMissionJustPressed then UiDisableInput() UiIgnoreNavigation() end local enableSkip = (score == 0 and GetInt("options.game.missionskipping") == 1) local padding = 20 local buttonWidth = 275 Ui:MediumFont(30) UiButtonHoverColor(hexrgb(Ui.Style.Color.Yellow)) if enableSkip then UiPush() UiTranslate(padding, 0) if UiTextButton("loc@UI_BUTTON_SKIP_MISSION", buttonWidth, 48) then skipMission(gMissionViewed) gDisableEvaluation = true if gMissionViewed == "frustrum_chase" then startCinematic("ending1") elseif gMissionViewed == "cullington_bomb" then startCinematic("ending2") else startHub() end gSkipMissionJustPressed = true end UiPop() end UiPush() UiAlign("center middle") if (enableSkip) then UiTranslate(UiWidth() - padding - buttonWidth / 2, 0) else UiTranslate(UiCenter(), 0) end -- NOTE: look ugly --UiScale(1.1 + math.sin(GetTime()*10)*0.06) local playText = GetTranslatedStringByKey("UI_BUTTON_PLAY") if score > 0 then playText = GetTranslatedStringByKey("PLAYTEXT_REPLAY_MISSION") end UiButtonHoverColor(hexrgb(Ui.Style.Color.Yellow)) if UiTextButton(playText, buttonWidth, 48) then startMission(gMissionViewed) end UiPop() UiPop() end UiPop() local hoverAnything = hoverLevel or hoverMission or gHoverTop or gHoverDebug if Terminal.currentPanel == Terminal.missions and gLevelScale == 1 and InputPressed("menu_cancel") or ((InputPressed("lmb") or InputPressed("menu_accept")) and not hoverAnything) then UiSound("terminal/level.ogg", 1, 0.9) SetValue("gLevelScale", 0, "easein", 0.3) gLevelSelected = "" gMissionSelected = "" end end } function Terminal.missions.detail:DrawMissionItem(i) local id = self.data local title = gMissions[id].title local details = getScoreDetails(id, getMissionScore(id)); local itemWidth = 777 local itemHeight = 54 UiAlign("left middle") UiTranslate(0, 30) local haveMarker = false if id == gMissionHighlighted then UiPush() UiTranslate(12 + math.sin(GetTime()*10)*4, 0) UiImage("ui/terminal/arrow.png") UiPop() haveMarker = true else if isMissionCompleted(id) then UiPush() UiTranslate(10, 0) UiImage("ui/terminal/checkmark.png") UiPop() haveMarker = true end end if id == gMissionSelected then UiColor(1,1,1,0.1) UiImageBox("ui/common/box-solid-6.png", itemWidth, itemHeight, 6, 6) end if UiIsMouseInRect(itemWidth, itemHeight) then UiColor(1,1,1,0.08) UiImageBox("ui/common/box-solid-6.png", itemWidth, itemHeight, 6, 6) if (InputPressed("lmb") or InputPressed("menu_accept")) then gMissionHighlighted = "" if gMissionSelected == id then UiSound("terminal/mission.ogg", 1, 0.9) SetValue("gMissionScale", 0, "cosine", 0.2) gMissionSelected = "" else UiSound("terminal/mission.ogg", 1, 1.1) gMissionSelected = id gMissionViewed = id SetValue("gMissionScale", 1, "cosine", 0.2) end end end if id == gMissionSelected then UiColor(1, 1, 0.5) else UiColor(1,1,1) end UiFont("medium.ttf", 27) if haveMarker then UiTranslate(60, 0) else UiTranslate(10, 0) end UiText(title) UiColor(1,1,1) local count = details.required + details.optional + details.bonuses local taken = details.requiredTaken + details.optionalTaken + details.bonusesTaken local offset = 760 if haveMarker then offset = 710 end UiTranslate(offset, 0) drawTargetDots(count, taken, false) end function init() gMissionSelected = "" gMissionViewed = "" gMissionHighlighted = "" gMissionScale = 0 gLevelSelected = "" gLevelViewed = "" gLevelHighlighted = "" gLevelScale = 0 gMessageSelected = "" gMessageAttachmentScale = 0 gMessageRightPanelScaleOnSelect = 0 gMessageScale = 0 gRankInfoScale = 0 gCashInfoScale = 0 gNoCash = 0 gCash = GetInt("savegame.mod.cash") gHoverTop = false gHoverDebug = false gHoverScore = false gHoverCash = false gDebugScale = 0 gTabScroll = Terminal.currentPanel.TabScroll gTimeSinceMessageRead = 0 gShowLargeUI = GetBool("game.largeui") if gShowLargeUI then gUiScaleUpFactor = 1.3 else gUiScaleUpFactor = 1.0 end local msg = ListKeys("savegame.mod.message") for i=1, #msg do local id = msg[i] if gMessages[id]==nil then ClearKey("savegame.mod.message."..id) end end end function tick() gTimeSinceMessageRead = gTimeSinceMessageRead + GetTimeStep() evaluate() end function evaluate() if gDisableEvaluation then return end if not isMessageReceived("nils_intro") then receiveMessage("nils_intro") end if not isMessageReceived("hub_tools") and GetBool("savegame.mod.backintime", false) then receiveMessage("hub_tools") end if not isMessageReceived("town_heist") and GetBool("savegame.mod.firsttools", false) then receiveMessage("town_heist") end if not isMessageReceived("town_explosive") and isMissionCompleted("town_heist") then receiveMessage("town_explosive") end if not isMessageReceived("ravine_heist") and isMissionCompleted("town_explosive") then --receiveMessage("ravine_heist") end if not isMessageReceived("town_destruction") and isMissionCompleted("ravine_heist") then receiveMessage("town_destruction") end if not isMessageReceived("ravine_tornado") and isMissionCompleted("town_destruction") then receiveMessage("ravine_tornado") end if not isMessageReceived("town_grease") and isMissionCompleted("ravine_tornado") then receiveMessage("town_grease") end if not isMessageReceived("ravine_motivational") and isMissionCompleted("town_grease") then --receiveMessage("ravine_motivational") end if not isMessageReceived("town_cars") and isMissionCompleted("ravine_motivational") then receiveMessage("town_cars") end if not isMessageReceived("ravine_bridge") and isMissionCompleted("town_cars") then receiveMessage("ravine_bridge") end if not isMessageReceived("end") and isMissionCompleted("ravine_bridge") then -- receiveMessage("end") end end function inCarib() --Evaluate if we're in caribbean hub based on level path return string.find(GetString("game.levelpath"), "hub_carib") end function isMessageReceived(messageId) return GetInt("savegame.mod.message."..messageId) > 0 end function isMessageRead(messageId, delay) if not delay then delay = 0 end return GetInt("savegame.mod.message."..messageId) == 2 and (delay == 0 or gTimeSinceMessageRead > delay) end function hasUnreadMessages() local messages = ListKeys("savegame.mod.message") for i=1,#messages do if GetInt("savegame.mod.message."..messages[i]) == 1 then return true end end return false end function isMissionCompleted(missionId) return missionId and getMissionScore(missionId) > 0 end function isSideMission(missionId) local m = gMissions[missionId] return m and m.sidemission end function isMissionInMessageCompleted(messageId) return isMissionCompleted(gMessages[messageId].mission) end function getTotalScore() local score = 0 local missions = ListKeys("savegame.mod.mission") for i=1,#missions do score = score + GetInt("savegame.mod.mission."..missions[i]..".score") end return score end function getUnfinishedMissionCount(levelId) local count = 0 for id,mission in pairs(gMissions) do if mission.level == levelId then if GetBool("savegame.mod.mission."..id) and GetInt("savegame.mod.mission."..id..".score") == 0 then count = count + 1 end end end return count end function getRank(score) local name for i=1,#gRanks do if score >= gRanks[i].score then name = gRanks[i].name end end return name end function maxRankTextWidth() local result = 0 for i=1,#gRanks do result = math.max(GetTextSize(gRanks[i].name), result) end return math.max(result, GetTextSize(GetTranslatedStringByKey("N_NEXT_RANK"))) end function receiveMessage(messageId) UiSound("terminal/new-message.ogg") SetInt("savegame.mod.message."..messageId, 1) end function unlockMission(missionId) if missionId == nil or missionId == "" then return end SetInt("savegame.mod.mission."..missionId, 1) syncActivities(missionId, false, false) end function isMissionUnlocked(missionId) return GetInt("savegame.mod.mission."..missionId) == 1 end function getLevelMissions(level) local missions = ListKeys("savegame.mod.mission") local levelMissions = {} for i=1,#missions do local missionId = missions[i] if gMissions[missionId] then if gMissions[missionId].level == level then levelMissions[#levelMissions+1] = missionId end end end return levelMissions end function getUnlockedLevels() local missions = ListKeys("savegame.mod.mission") local levels = {} local tmp = {} for i=1,#missions do local missionId = missions[i] if gMissions[missionId] then local level = gMissions[missionId].level if level ~= "" and tmp[level] == nil then tmp[level] = 1 levels[#levels+1] = level end end end return levels end function getMissionScore(missionId) return GetInt("savegame.mod.mission."..missionId..".score") end function getMessageForMission(missionId) for id,msg in pairs(gMessages) do if msg.mission == missionId then return msg end end return nil end function hasRank(rank) for i=1,#gRanks do if string.lower(gRanks[i].name) == string.lower(rank) then return getTotalScore() >= gRanks[i].score end end return false end function startMission(id) if gMissions[id] then StartLevel(id, gMissions[id].file, gMissions[id].layers, gNoLoadingScreen) end end ----------------------------------------------------------------------------------------------------------------------------------------- function draw() UiPush() if gDebugScale > 0 then local w = UiWidth() local h = UiHeight() local dw = 300*gDebugScale local mw = UiWidth() - dw UiPush() UiScale(gDebugScale, 1) UiWindow(300, h, true) if terminalDebug then terminalDebug() end UiPop() UiTranslate(dw, 0) UiScale(mw/w, 1) UiWindow(w, h, true) end Terminal:draw() UiPop() if not GetBool("game.deploy") then UiPush() setFont("h2") UiTranslate(60, 1066) UiColor(1, 1, 1, 0.5) UiAlign("center middle") gHoverDebug = UiIsMouseInRect(100, 30) if UiTextButton("loc@UI_BUTTON_DEBUG") then if gDebugScale > 0 then SetValue("gDebugScale", 0, "cosine", 0.2) else SetValue("gDebugScale", 1, "cosine", 0.2) end end UiPop() end end function setTab(tab) UiSound("terminal/tab.ogg") gTabSelected = tab if tab == "messages" then SetValue("gTabScroll", 0, "cosine", 0.4) elseif tab == "missions" then SetValue("gTabScroll", 1, "cosine", 0.4) end end function Button(text, callback) local w,_ = UiGetTextSize(GetTranslatedStringByKey(text)) local rx, ry = UiGetRelativePos() if UiTextButton(text) then callback() end UiTranslate(w + terminalTabs.marginRight,0) end function TabButton(text, tab) if gTabSelected == tab then UiColor(1,1,1) else UiColor(0.7, 0.7, 0.7) end Button(text, function() setTab(tab) end) end terminalTabs = { content = { {"loc@UI_BUTTON_MESSAGES", "messages"}, {"loc@UI_BUTTON_MISSIONS", "missions"}, }, wholeSize = 0, marginRight = 10, Draw = function (self) self.wholeSize = self.marginRight * (#self.content - 1) for _, button in pairs(self.content) do self.wholeSize = self.wholeSize + UiGetTextSize(GetTranslatedStringByKey(button[1])) end for _, button in pairs(self.content) do TabButton(button[1], button[2]) end end } function drawTop() UiPush() UiScale(gUiScaleUpFactor) UiColor(0.1,0.1,0.1) UiRect(UiWidth(), 50) gHoverTop = UiIsMouseInRect(UiWidth(), 50) setFont("h1") UiTranslate(UiCenter()/gUiScaleUpFactor - terminalTabs.wholeSize / 2, 25) UiAlign("middle") if hasUnreadMessages() and math.mod(GetTime(), 1.0) > 0.5 then UiPush() UiColor(.5, .8, 1) UiTranslate(-20, 0) UiImage("ui/terminal/new-message-dot.png") UiPop() end terminalTabs:Draw() UiPop() UiPush() UiScale(gUiScaleUpFactor) setFont("h2") local score = getTotalScore() local cash = GetInt("savegame.mod.cash") gCash = gCash + (cash-gCash)*GetTimeStep()*5 if math.abs(gCash - cash) < 1 then gCash = cash end local rw,_ = UiGetTextSize(getRank(score)) UiPush() UiTranslate(20, 10) UiColor(.2,.2,.2) UiImageBox("ui/common/box-solid-10.png", 155+rw, 34, 10, 10) if gHoverScore then UiColor(1,1,1) else UiColor(.8,.8,.8) end UiTranslate(20, 24) local textScore = GetTranslatedStringByKey("UI_TEXT_SCORE") UiText(textScore) UiTranslate(UiGetTextSize(textScore) + 60, 0) UiAlign("right") UiText(score) UiTranslate(20, 0) UiAlign("left") UiText(getRank(score)) UiPop() UiPush() local padding = 20 UiTranslate(UiWidth()/gUiScaleUpFactor - (20), 10) UiAlign("right") local overCash = UiIsMouseInRect(155+rw, 34) UiColor(.2,.2,.2) local textCash = GetTranslatedStringByKey("UI_TEXT_CASH_$," .. math.floor(gCash)) local textWidth = UiGetTextSize(textCash) UiImageBox("ui/common/box-solid-10.png", textWidth + 2 * padding, 34, 10, 10) if gHoverCash then UiColor(1,1,1) else UiColor(.8,.8,.8) end UiTranslate(-padding, 24) UiText(textCash) if gNoCash > 0 then UiColor(0.8,0.8,0.8,gNoCash) UiTranslate(-20, 40) UiText("loc@UI_TEXT_NOT_ENOUGH") end UiPop() local overScore = UiIsMouseInRect(195+rw, 50) if overScore and not gHoverScore then SetValue("gRankInfoScale", 1, "bounce", 0.4) elseif not overScore and gHoverScore then SetValue("gRankInfoScale", 0, "easein", 0.2) end gHoverScore = overScore if gRankInfoScale > 0 then UiPush() UiTranslate(20, 55) UiScale(1, gRankInfoScale) UiColor(.2,.2,.2) UiImageBox("ui/common/box-solid-10.png", 155+rw, gRankInfoHeight, 10, 10) UiPush() UiColor(.8, .8, .8) UiTranslate(0, 30) local didNext = false local didCurrent = false for i=#gRanks, 1, -1 do local valid = true local s = gRanks[i].score local n = gRanks[i].name if score < s then if score < gRanks[i-1].score then valid = false else didNext = true n = GetTranslatedStringByKey("N_NEXT_RANK") UiColor(0.4, 0.4, 0.4) end else if didCurrent then UiColor(.7,.7,.7) else UiColor(1,1,1) didCurrent = true end end if valid then UiPush() UiTranslate(110, 0) UiAlign("right") UiText(s) UiTranslate(20, 0) UiAlign("left") UiText(n) UiPop() UiTranslate(0, 24) end end UiTranslate(10, 10) setFont("p") UiColor(.6, .6, .6) UiWordWrap(280) UiText(GetTranslatedStringByKey("UI_TEXT_COMPLETE_MISSIONS"), true) _,gRankInfoHeight = UiGetRelativePos() UiPop() UiPop() end UiPush() UiTranslate(UiWidth()/gUiScaleUpFactor-190, 0) local overCash = UiIsMouseInRect(190, 50) UiPop() if overCash and not gHoverCash then SetValue("gCashInfoScale", 1, "bounce", 0.4) elseif not overCash and gHoverCash then SetValue("gCashInfoScale", 0, "easein", 0.2) end gHoverCash = overCash UiPop() end function showMessageByMission(mission) for id,_ in pairs(gMessages) do if gMessages[id].mission == mission then selectMessage(id) setTab("messages") return end end end function showMission(mission) Terminal.missions:SetCurrent() if gLevelSelected ~= gMissions[mission].level then gLevelHighlighted = gMissions[mission].level gLevelSelected = "" SetValue("gLevelScale", 0) end if gMissionSelected ~= mission then gMissionHighlighted = mission gMissionSelected = "" SetValue("gMissionScale", 0) end end function setFont(t, c) if t == "h1" then UiFont("bold.ttf", 42) end if t == "h1.5" then UiFont("bold.ttf", 36) end if t == "h2" then UiFont("bold.ttf", 30) end if t == "button" then UiFont("regular.ttf", 28) end if t == "p" then UiFont("regular.ttf", 27) end if t == "name" then UiFont("bold.ttf", 27) end if c == "mark" then Ui.Color(Ui.Style.Color.Mark) elseif c == "dim" then Ui.Color(Ui.Style.Color.Dim) -- selected elseif c == "sel" then Ui.Color(Ui.Style.Color.Yellow) elseif c ~= "nocolor" then Ui.Color(Ui.Style.Color.White) end end