#include "script/common.lua" #include "ui/ui_extensions.lua" #include "ui/ui_helpers.lua" #include "buttons.lua" #include "game.lua" ModManager = {} ModManager.Sort = { Alphabetical = 1, RecentUpdate = 2, RecentSubscribe = 3, Count = 3, toString = function(s) if ModManager.Sort.Alphabetical == s then return "loc@UI_BUTTON_ALPHABETICAL" elseif ModManager.Sort.RecentUpdate == s then return "loc@UI_BUTTON_RECENTLY_UPDATED" elseif ModManager.Sort.RecentSubscribe == s then return "loc@UI_BUTTON_RECENTLY_SUBSCRIBED" end return "Unknown" end } ModManager.Filter = { All = 1, Global = 2, Content = 3, Count = 3, toString = function(f) if ModManager.Filter.All == f then return "loc@UI_BUTTON_ALL" elseif ModManager.Filter.Global == f then return "loc@UI_BUTTON_GLOBAL" elseif ModManager.Filter.Content == f then return "loc@UI_BUTTON_CONTENT" end return "Unknown" end } ModManager.List = function(obj) local list = Ui.VerticalList { state = { lerpSpeed = 30, itemsPadding = 4, scrollbarOffset = 5, background = { w = 10, h = 334, color = {1, 1, 1, 0.3}, imageBox = { "common/box-3.png", 3, 3 } }, thumb = { w = 6, color = {1, 1, 1, 0.6}, imageBox = { "common/box-2.png", 2, 2 } }, holdFocusOnListEnd = true }, sourceList = {}, sortType = ModManager.Sort.Alphabetical, filterType = ModManager.Filter.All, refresh = function(self, modsList) self.sourceList = modsList or self.sourceList self.children = {} local filterCondition = function(mod) return (mod.isplayable and self.filterType == ModManager.Filter.Content) or (not mod.isplayable and self.filterType == ModManager.Filter.Global) or (self.filterType == ModManager.Filter.All) end local sortCondition = function(m1, m2) if self.sortType == ModManager.Sort.Alphabetical then return tostring(string.lower(m1.name)) < tostring(string.lower(m2.name)) elseif self.sortType == ModManager.Sort.RecentUpdate then return m1.steamtime > m2.steamtime elseif self.sortType == ModManager.Sort.RecentSubscribe then return m1.subscribetime > m2.subscribetime end return false end local filtered = tableFilter(self.sourceList, filterCondition) table.sort(filtered, sortCondition) self:appendMany(filtered) local restoredId = GetString("mods.modmanager.selectedmod") if restoredId ~= "" then local contains, mod = tableContains(self.children, function(it) return it.id == restoredId end) if contains then ModManager.selectMod(mod) end end local offset = self.state:getOffset() self.state:reset() self.state:addOffset(offset, true) end, remove = function(self, id) local sourceIdx = 0 for i, m in ipairs(self.sourceList) do if m.id == id then sourceIdx = i break end end local childIdx = 0 for i, m in ipairs(self.children) do if m.id == id then childIdx = i break end end if sourceIdx ~= 0 then table.remove(self.sourceList, sourceIdx) end if childIdx ~= 0 then table.remove(self.children, childIdx) if ModManager.selectedMod.id == id then local newIdx = clamp(childIdx - 1, 1, #self.children) ModManager.selectMod(self.children[newIdx]) end end end, onPreDraw = function(self) if InputPressed("rmb") and self.drawState.hovered then ModManager.ContextMenu.contextList = self ModManager.ContextMenu.contextMod = ModManager.hoveredMod ModManager.ContextMenu:show() end end } return list:new(obj) end ModManager.EmptyMod = { empty = true } ModManager.selectedMod = ModManager.EmptyMod ModManager.selectMod = function(mod) if not mod then if not ModManager.selectedMod.empty then ModManager.WindowAnimation:reset() ModManager.selectedMod = ModManager.EmptyMod SetString("mods.modmanager.selectedmod", "") end return end if ModManager.selectedMod.id == mod.id then return end if ModManager.selectedMod.empty then ModManager.WindowAnimation:play() end ModManager.selectedMod.detailedInfo = nil ModManager.selectedMod = mod Command("mods.updateselecttime", mod.id) SetString("mods.modmanager.selectedmod", mod.id) if ModManager.selectedMod.showbold then ModManager.selectedMod.showbold = false SetBool("mods.available.".. ModManager.selectedMod.id .. ".showbold", false) end local key = "mods.available." .. mod.id ModManager.selectedMod.detailedInfo = { fullName = GetString(key .. ".name"), author = GetString(key .. ".author"), tags = GetString(key .. ".tags"), desc = GetString(key .. ".description"), timestamp = GetString(key .. ".timestamp") } ModManager.Window.modDetailsScrollArea.scrollBar:reset() end ModManager.hoveredMod = nil ModManager.ModItem = function(obj) local item = Ui.Frame { w = 350, h = 26, Ui.ImageBox { resizeToParent = true, id = "highlight", path = "common/box-4.png", border = { width = 4, height = 4 }, color = 0xD9D9D91B, visible = false, }, Ui.Frame { w = 26, h = 26, Ui.Image { vAlign = Ui.ContentLayout.Vertical.Middle, hAlign = Ui.ContentLayout.Horizontal.Center, id = "actIndicator", path = "common/circle_hollow_12x12.png", color = 0xFFFFFF66, visible = false }, onCreate = function(self) self.indicator = self:findById("actIndicator") end, onPressed = function(self) self.parent:toggleActive() end, onPreDraw = function(self) if self.parent.active then self.indicator.path = "common/circle_filled_12x12.png" self.indicator.color = 0xFFFF7F else self.indicator.path = "common/circle_hollow_12x12.png" self.indicator.color = 0xFFFFFF66 end end }, Ui.Frame { w = 324, h = 26, margin = { left = 26 }, Ui.Text { vAlign = Ui.ContentLayout.Vertical.Middle, id = "modName", font = { "regular.ttf", 18 }, text = "modItem" }, onCreate = function(self) self.modName = self:findById("modName") end, onPressed = function(self) ModManager.selectMod(self.parent) end, onPreDraw = function(self) UiClipRect(self.w, self.h, true) if self.slide then if self.slideOffset < self.slideSpan then self.slideOffset = self.slideOffset + self.slideSpeed * GetTimeStep() elseif self.slideOffset >= self.slideSpan and not self.isWaiting then self.isWaiting = true self.waitStart = GetTime() elseif self.isWaiting then local wdt = GetTime() - self.waitStart if wdt > self.waitTime then self.isWaiting = false self.slideOffset = 0 end end self.modName.margin.left = -self.slideOffset end end, onMouseEnter = function(self) if self.w < self.modName.w then self.slide = true self.slideSpeed = 150 self.waitTime = 1 self.isWaiting = false self.slideSpan = self.modName.w - self.w self.slideOffset = 0 end end, onMouseLeave = function(self) if self.slide then self.slide = false self.isWaiting = false self.slideOffset = 0 self.modName.margin.left = 0 end end }, onCreate = function(self) self.modName = self:findById("modName") self.indicator = self:findById("actIndicator") self.highlight = self:findById("highlight") end, onPreDraw = function(self) self.x, self.y = UiGetCursorPos() local navId = UiNavComponent(self.w, self.h) local isFocused = UiIsComponentInFocus(navId) if isFocused and ModManager.selectedMod.id ~= self.id then ModManager.selectMod(self) end self.modName.text = self.name self.indicator.visible = self.override if ModManager.selectedMod.id == self.id then self.highlight.visible = true self.modName.color = 0xFFFF7F self.highlight.color = 0xD9D9D91B elseif self.drawState.hovered then self.modName.color = 0xFFFF7FCF self.highlight.visible = true self.highlight.color = 0x10000001F else self.modName.color = 0xFFFFFF self.highlight.visible = false self.highlight.color = 0xD9D9D91B end if self.showbold then self.modName.font = { "bold.ttf", 20 } self.outlineColor = { 1, 1, 1, 1 } self.outlineThickness = 0.4 else self.modName.font = { "regular.ttf", 18 } self.outlineColor = { 0, 0, 0, 0 } self.outlineThickness = 0 end end, onMouseEnter = function(self) ModManager.hoveredMod = self end, onMouseLeave = function(self) ModManager.hoveredMod = nil end, toggleActive = function(self) if self.isplayable then return end self.active = not self.active local modKey = "mods.available." .. self.id SetBool(modKey .. ".active", self.active) if self.active then Command("mods.activate", self.id) else Command("mods.deactivate", self.id) end end } return item:new(obj) end ModManager.Window = Ui.Window { w = 1232, animator = { playTime = 0.25 }, ignoreNavigation = true, Ui.Frame { resizeToParent = true, Ui.ImageBox { resizeToParent = true, path = "common/box-9.png", border = { width = 9, height = 9 }, color = 0x100000099 }, Ui.VerticalLayout { id = "windowContent", hAlign = Ui.ContentLayout.Horizontal.Center, alignment = Ui.ContentLayout.Horizontal.Center, padding = 10, Ui.Text { margin = { top = 20 }, font = { "bold.ttf", 30 }, text = "loc@MOD_MANAGER_UPPER", }, Ui.Text { font = { "medium.ttf", 20 }, text = "loc@UI_TEXT_CREATE_YOUR", color = 0xFFFFFF90, wrapWidth = 850, linesAlignment = Ui.TextAlignment.Center, }, Ui.HorizontalLayout { margin = { top = -10 }, padding = 10, alignment = Ui.ContentLayout.Vertical.Center, Ui.Text { font = { "medium.ttf", 22 }, text = "[[menu:pause;iconsize=42,42;color=255,255,255]]", visible = false }, --Ui.Text --{ -- font = { "medium.ttf", 18 }, -- text = "www.teardowngame.com/modding", -- color = 0xFFFF7F90, -- underline = true, -- onMouseEnter = function(self) -- self.color = 0xFFFF7F64 -- end, -- onMouseLeave = function(self) -- self.color = 0xFFFF7F90 -- end, -- onPressed = function(self) -- Command("game.openurl", "http://www.teardowngame.com/modding") -- end, --} }, Ui.HorizontalLayout { padding = 15, margin = { top = 10 }, Ui.VerticalLayout { padding = 10, Ui.Text { font = { "medium.ttf", 22 }, text = "loc@TITLE_BUILT-IN", }, Ui.Frame { w = 380, h = 498, Ui.ImageBox { resizeToParent = true, path = "common/box-6.png", border = { width = 6, height = 6 }, color = 0xFFFFFF19 }, ModManager.List { id = "builtInModsList", margin = { top = 10, left = 10 }, w = 350, h = 478 } } }, Ui.VerticalLayout { padding = 10, alignment = Ui.ContentLayout.Horizontal.Center, Ui.Text { hAlign = Ui.ContentLayout.Horizontal.Left, font = { "medium.ttf", 22 }, text = "loc@TITLE_SUBSCRIBED", }, Ui.HorizontalLayout { padding = 10, Teardown.GenericButton { id = "filterButton", ignoreNavigation = true, w = 185, h = 38, hoveredImageBox = {"ui/common/box-6.png", 6, 6, 1, 1, 1, 0.3 }, imageBox = {"ui/common/box-6.png", 6, 6, 1, 1, 1, 0.1 }, textHoverColor = {0.96, 0.96, 0.96, 0.8}, overflowHandling = Teardown.Button.TextOverflow.Truncate, text = ModManager.Filter.toString(ModManager.Filter.All), filterType = ModManager.Filter.All, onPressed = function(self) if not self.list then self.list = self.parent.parent:findById("subscribedModsList") end self.filterType = self.filterType + 1 if self.filterType > ModManager.Filter.Count then self.filterType = 1 end self.text = ModManager.Filter.toString(self.filterType) self.list.filterType = self.filterType self.list:refresh() end, }, Teardown.GenericButton { id = "sortButton", ignoreNavigation = true, w = 185, h = 38, hoveredImageBox = {"ui/common/box-6.png", 6, 6, 1, 1, 1, 0.3 }, imageBox = {"ui/common/box-6.png", 6, 6, 1, 1, 1, 0.1 }, textHoverColor = {0.96, 0.96, 0.96, 0.8}, overflowHandling = Teardown.Button.TextOverflow.Truncate, text = ModManager.Sort.toString(ModManager.Sort.Alphabetical), sortType = ModManager.Sort.Alphabetical, onPressed = function(self) if not self.list then self.list = self.parent.parent:findById("subscribedModsList") end self.sortType = self.sortType + 1 if self.sortType > ModManager.Sort.Count then self.sortType = 1 end self.text = ModManager.Sort.toString(self.sortType) self.list.sortType = self.sortType self.list:refresh() end } }, Ui.Frame { w = 380, h = 402, Ui.ImageBox { resizeToParent = true, path = "common/box-6.png", border = { width = 6, height = 6 }, color = 0xFFFFFF19 }, ModManager.List { id = "subscribedModsList", margin = { top = 10, left = 10 }, w = 350, h = 382 } }, Teardown.GenericButton { w = 380, h = 38, hoveredImageBox = {"ui/common/box-6.png", 6, 6, 1, 1, 1, 0.3 }, imageBox = {"ui/common/box-6.png", 6, 6, 1, 1, 1, 0.1 }, textHoverColor = {0.96, 0.96, 0.96, 0.8}, ignoreNavigation = true, text = "loc@UI_BUTTON_MANAGE_SUBSCRIBED", overflowHandling = Teardown.Button.TextOverflow.Truncate, onPressed = function(self) Command("mods.browse") end } }, Ui.VerticalLayout { padding = 10, Ui.Text { font = { "medium.ttf", 22 }, text = "loc@TITLE_LOCAL_FILES", }, Ui.Frame { w = 380, h = 498, Ui.ImageBox { resizeToParent = true, path = "common/box-6.png", border = { width = 6, height = 6 }, color = 0xFFFFFF19 }, ModManager.List { id = "localModsList", margin = { top = 10, left = 10 }, w = 350, h = 478 } } } }, Ui.Frame { id = "modDetailsView", ignoreNavigation = true, w = 1172, h = 254, margin = { top = 10 }, Ui.ImageBox { resizeToParent = true, path = "common/box-6.png", border = { width = 6, height = 6 }, color = 0xFFFFFF19 }, Ui.Frame { w = 1010, h = 214, margin = { left = 20, top = 20 }, Ui.ScrollableArea { w = 850, h = 220, id = "modDetailsScrollArea", Ui.VerticalLayout { padding = 8, Ui.Text { id = "modName", font = { "medium.ttf", 25 }, text = "MOD TITLE", wrapWidth = 840 }, Ui.Text { id = "modAuthor", font = { "medium.ttf", 20 }, text = "By someone", color = 0xFFFFFFCC, wrapWidth = 840 }, Ui.Text { id = "modTags", font = { "medium.ttf", 20 }, text = "Some tags", color = 0xFFFFFFCC, wrapWidth = 840 }, Ui.Text { id = "modDesc", font = { "medium.ttf", 20 }, text = "Description", wrapWidth = 840, color = 0xFFFFFF99, }, Ui.Text { id = "modUpdateTime", font = { "medium.ttf", 20 }, text = "Update time", color = 0xFFFFFF66, } } } }, Teardown.GenericButton { id = "makeCopyBtn", vAlign = Ui.ContentLayout.Vertical.Top, hAlign = Ui.ContentLayout.Horizontal.Right, margin = { top = 16, right = 16 }, overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 248, h = 42, text = "loc@UI_BUTTON_MAKE_LOCAL", onPressed = function(self) Command("mods.makelocalcopy", ModManager.selectedMod.id) ModManager.Window:refresh() end }, Ui.Frame { vAlign = Ui.ContentLayout.Vertical.Top, hAlign = Ui.ContentLayout.Horizontal.Right, margin = { top = 16, right = 16 }, w = 248, h = 100, Ui.VerticalLayout { vAlign = Ui.ContentLayout.Vertical.Bottom, hAlign = Ui.ContentLayout.Horizontal.Left, padding = 16, Teardown.GenericButton { id = "editBtn", overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 248, h = 42, text = "loc@UI_BUTTON_EDIT", onPressed = function(self) Command("mods.edit", ModManager.selectedMod.id) end }, Teardown.GenericButton { id = "publishBtn", overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 248, h = 42, text = "loc@UI_BUTTON_PUBLISH", onPressed = function(self) ModManager.PublishWindow:show() end } } }, Ui.VerticalLayout { vAlign = Ui.ContentLayout.Vertical.Bottom, hAlign = Ui.ContentLayout.Horizontal.Right, padding = 16, margin = { bottom = 16, right = 16 }, Teardown.GenericButton { id = "optionsBtn", overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 248, h = 42, text = "loc@UI_BUTTON_OPTIONS", visible = false, onPressed = function(self) Command("mods.options", ModManager.selectedMod.id) end }, Teardown.GenericButton { id = "playBtn", overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 248, h = 42, text = "loc@UI_BUTTON_PLAY", onPressed = function(self) Command("mods.play", ModManager.selectedMod.id) end }, Teardown.GenericButton { id = "enableBtn", overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 248, h = 42, text = "enableBtn", onPressed = function(self) ModManager.selectedMod:toggleActive() end }, }, Ui.Text { hAlign = Ui.ContentLayout.Horizontal.Center, vAlign = Ui.ContentLayout.Vertical.Bottom, margin = { bottom = -30 }, id = "pathToLocalMod", visible = false, font = { "regular.ttf", 20 }, text = "Description", color = 0xFFFFFF80, onMouseEnter = function(self) self.color = 0xFFFF7F end, onMouseLeave = function(self) self.color = 0xFFFFFF80 end, onPressed = function(self) Command("game.openfolder", GetString("mods.available." .. ModManager.selectedMod.id ..".path")) end, }, onCreate = function(self) self.modName = self:findById("modName") self.modAuthor = self:findById("modAuthor") self.modTags = self:findById("modTags") self.modDesc = self:findById("modDesc") self.modUpdateTime = self:findById("modUpdateTime") self.makeCopyBtn = self:findById("makeCopyBtn") self.optionsBtn = self:findById("optionsBtn") self.playBtn = self:findById("playBtn") self.enableBtn = self:findById("enableBtn") self.editBtn = self:findById("editBtn") self.publishBtn = self:findById("publishBtn") end, onPreDraw = function(self) if not self.pathToLocalMod then self.pathToLocalMod = self.root:findById("pathToLocalMod") end if not ModManager.selectedMod.detailedInfo then self.pathToLocalMod.visible = false return end local mod = ModManager.selectedMod local info = mod.detailedInfo self.modName.text = info.fullName self.modAuthor.text = GetTranslatedStringByKey("UI_TEXT_BY") .. " " .. info.author self.modTags.text = GetTranslatedStringByKey("UI_TEXT_TAGS") .. " " .. info.tags self.modDesc.text = info.desc if mod.isSubscribed then self.modUpdateTime.visible = true self.modUpdateTime.text = GetTranslatedStringByKey("UI_TEXT_UPDATED") .. " " .. info.timestamp else self.modUpdateTime.visible = false end local isCopyable = not startsWith(mod.id, "dlc-") or mod.id == "dlc-artvandals" self.makeCopyBtn.visible = not mod.isLocal and isCopyable self.optionsBtn.visible = mod.hasOptions self.enableBtn.visible = mod.override self.playBtn.visible = mod.isplayable if self.enableBtn.visible then local enabledTxt = "[[menu/promo-active-btn.png;iconsize=42,42;color=255,255,127]]" .. GetTranslatedStringByKey("UI_BUTTON_ENABLED") local disabledTxt = "[[common/circle_hollow_icon.png;iconsize=42,42;color=217,217,217]]" .. GetTranslatedStringByKey("UI_BUTTON_DISABLED") self.enableBtn.text = mod.active and enabledTxt or disabledTxt end self.editBtn.visible = mod.isplayable and mod.isLocal self.publishBtn.visible = mod.isLocal and GetBool("game.workshop") and GetBool("game.workshop.publish") self.pathToLocalMod.visible = mod.isLocal == true self.pathToLocalMod.text = GetString("mods.available." .. mod.id ..".path") end }, } }, onCreate = function(self) self.subscribedMods = self:findById("subscribedModsList") self.builtinMods = self:findById("builtInModsList") self.localMods = self:findById("localModsList") self.windowContent = self:findById("windowContent") self.modDetailsScrollArea = self:findById("modDetailsScrollArea") end, onPreDraw = function(self) if self.animator.isFinished then UiSetCursorState(UI_CURSOR_SHOW) end SetFloat("game.music.volume", (1.0 - 0.8 * self.animator.factor)) end, onShow = function(self) self:refresh() self.modDetailsScrollArea.scrollBar:reset() ModManager.WindowAnimation.duration = 0.25 ModManager.WindowAnimation:init(self) end, canRestore = function(self) return GetString("mods.modmanager.selectedmod") ~= "" end, onRestore = function(self) ModManager.WindowAnimation.duration = 0.0 ModManager.WindowAnimation:init(self) self:refresh() end, onClose = function(self) ModManager.WindowAnimation:reset(self) ModManager.selectedMod = ModManager.EmptyMod SetString("mods.modmanager.selectedmod", "") end, refresh = function(self) Command("mods.refresh") local ids = ListKeys("mods.available") local createTransform = function(info) return function(mId) local modItem = ModManager.ModItem { -- logical part id = mId, name = GetString("mods.available.".. mId ..".listname"), override = GetBool("mods.available.".. mId ..".override") and not GetBool("mods.available.".. mId ..".playable"), active = GetBool("mods.available.".. mId ..".active"), steamtime = GetInt("mods.available.".. mId ..".steamtime"), subscribetime = GetInt("mods.available.".. mId ..".subscribetime"), showbold = GetBool("mods.available.".. mId .. ".showbold"), isplayable = GetBool("mods.available.".. mId ..".playable"), hasOptions = GetBool("mods.available." .. mId .. ".options") } for k,v in pairs(info) do modItem[k] = v end return modItem end end local canShow = function(m) local isExpansion = tableContains(gExpansions, function(v) return v.level == m end) return not isExpansion or m == "dlc-artvandals" end self.builtinMods:refresh( tableTransform( tableFilter( ids, function(m) return canShow(m) and (string.sub(m, 1, 8) == "builtin-" or string.sub(m, 1, 4) == "dlc-") end ), createTransform({ isBuiltin = true }) ) ) self.subscribedMods:refresh( tableTransform( tableFilter( ids, function(m) return canShow(m) and (string.sub(m, 1, 6) == "steam-" or string.sub(m, 1, 5) == "pros-") end ), createTransform({ isSubscribed = true }) ) ) self.localMods:refresh( tableTransform( tableFilter( ids, function(m) return canShow(m) and string.sub(m, 1, 6) == "local-" end ), createTransform({ isLocal = true }) ) ) end } ModManager.WindowAnimation = { duration = 0.25, curve = "cosine", progress = 0.0, init = function(self, window) local modDetails = window:findById("modDetailsView") window.modDetails = modDetails local detailsPreDraw = modDetails.onPreDraw modDetails.onPreDraw = function(self) if ModManager.WindowAnimation.progress == 0.0 then UiDisableInput() end UiScale(1, ModManager.WindowAnimation.progress) detailsPreDraw(self) end local windowPreDraw = window.onPreDraw window.onPreDraw = function(self) local factor = ModManager.WindowAnimation.progress if factor > 0 and factor < 1 then UiIgnoreNavigation() end local _, detailsH = self.modDetails:getSize() local _, windowH = self.windowContent:getSize() -- initial window height when details are hidden windowH = windowH - detailsH + 10 -- lerp to window height when details are shown self.h = windowH + factor * (detailsH + 25) local canvas = UiCanvasSize() self.x = canvas.w / 2 - self.w / 2 self.y = canvas.h / 2 - self.h / 2 windowPreDraw(self) end end, play = function(self) self:reset() SetValueInTable(self, "progress", 1, self.curve, self.duration) end, reset = function(self) self.progress = 0.0 end } ModManager.ContextMenuItem = function(obj) local item = Ui.Frame { w = 232, h = 42, Ui.ImageBox { resizeToParent = true, id = "highlight", path = "common/box-9.png", border = { width = 9, height = 9 }, color = 0xFFFFFF26 }, Ui.Text { id = "label", vAlign = Ui.ContentLayout.Vertical.Middle, margin = { left = 10, right = 10 }, font = { "medium.ttf", 22 }, text = "item", }, onPreDraw = function(self) self.w = self.label.margin.left + self.label.w + self.label.margin.right self.highlight.visible = self.drawState.hovered and not self.disabled self.label.text = self.text self.label.color = (self.drawState.hovered and not self.disabled) and 0xFFFF7F or 0xFFFFFF end } item.highlight = item:findById("highlight") item.label = item:findById("label") item.text = "" item.disabled = false local it = item:new(obj) if it.onPressed then local originOnPressed = it.onPressed it.onPressed = function(self) if self.disabled then return end originOnPressed(self) end end return it end ModManager.ContextMenu = Ui.Window { fitToContent = true, blur = false, Ui.ImageBox { resizeToParent = true, path = "common/box-8.png", border = { width = 8, height = 8 }, color = 0x585858 }, Ui.VerticalLayout { Ui.VerticalLayout { margin = { left = 10, right = 10, bottom = 10, top = 10 }, id = "menuItems", ModManager.ContextMenuItem { id = "unsubscribe", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_UNSUBSCRIBE", onPressed = function(self) local modId = ModManager.ContextMenu.contextMod.id Command("mods.unsubscribe", modId) ModManager.Window.subscribedMods:remove(modId) self.root:hide() end }, ModManager.ContextMenuItem { id = "browse", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_DETAILS", onPressed = function(self) Command("mods.browsesubscribed", ModManager.ContextMenu.contextMod.id) ModManager.Window:refresh() self.root:hide() end }, ModManager.ContextMenuItem { id = "newGlobalMod", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_NEW_GLOBAL", onPressed = function(self) Command("mods.new", "global") ModManager.Window:refresh() self.root:hide() end }, ModManager.ContextMenuItem { id = "newContentMod", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_NEW_CONTENT", onPressed = function(self) Command("mods.new", "content") ModManager.Window:refresh() self.root:hide() end }, ModManager.ContextMenuItem { id = "duplicateMod", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_DUPLICATE_MOD", onPressed = function(self) Command("mods.makelocalcopy", ModManager.ContextMenu.contextMod.id) ModManager.Window:refresh() self.root:hide() end }, ModManager.ContextMenuItem { id = "deleteMod", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_DELETE_MOD", onPressed = function(self) local modId = ModManager.ContextMenu.contextMod.id ModManager.AcceptDialog.yes.onPressed = function(self) Command("mods.delete", modId) ModManager.Window.localMods:remove(modId) ModManager.AcceptDialog:hide() end ModManager.AcceptDialog.no.onPressed = function(self) ModManager.AcceptDialog:hide() end ModManager.AcceptDialog:show() self.root:hide() end }, ModManager.ContextMenuItem { id = "disableAll", font = { "regular.ttf", 20 }, text = "loc@UI_TEXT_DISABLE_ALL", onPressed = function(self) local list = ModManager.ContextMenu.contextList for mod in filter(list.children, function(it) return it.override end) do if mod.active then mod:toggleActive() end end self.root:hide() end } } }, animator = { curveType = Ui.AnimationCurves.Bounce, playTime = 0.35, onAnimationUpdate = function(self, factor) UiAlign("top left") UiScale(1, factor) UiWindow(self.parent.w, self.parent.h, self.parent.clip) end }, onCreate = function(self) self.menuLayout = self:findById("menuItems") self.unsubscribe = self:findById("unsubscribe") self.browse = self:findById("browse") self.newGlobalMod = self:findById("newGlobalMod") self.newContentMod = self:findById("newContentMod") self.duplicateMod = self:findById("duplicateMod") self.deleteMod = self:findById("deleteMod") self.disableAll = self:findById("disableAll") end, onShow = function(self) self.animator.playTime = 0.0 local x, y = UiGetCanvasMousePos() self.x = x self.y = y for _, it in ipairs(self.menuLayout.children) do it.visible = false end local itemInContext = self.contextMod ~= nil if self.contextList.id == "builtInModsList" then self.disableAll.visible = true elseif self.contextList.id == "subscribedModsList" then self.disableAll.visible = true self.unsubscribe.visible = itemInContext self.browse.visible = itemInContext elseif self.contextList.id == "localModsList" then self.newGlobalMod.visible = true self.newContentMod.visible = true self.duplicateMod.visible = itemInContext self.deleteMod.visible = itemInContext self.disableAll.visible = true end local activeCount = tableCount(self.contextList.children, function(it) return it.active end) if activeCount > 0 then self.disableAll.colorFilter = {1,1,1,1} self.disableAll.disabled = false else self.disableAll.colorFilter = {1,1,1,0.5} self.disableAll.disabled = true end end, onClose = function(self) self.animator.playTime = 0.35 self.contextList = nil self.contextMod = nil end, onLogicUpdate = function(self) if InputPressed("rmb") then self:hide() end end } ModManager.AcceptDialog = Ui.Window { w = 440, h = 210, animator = { playTime = 0.1, onAnimationUpdate = function(self, factor) UiTranslate(self.parent.w / 2, self.parent.h / 2) UiAlign("middle center") if self.parent.blur then UiBlur(factor) end UiColorFilter(1, 1, 1, factor) UiWindow(self.parent.w, self.parent.h, self.parent.clip) UiAlign("top left") UiTranslateToWindowTopLeft() end }, Ui.Frame { resizeToParent = true, Ui.ImageBox { resizeToParent = true, path = "common/box-8.png", border = { width = 8, height = 8 }, color = 0x10000008C }, Ui.Frame { w = 400, h = 110, margin = { top = 30 }, vAlign = Ui.ContentLayout.Vertical.Top, hAlign = Ui.ContentLayout.Horizontal.Center, Ui.Text { vAlign = Ui.ContentLayout.Vertical.Middle, hAlign = Ui.ContentLayout.Horizontal.Center, font = { "medium.ttf", 22 }, text = "loc@ARE_YOU", wrapWidth = 400, linesAlignment = Ui.TextAlignment.Center } }, Ui.HorizontalLayout { margin = { bottom = 30 }, padding = 20, vAlign = Ui.ContentLayout.Vertical.Bottom, hAlign = Ui.ContentLayout.Horizontal.Center, Teardown.GenericButton { id = "yesButton", w = 130, h = 40, colorFilter = { 0.9, 0.23, 0.23 }, text = "loc@UI_BUTTON_YES", onMouseEnter = function(self) self.colorFilter = { 1, 1, 1 } end, onMouseLeave = function(self) self.colorFilter = { 0.9, 0.23, 0.23 } end }, Teardown.GenericButton { id = "noButton", w = 130, h = 40, text = "loc@UI_BUTTON_NO" } } }, onCreate = function(self) self.yes = self:findById("yesButton") self.no = self:findById("noButton") end, onShow = function(self) local canvas = UiCanvasSize() self.x = canvas.w / 2 - self.w / 2 self.y = canvas.h / 2 - self.h / 2 end } ModManager.ValidatedText = function(obj) local txt = Ui.Text { defaultText = "", errorText = "error", errorNotificationText = "loc@FAILMESSAGE_INCOMPLETE_INFORMATION" } local originalDraw = txt.draw txt.draw = function(self) if self.defaultText == "" then self.text = self.errorText self.color = 0xCC2B2A ModManager.publishErrorText = self.errorNotificationText else self.text = self.defaultText self.color = self.defaultColor end originalDraw(self) end local item = txt:new(obj) item.defaultColor = item.color return item end ModManager.ProgressBar = function(obj) local bar = Ui.Frame { w = 350, h = 40, Ui.Rect { resizeToParent = true, color = { 0, 0, 0 } }, Ui.Frame { resizeToParent = true, padding = { top = 2, left = 2, right = 2, bottom = 2 }, Ui.Rect { vAlign = Ui.ContentLayout.Vertical.Middle, id = "progressRect", color = { 1, 1, 1 } }, Ui.Text { vAlign = Ui.ContentLayout.Vertical.Middle, hAlign = Ui.ContentLayout.Horizontal.Center, text = "loc@UI_TEXT_UPLOADING", font = { "regular.ttf", 20 }, color = 0x7F7F7F } } } bar.progressRect = bar:findById("progressRect") bar.updateProgress = function(self, value) local p = self.progressRect.parent self.progressRect.w = (p.w - p.padding.left - p.padding.right) * value self.progressRect.h = p.h - p.padding.top - p.padding.bottom end return bar:new(obj) end ModManager.PublishWorkshop = { Steam = 0, Pros = 1, Count = 2, toString = function(s) if ModManager.PublishWorkshop.Pros == s then return "loc@UI_WORKSHOP_PROS" end return "loc@UI_WORKSHOP_STEAM" end } ModManager.PublishVisibility = { Public = 0, Friends = 1, Private = 2, Count = 3, toString = function(s) if ModManager.PublishVisibility.Public == s then return "loc@UI_BUTTON_PUBLIC" elseif ModManager.PublishVisibility.Friends == s then return "loc@UI_BUTTON_FRIENDS" elseif ModManager.PublishVisibility.Private == s then return "loc@UI_BUTTON_PRIVATE" end return "loc@UI_BUTTON_UNLISTED" end } ModManager.PublishWindow = Ui.Window { w = 720, h = 820, animator = { playTime = 0.25 }, Ui.Frame { resizeToParent = true, Ui.ImageBox { resizeToParent = true, path = "common/box-solid-shadow-50.png", border = { width = -50, height = -50 }, color = 0x10000007F }, Ui.VerticalLayout { hAlign = Ui.ContentLayout.Horizontal.Center, margin = { top = 30 }, alignment = Ui.ContentLayout.Horizontal.Center, Ui.Text { font = { "bold.ttf", 40 }, text = "loc@UI_TEXT_PUBLISH_MOD", }, Ui.Frame { w = 335, h = 335, margin = { top = 35 }, Ui.Rect { resizeToParent = true, color = { 1, 1, 1, 0.05 }, }, Ui.Image { id = "previewImage", visible = false, vAlign = Ui.ContentLayout.Vertical.Middle, hAlign = Ui.ContentLayout.Horizontal.Center, onPreDraw = function(self) local iw, ih = UiGetImageSize(self.path) self.w = 335 self.h = ih * self.w / iw end }, Ui.Text { vAlign = Ui.ContentLayout.Vertical.Middle, hAlign = Ui.ContentLayout.Horizontal.Center, id = "noPreviewNote", font = { "regular.ttf", 17 }, color = 0xB82B2A, text = "loc@UI_TEXT_NO_PREVIEW", onPreDraw = function(self) ModManager.publishErrorText = "loc@FAILMESSAGE_PREVIEW_IMAGE" end } }, Ui.HorizontalLayout { id = "modDetailsView", ignoreNavigation = true, margin = { top = 40 }, alignment = Ui.ContentLayout.Vertical.Bottom, Ui.Frame { w = 380, h = 310, Ui.VerticalLayout { ModManager.ValidatedText { id = "modName", font = { "bold.ttf", 27 }, text = "MOD TITLE", errorText = "loc@UI_TEXT_NAME_NOT", wrapWidth = 305 }, Ui.Text { id = "modSteamId", font = { "regular.ttf", 17 }, text = "0", color = 0xFFFFFFCC, wrapWidth = 305, visible = false }, ModManager.ValidatedText { id = "modAuthor", font = { "regular.ttf", 17 }, text = "By someone", errorText = "loc@UI_TEXT_AUTHOR_NOT", color = 0xFFFFFFCC, wrapWidth = 305 }, Ui.Text { id = "modTags", font = { "regular.ttf", 17 }, text = "Some tags", color = 0xFFFFFFCC, wrapWidth = 305 }, Ui.ScrollableArea { w = 310, h = 170, margin = { top = 10 }, ModManager.ValidatedText { id = "modDesc", font = { "regular.ttf", 17 }, text = "Description", color = 0xFFFFFF99, wrapWidth = 305 } } }, Ui.Text { vAlign = Ui.ContentLayout.Vertical.Bottom, id = "errorNotification", text = "error", font = { "regular.ttf", 17 }, color = 0xCC2B2A, wrapWidth = 305, onPreDraw = function(self) self.text = ModManager.publishErrorText end }, ModManager.ProgressBar { id = "progressBar", vAlign = Ui.ContentLayout.Vertical.Bottom } }, Ui.Frame { w = 220, h = 310, Ui.VerticalLayout { vAlign = Ui.ContentLayout.Vertical.Bottom, hAlign = Ui.ContentLayout.Horizontal.Center, alignment = Ui.ContentLayout.Horizontal.Center, padding = 10, Ui.HorizontalLayout { id = "workshopBar", hAlign = Ui.ContentLayout.Horizontal.Left, alignment = Ui.ContentLayout.Vertical.Middle, padding = 10, Ui.Text { font = { "medium.ttf", 20 }, text = "loc@UI_TEXT_SELECT_WORKSHOP", }, Ui.Text { id = "selectWorkshop", font = { "medium.ttf", 20 }, text = ModManager.PublishWorkshop.toString(ModManager.PublishWorkshop.Steam), pV = ModManager.PublishWorkshop.Steam, color = 0xFFFF7FD0, onMouseEnter = function(self) self.color = 0xFFFF7F end, onMouseLeave = function(self) self.color = 0xFFFF7FD0 end, onPressed = function(self) if GetBool("mods.pros_enabled") then self.pV = self.pV + 1 end if (self.pV < 0) or (self.pV > ModManager.PublishWorkshop.Count - 1) then self.pV = 0 end self.text = ModManager.PublishWorkshop.toString(self.pV) SetInt("mods.publish.workshop", self.pV) end } }, Ui.HorizontalLayout { id = "visibilityBar", hAlign = Ui.ContentLayout.Horizontal.Left, alignment = Ui.ContentLayout.Vertical.Middle, padding = 10, Ui.Text { font = { "medium.ttf", 20 }, text = "loc@UI_TEXT_VISIBILITY", }, Ui.Text { id = "visibility", font = { "medium.ttf", 20 }, text = ModManager.PublishVisibility.toString(ModManager.PublishVisibility.Public), pV = ModManager.PublishVisibility.Public, color = 0xFFFF7FD0, onMouseEnter = function(self) self.color = 0xFFFF7F end, onMouseLeave = function(self) self.color = 0xFFFF7FD0 end, onPressed = function(self) self.pV = self.pV + 1 if (self.pV < 0) or (self.pV > ModManager.PublishVisibility.Count - 1) then self.pV = 0 end self.text = ModManager.PublishVisibility.toString(self.pV) SetInt("mods.publish.visibility", self.pV) end } }, Teardown.GenericButton { id = "publishBtn", overflowHandling = Teardown.Button.TextOverflow.Truncate, w = 210, h = 40, disabledTextColor = {1,1,1,1}, text = "loc@UI_BUTTON_PUBLISH" } } }, onCreate = function(self) self.modName = self:findById("modName") self.modSteamId = self:findById("modSteamId") self.modAuthor = self:findById("modAuthor") self.modTags = self:findById("modTags") self.modDesc = self:findById("modDesc") self.errorNote = self:findById("errorNotification") self.publishBtn = self:findById("publishBtn") self.visibility = self:findById("visibility") self.visibilityBar = self:findById("visibilityBar") self.visibility.pV = GetInt("mods.publish.visibility") end, onPreDraw = function(self) if not ModManager.selectedMod.detailedInfo then return end if not self.progressBar then self.progressBar = self.root.progressBar end local steamId = GetString("mods.publish.id") local mod = ModManager.selectedMod local info = mod.detailedInfo self.modName.defaultText = info.fullName self.modSteamId.visible = steamId ~= "" and steamId ~= "0" self.modSteamId.text = steamId self.modAuthor.defaultText = GetTranslatedStringByKey("UI_TEXT_BY") .. " " .. info.author self.modTags.visible = info.tags ~= "" self.modTags.text = GetTranslatedStringByKey("UI_TEXT_TAGS") .. " " .. info.tags self.modDesc.defaultText = info.desc local state = GetString("mods.publish.state") local canPublish = (state == "ready" or state == "failed") and not ModManager.publishError local update = steamId ~= "" and steamId ~= "0" self.visibilityBar.visible = false if state == "uploading" then self.publishBtn.text = "loc@UI_BUTTON_CANCEL" self.publishBtn.onPressed = function(self) Command("mods.publishcancel") end self.progressBar.visible = true local progress = math.clamp(GetFloat("mods.publish.progress"), 0.1, 0.9) self.progressBar:updateProgress(progress) self.errorNote.visible = false else self.progressBar.visible = false self.errorNote.visible = true if state == "done" then self.publishBtn.text = "loc@UI_BUTTON_DONE" self.publishBtn.onPressed = function(self) self.root:hide() end else self.visibilityBar.visible = true if not canPublish then self.visibilityBar.colorFilter = {1,1,1,0.3} self.publishBtn.disabled = true else self.visibilityBar.colorFilter = {1,1,1,1} self.publishBtn.disabled = false end self.publishBtn.text = update and "loc@CAPTION_PUBLISH_UPDATE" or "loc@CAPTION_PUBLISH" self.publishBtn.onPressed = function(self) Command("mods.publishupload") end end end end }, } }, onCreate = function(self) self.previeImage = self:findById("previewImage") self.noPreveiweNote = self:findById("noPreviewNote") self.progressBar = self:findById("progressBar") end, onShow = function(self) Command("mods.publishbegin", ModManager.selectedMod.id) local modKey = "mods.available."..ModManager.selectedMod.id local previewPath = "RAW:"..GetString(modKey .. ".path").."/preview.jpg" local hasPreview = UiHasImage(previewPath) self.previeImage.visible = hasPreview self.previeImage.path = previewPath self.noPreveiweNote.visible = not hasPreview end, canHide = function(self) return GetString("mods.publish.state") ~= "uploading" end, onBeginClosing = function(self) Command("mods.publishend") end, onPreDraw = function(self) local canvas = UiCanvasSize() self.x = canvas.w / 2 - self.w / 2 self.y = canvas.h / 2 - self.h / 2 ModManager.publishErrorText = GetString("mods.publish.message") end, onPostDraw = function(self) ModManager.publishError = ModManager.publishErrorText ~= "" end }