#include "ui/ui_extensions.lua" #include "ui/ui_helpers.lua" #include "common.lua" --Spawn state spawnUi = false spawnUiScale = 0 spawnPlacement = false spawnEntities = nil spawnOffset = nil spawnRadius = 0 spawnDist = 3.0 spawnRot = 0 spawnPreviousTool = "" spawnFile = "" gIsSpawnUiHidden = true --Currently selected source and type gSelectedSource = "" gSelectedType = "" -- Cached selected items gSourceId = 0 gTypeId = 0 gItemId = 0 --Raw list of spawnable items from all mods gSpawnList = {} -- name, type, path, mod --Filtered, sorted lists to display in UI gSources = {} -- name, mod, category, enabled, visible gTypes = {} -- name, visible gItems = {} -- name, type, mod, path, visible --Internal state gFilterText = "" gNeedRefresh = true gSelectVisible = false gFocusText = false gScrollToVisibleItem = false gSetShortcut = false gRefreshDelay = 0.0 gHoverType = "" gHoverId = "" gHoverIdLast = "" gCurrentSourceInFocus = nil local needUpdateSelectedIdx = false local backGround = { w = 12, h = 560, draw = function(self) UiPush() UiColor(1,1,1, 0.2) UiTranslate(0, 0) UiImageBox("ui/common/box-solid-6.png", self.w, self.h, 6, 6) UiPop() end, } local sourceThumb = { w = 8, h = 100, draw = function(self) UiPush() UiColor(1,1,1) UiTranslate(2, 2) UiImageBox("ui/common/box-solid-4.png", self.w, self.h, 4, 4) UiPop() end, } local typeThumb = { w = 8, h = 100, draw = function(self) UiPush() UiColor(1,1,1) UiTranslate(2, 2) UiImageBox("ui/common/box-solid-4.png", self.w, self.h, 4, 4) UiPop() end, } local itemThumb = { w = 8, h = 100, draw = function(self) UiPush() UiColor(1,1,1) UiTranslate(2, 2) UiImageBox("ui/common/box-solid-4.png", self.w, self.h, 4, 4) UiPop() end, } SpawnScreen = {} SpawnScreen.initialized = false SpawnScreen.init = function() if SpawnScreen.initialized then return end RegisterListenerTo("LanguageChanged", "onLanguageChanged") sourceScrollbar = UiCreateScrollBar(sourceThumb, backGround) sourceScrollbar.holdFocusOnListEnd = true sourceScrollbar.preserveState = true sourceScrollbar.lerpSpeed = 15 typeScrollbar = UiCreateScrollBar(typeThumb, backGround) typeScrollbar.holdFocusOnListEnd = true typeScrollbar.preserveState = true typeScrollbar.lerpSpeed = 15 itemScrollbar = UiCreateScrollBar(itemThumb, backGround) itemScrollbar.holdFocusOnListEnd = true itemScrollbar.preserveState = true itemScrollbar.lerpSpeed = 15 SpawnScreen.initialized = true end function onLanguageChanged() needUpdateSelectedIdx = true end SpawnScreen.gSourceHover = 0 SpawnScreen.gTypeHover = 0 SpawnScreen.gItemHover = 0 SpawnScreen.gSourceClick = 0 SpawnScreen.gTypeClick = 0 SpawnScreen.gItemClick = 0 SpawnScreen.gSourceTextPos = {pos=0} SpawnScreen.gTypeTextPos = {pos=0} SpawnScreen.gItemTextPos = {pos=0} SpawnScreen.catPos = 0 SpawnScreen.gScrollSpeed = 60 ControlHint = {} ControlHint.linePadding = 0 ControlHint.iconSize = 42 ControlHint.boxWidth = 0 ControlHint.boxHeight = 0 ControlHint.boxPadding = 15 ControlHint.icoBoxWidth = 0 ControlHint.labelBoxWidth = 0 ControlHint.boxIndent = 20 ControlHint.draw = function(pcHints, gamepadHints) UiPush() UiTranslateToScreenRightEdge(-(ControlHint.boxWidth + 30), 0) UiTranslateToScreenBottomEdge(0, -(ControlHint.boxHeight + 30)) UiColor(0, 0, 0, 0.5) UiImageBox("ui/common/box-solid-10.png", ControlHint.boxWidth, ControlHint.boxHeight, 10, 10) UiColor(1, 1, 1, 1) UiColorFilter(1, 1, 1, 1) local isGamepad = LastInputDevice() == UI_DEVICE_GAMEPAD local th = 0 if isGamepad then th = ControlHint.iconSize * 0.8 else _, th = GetTextSizeWithFont(pcHints[1].ico, "regular.ttf", 27) end UiTranslate(ControlHint.boxPadding + ControlHint.icoBoxWidth, ControlHint.boxPadding + th / 2) UiPush() UiFont("regular.ttf", 30) UiAlign("right middle") if not isGamepad then UiBeginFrame() for i=1,#pcHints do UiText(pcHints[i].ico) UiTranslate(0, ControlHint.linePadding + ControlHint.iconSize) end UiTranslate(0, -(ControlHint.linePadding + ControlHint.iconSize)) UiEndFrame() ControlHint.icoBoxWidth, ControlHint.boxHeight = UiGetItemSize() else UiBeginFrame() for i=1,#gamepadHints do UiText(gamepadHints[i].ico) UiTranslate(0, ControlHint.linePadding + ControlHint.iconSize) end UiTranslate(0, -(ControlHint.linePadding + ControlHint.iconSize)) UiEndFrame() ControlHint.icoBoxWidth, ControlHint.boxHeight = UiGetItemSize() end UiPop() UiPush() UiTranslate(ControlHint.boxIndent, 0) UiFont("regular.ttf", 27) UiAlign("left middle") UiColorFilter(1, 1, 1, 0.7) if not isGamepad then UiBeginFrame() for i=1,#pcHints do UiText(pcHints[i].label) UiTranslate(0, ControlHint.linePadding + ControlHint.iconSize) end UiTranslate(0, -(ControlHint.linePadding + ControlHint.iconSize)) UiEndFrame() else UiBeginFrame() for i=1,#gamepadHints do UiText(gamepadHints[i].label) UiTranslate(0, ControlHint.linePadding + ControlHint.iconSize) end UiTranslate(0, -(ControlHint.linePadding + ControlHint.iconSize)) UiEndFrame() end ControlHint.labelBoxWidth, _ = UiGetItemSize() UiPop() UiPop() ControlHint.boxWidth = ControlHint.icoBoxWidth + ControlHint.labelBoxWidth + 2 * ControlHint.boxPadding + ControlHint.boxIndent ControlHint.boxHeight = ControlHint.boxHeight + 2 * ControlHint.boxPadding end function sourceFocuseListener(id) SpawnScreen.gSourceHover = id SpawnScreen.gTypeHover = 0 SpawnScreen.gItemHover = 0 end function typeFocusListener(id) SpawnScreen.gTypeHover = id SpawnScreen.gSourceHover = 0 SpawnScreen.gItemHover = 0 end function itemFocusListener(id) SpawnScreen.gItemHover = id SpawnScreen.gSourceHover = 0 SpawnScreen.gTypeHover = 0 end function sourceClickListener(id) --typeScrollbar:reset() typeScrollbar:scrollToItem(1) itemScrollbar:scrollToItem(1) SpawnScreen.gSourceClick = id SpawnScreen.gTypeClick = 0 end function typeClickListener(id) --itemScrollbar:reset() itemScrollbar:scrollToItem(1) SpawnScreen.gTypeClick = id end function itemClickListener(id) SpawnScreen.gItemClick = id end function trim(s) local n = string.find(s,"%S") return n and string.match(s, ".*%S", n) or "" end function init() SpawnScreen.init() end function isSourceValid(mod) if gSelectedSource ~= "" then if gSelectedSource == "all" then return not GetBool("savegame.spawn.disabled." .. mod) elseif gSelectedSource == "builtin" or gSelectedSource == "steam" or gSelectedSource == "local" or gSelectedSource == "dlc" or gSelectedSource == "other" then if not startsWith(mod, gSelectedSource) then return false end else if mod ~= gSelectedSource then return false end end end return not GetBool("savegame.spawn.disabled." .. mod) end function isTypeValid(type) return gSelectedType == type end function matchName(name) if gFilterText == "" then return true else return string.find(string.lower(name), string.lower(gFilterText)) end end function refresh() --Spawnables gSpawnList = {} local mods = ListKeys("spawn") local types = {} for m=1, #mods do local mod = mods[m] if HasKey("mods.available." .. mod) then local ids = ListKeys("spawn." .. mod) for i=1, #ids do local tmp = "spawn." .. mod .. "." .. ids[i] local n = GetString(tmp) local p = GetString(tmp .. ".path") local t = GetTranslatedStringByKey("T_OTHER") local s = string.find(n, "/", 1, true) if s and s > 1 then t = string.sub(n, 1, s-1) n = string.sub(n, s+1, string.len(n)) end if n == "" then n = GetTranslatedStringByKey("N_UNNAMED") end t = trim(t) local found = false for j=1, #types do if string.lower(types[j]) == string.lower(t) then t = types[j] found = true break end end if not found then types[#types+1] = t end local item = {} item.name = GetTranslatedStringByKey(n, n) item.type = GetTranslatedStringByKey(t, t) item.path = p item.mod = mod gSpawnList[#gSpawnList+1] = item end end end local ids = ListKeys("spawn.creativemode") for i=1, #ids do local tmp = "spawn.creativemode." .. ids[i] local n = GetString(tmp) local p = GetString(tmp .. ".path") local t = GetTranslatedStringByKey("UI_TEXT_CREATIVE_MODE") local found = false for j=1, #types do if string.lower(types[j]) == string.lower(t) then t = types[j] found = true break end end if not found then types[#types+1] = t end local item = {} item.name = n item.type = t item.path = p item.mod = "other-creativemode" gSpawnList[#gSpawnList+1] = item end --Sources gSources = {} for i=1, #gSpawnList do local index = 0 local mod = gSpawnList[i].mod for j=1, #gSources do if gSources[j].mod == mod then index = j break end end if index == 0 then local source = {} if mod == "other-creativemode" then source.name = GetTranslatedStringByKey("UI_TEXT_CREATIVE_MODE") source.mod = mod source.category = "other" else source.name = GetString("mods.available."..mod..".listname") source.mod = mod source.category = "" if startsWith(mod, "builtin") then source.category = "builtin" elseif startsWith(mod, "steam") then source.category = "steam" elseif startsWith(mod, "dlc") then source.category = "dlc" else source.category = "local" end end source.enabled = not GetBool("savegame.spawn.disabled."..mod) source.visible = false gSources[#gSources+1] = source index = #gSources end if gSources[index].visible==false and gSources[index].enabled then if matchName(gSpawnList[i].name) or matchName(gSpawnList[i].type) or matchName(gSources[index].name) then gSources[index].visible = true end end end table.sort(gSources, function(a,b) return a.name < b.name end) --This happens when altering the search field if gSelectVisible then local alreadyVisible = false for i=1, #gSources do if gSources[i].mod == gSelectedSource and gSources[i].visible then alreadyVisible = true break end end if not alreadyVisible then gSelectedSource = "all" end end --Types gTypes = {} for i=1, #gSpawnList do if isSourceValid(gSpawnList[i].mod) and gSelectedSource ~="" then local name = gSpawnList[i].type local index = 0 for j=1, #gTypes do if gTypes[j].name == name then index = j break end end if index == 0 then local typ = {} typ.name = name typ.visible = false gTypes[#gTypes+1] = typ index = #gTypes end if not gTypes[index].visible then if matchName(gSpawnList[i].name) or matchName(gTypes[index].name) then gTypes[index].visible = true end end end end table.sort(gTypes, function(a,b) return a.name < b.name end) if gSelectVisible then local alreadyVisible = false for i=1, #gTypes do if gTypes[i].name == gSelectedType and gTypes[i].visible then alreadyVisible = true break end end --If current selected is not visible, select first visible if not alreadyVisible then for i=1, #gTypes do if gTypes[i].visible then gSelectedType = gTypes[i].name itemScrollbar:reset() SpawnScreen.gTypeClick = i break end end end end --Items gItems = {} for i=1, #gSpawnList do if isSourceValid(gSpawnList[i].mod) and isTypeValid(gSpawnList[i].type) then local item = {} item.name = gSpawnList[i].name item.type = gSpawnList[i].type item.mod = gSpawnList[i].mod item.path = gSpawnList[i].path item.visible = matchName(gSpawnList[i].name) gItems[#gItems+1] = item end end table.sort(gItems, function(a,b) return a.name < b.name end) gScrollToVisibleItem = true gSelectVisible = false end function getRotation() local fwd = TransformToParentVec(GetPlayerCameraTransform(), Vec(0, 0, -1)) fwd[2] = 0 fwd = VecNormalize(fwd) local angle = math.atan2(-fwd[3], fwd[1]) * 180.0 / math.pi return QuatEuler(0, angle + spawnRot, 0) end function update() if spawnPlacement then local t = GetPlayerCameraTransform() t.pos = VecAdd(t.pos, TransformToParentVec(t, Vec(0, 0, -spawnDist))) t.rot = getRotation() for i=1, #spawnEntities do local e = spawnEntities[i] if GetEntityType(e) == "body" and spawnOffset[i] then local wt = TransformToParentTransform(t, spawnOffset[i]) local q = 0.25 local bt = GetBodyTransform(e) local it = Transform(VecLerp(bt.pos, wt.pos, q), QuatSlerp(bt.rot, wt.rot, q)) SetBodyTransform(e, it) SetBodyVelocity(e, Vec(0,0,0)) SetBodyAngularVelocity(e, Vec(0,0,0)) end end end end function tick(dt) if spawnPlacement then SetBool("game.input.locktool", true) SetBool("game.disablepause", true) SetBool("game.disableinteract", true) SetBool("game.disablemap", true) spawnDist = clamp(spawnDist + InputValue("mousewheel"), 2, 8+spawnRadius) local touchShape = 0 for i=1, #spawnEntities do local e = spawnEntities[i] if GetEntityType(e) == "shape" then local overlap = QueryAabbShapes(GetShapeBounds(e)) for j=1, #overlap do local o = overlap[j] local valid = true for k=1,#spawnEntities do if o == spawnEntities[k] then valid = false break end end if valid then if IsShapeTouching(e, o) then touchShape = o break end end end end end if touchShape ~= 0 then for i=1, #spawnEntities do local e = spawnEntities[i] if GetEntityType(e) == "shape" then DrawShapeOutline(e, 1, 0, 0, 1) end end end if InputPressed("usetool") then SetBool("game.player.disableinput", true) for i=1, #spawnEntities do local e = spawnEntities[i] if GetEntityType(e) == "shape" then SetShapeCollisionFilter(e, 1, 255) if touchShape ~= 0 then if HasTag(e, "creative") then local wt = GetShapeWorldTransform(e) local newBody = GetShapeBody(touchShape) SetShapeBody(e, newBody) SetShapeLocalTransform(e, TransformToLocalTransform(GetBodyTransform(newBody), wt)) end end end end spawnPlacement = false SetBool("game.ui.hidemods", false) if InputDown("shift") then local oldDist = spawnDist local oldRot = spawnRot spawn(spawnFile) spawnDist = oldDist spawnRot = oldRot end end if InputDown("grab") then SetBool("game.player.disableinput", true) spawnRot = spawnRot + InputValue("camerax") * 50 end if InputPressed("pause") then spawnAbort() end else if spawnPreviousTool ~= "" then SetString("game.player.tool", spawnPreviousTool) spawnPreviousTool = "" end end if not spawnUi and spawnEnabled() then if PauseMenuButton("loc@SPAWN") then spawnUi = true gFocusText = false gSetShortcut = false gNeedRefresh = true end end if gRefreshDelay > 0 then gRefreshDelay = gRefreshDelay - dt elseif gNeedRefresh then refresh() gNeedRefresh = false end end function spawnAbort() for i=1, #spawnEntities do Delete(spawnEntities[i]) end spawnPlacement = false SetBool("game.ui.hidemods", false) end function spawnEnabled() return GetBool("game.player.interactive") and (GetBool("level.spawn") or GetBool("options.game.spawn")) end function makeListItems(items, selected, itemTextPos, focusCallback, clickCallback) local listItems = {} itemTextPos = itemTextPos or {pos=0} for i=1, #items do local item = items[i] UiFont("regular.ttf", 26) listItems[#listItems+1] = { id = i, w = 332, h = UiFontHeight() + 4, draw = function(self) local itemId = UiNavComponent(self.w, self.h) local isFocused = UiIsMouseInRect(self.w, self.h) or UiIsComponentInFocus(itemId) local isClicked = isFocused and (InputPressed("menu_accept") or InputPressed("lmb")) UiColor(1,1,1) if item.hide then UiColor(1,1,1,0.3) end if item.bold then UiFont("bold.ttf", 26) end if self.id == selected then UiColor(1,1,0) end if isFocused then UiPush() UiColor(0.85, 0.85, 0.85, 0.12) UiImageBox("ui/common/box-solid-4.png", self.w, self.h, 4, 4) UiPop() UiColor(1,1,0) focusCallback(self.id) end if isClicked and not item.disable then UiSound("spawn/select.ogg") clickCallback(self.id) end UiPush() UiAlign("middle") UiTranslate(12, self.h/2) local tw, _ = GetTextSize(item.label) local deltaWidth = tw - self.w + 12 if deltaWidth > 0 and isFocused then UiTranslate(-itemTextPos.pos, 0) if itemTextPos.pos < deltaWidth then itemTextPos.pos = itemTextPos.pos + SpawnScreen.gScrollSpeed * GetTimeStep() end end local w,_ = UiText(item.label) if item.disable then UiPush() UiColorFilter(1,1,1,0.75) UiRect(w+8, 1) UiPop() end UiPop() end } end return listItems end function drawListBackground(w, h, title) UiPush() UiPush() UiColor(1,1,1) UiFont("regular.ttf", 26) UiTranslate(0, -UiFontHeight()-5) UiText(title) UiPop() UiPush() UiColor(1,1,1,0.1) UiTranslate(-3, -3) UiImageBox("ui/common/box-solid-6.png", w, h, 6, 6) UiPop() UiPop() end function getSource(mod) for i=1, #gSources do if gSources[i].mod == mod then return gSources[i] end end return nil end function getItem(itemId) for i=1, #gItems do if gItems[i].path == itemId then return gItems[i] end end return nil end function commaSeparatedList(list) local str = "" for i=1, #list do if i > 1 then str = str .. ", " end str = str .. list[i] end return str end function drawSpawnUi(scale) if scale < 1 then UiIgnoreNavigation() UiDisableInput() end local isGamepad = LastInputDevice() == UI_DEVICE_GAMEPAD UiPush() gHoverType = "" local file = "" local w = 1190 local h = 1000 UiBlur(scale) UiTranslate(UiCenter(), UiMiddle()) UiScale(scale) UiColorFilter(1,1,1,scale) UiAlign("center middle") UiWindow(w, h) UiAlign("top left") UiColor(0,0,0,0.8) UiImageBox("ui/common/box-solid-shadow-50.png", w, h, -50, -50) if (GetBool("game.cursor.enabled") and InputPressed("lmb") and not UiIsMouseInRect(w, h)) then SetBool("game.player.disableinput", true) spawnUi = false end UiColor(1,1,1) UiPush() UiFont("bold.ttf", 36) UiAlign("center middle") UiTranslate(UiCenter(), 35) UiText("loc@UI_TEXT_SPAWN") UiPop() UiTranslate(0, 70) UiPush() local tw = 500 local th = 42 UiTranslate(w/2-tw/2, 0) UiColor(1,1,1, 0.8) UiImageBox("ui/common/box-outline-4.png", tw, th, 12, 12) UiColor(1,1,1) UiFont("regular.ttf", 26) local newText = UiTextInput(gFilterText, tw, th, gFocusText) -- prevent list focus interception if gFocusText then sourceScrollbar.forceIdx = 0 typeScrollbar.forceIdx = 0 itemScrollbar.forceIdx = 0 end gFocusText = false if string.len(newText) > 20 then newText = string.sub(newText, 1, 20) end if gFilterText == "" then UiColor(1,1,1,0.5) UiTranslate(10, 12) UiText("loc@UI_TEXT_SEARCH") else UiTranslate(tw-32, 10) UiColor(1,1,1) if not isGamepad then if UiImageButton("ui/common/close.png") then newText = "" gFocusText = true end end end if newText ~= gFilterText then gFilterText = newText gNeedRefresh = true gRefreshDelay = 0.5 gSelectVisible = true typeScrollbar:reset() SpawnScreen.gSourceClick = 1 end UiPop() UiTranslate(22, 100) local listw = 366 local listh = 600 UiPush() UiFont("bold.ttf", 26) local listNames, listValues, selected, newSelected, scrollToIndex local hover = 0 -------------------------------------------------------------------------------------- -- SOURCE -------------------------------------------------------------------------------------- listNames = {} listValues = {} selected = 0 listNames[#listNames+1] = {label=GetTranslatedStringByKey("UI_SPAWN_ALL"), bold=true} listValues[#listValues+1] = "all" if gSelectedSource == "all" then selected = 1 end local hidedCategory = 0 --TODO: fix this after mod will come back in console version if gSelectedSource == "" then SpawnScreen.gSourceClick = 1 end local names = {} local values = {} if IsRunningOnPC() or IsRunningOnMac() then names = {GetTranslatedStringByKey("CATNAME_BUILT-IN_MODS"), GetTranslatedStringByKey("CATNAME_SUBSCRIBED_MODS"), GetTranslatedStringByKey("CATNAME_LOCAL_MODS"), GetTranslatedStringByKey("CATNAME_DLC_MODS"), GetTranslatedStringByKey("T_OTHER")} values = {"builtin", "steam", "local", "dlc", "other"} else names = {GetTranslatedStringByKey("CATNAME_BUILT-IN_MODS"), GetTranslatedStringByKey("CATNAME_DLC_MODS"), GetTranslatedStringByKey("T_OTHER")} values = {"builtin", "dlc", "other"} end for t=1, #names do local catName = names[t] local catValue = values[t] listNames[#listNames+1] = {label=catName, bold=true} listValues[#listValues+1] = catValue local catIndex = #listNames if gSelectedSource == catValue then selected = #listNames end local hasVisible = false for i=1,#gSources do if gSources[i].category == catValue and gSources[i].name ~= "" then listNames[#listNames+1] = {label=" "..gSources[i].name} listValues[#listValues+1] = gSources[i].mod if gSources[i].visible then hasVisible = true else local item = listNames[#listNames] item.hide = true end if not gSources[i].enabled then local item = listNames[#listNames] item.disable = true end if gSelectedSource == gSources[i].mod then selected = #listNames end end end if not hasVisible then local item = listNames[catIndex] item.hide = true hidedCategory = hidedCategory + 1 end end if hidedCategory == 4 then local item = listNames[1] item.hide = true end local sourceItemList = makeListItems(listNames, selected, SpawnScreen.gSourceTextPos, sourceFocuseListener, sourceClickListener) drawListBackground(listw, listh, GetTranslatedStringByKey("HOVER_SOURCE")) UiPush() UiTranslate(8, 8) UiList(listw-34, listh-12, sourceItemList, sourceScrollbar) UiPop() if needUpdateSelectedIdx then SpawnScreen.gSourceClick = selected needUpdateSelectedIdx = false end hover = SpawnScreen.gSourceHover newSelected = SpawnScreen.gSourceClick gCurrentSourceInFocus = listValues[hover] gSourceId = SpawnScreen.gSourceClick if newSelected ~= selected then if newSelected ~= 0 then gSelectedSource = listValues[newSelected] else gSelectedSource = "" end gNeedRefresh = true end if hover ~= 0 then gHoverType = "source" gHoverId = listValues[hover] if InputPressed("rmb") or InputPressed("interact") then local key = "savegame.spawn.disabled." .. gHoverId local enabled = GetBool(key) if enabled then ClearKey(key) else SetBool(key, true) if newSelected == hover then SpawnScreen.gSourceClick = 0 end end typeScrollbar:reset() gNeedRefresh = true end end UiTranslate(listw+28, 0) -------------------------------------------------------------------------------------- -- TYPE -------------------------------------------------------------------------------------- listNames = {} listValues = {} selected = 0 for i=1,#gTypes do listNames[i] = {label=gTypes[i].name} listValues[i] = gTypes[i].name if not gTypes[i].visible then local item = listNames[i] item.hide = true end if gTypes[i].name == gSelectedType then selected = i end end local typeItemList = makeListItems(listNames, selected, SpawnScreen.gTypeTextPos, typeFocusListener, typeClickListener) drawListBackground(listw, listh, GetTranslatedStringByKey("HOVER_CATEGORY")) UiPush() UiTranslate(8, 8) UiList(listw-34, listh-12, typeItemList, typeScrollbar) UiPop() hover = SpawnScreen.gTypeHover newSelected = SpawnScreen.gTypeClick gTypeId = SpawnScreen.gTypeClick if newSelected ~= selected then if newSelected ~= 0 then gSelectedType = listValues[newSelected] else gSelectedType = "" end gNeedRefresh = true end if hover ~= 0 then gHoverType = "type" gHoverId = listValues[hover] end UiTranslate(listw+28, 0) -------------------------------------------------------------------------------------- -- ITEM -------------------------------------------------------------------------------------- listNames = {} listValues = {} selected = 0 scrollToIndex = 0 for i=1,#gItems do listNames[i] = {label=gItems[i].name} listValues[i] = gItems[i].path if gItems[i].visible then if gScrollToVisibleItem and scrollToIndex==0 then gScrollToVisibleItem = false scrollToIndex = i itemScrollbar:scrollToItem(scrollToIndex, true) end else local item = listNames[i] item.hide = true end end local itemList = makeListItems(listNames, selected, SpawnScreen.gItemTextPos, itemFocusListener, itemClickListener) drawListBackground(listw, listh, GetTranslatedStringByKey("HOVER_ITEM")) UiPush() UiTranslate(8, 8) UiList(listw-34, listh-12, itemList, itemScrollbar) UiPop() hover = SpawnScreen.gItemHover selected = SpawnScreen.gItemClick if selected ~= 0 then file = listValues[selected] SpawnScreen.gItemClick = 0 end if hover ~= 0 then gHoverType = "item" gHoverId = listValues[hover] gItemId = hover end if InputPressed("interact") and gHoverId ~= 0 and gHoverType == "item" then local item = getItem(gHoverId) if item ~= nil and item.mod == "other-creativemode" then DeleteShape(string.sub(gHoverId, 10, -5)) end end UiTranslate(250, 0) UiPop() UiTranslate(0, listh+16) -------------------------------------------------------------------------------------- -- HOVER INFO -------------------------------------------------------------------------------------- UiPush() local hoverh = 162 if isGamepad then hoverh = 138 end UiAlign("left") UiColor(1,1,1,0.1) UiTranslate(-3, 0) UiImageBox("ui/common/box-solid-10.png", w-36, hoverh, 10, 10) UiWindow(w-36, hoverh, true) if gSetShortcut then UiTranslate(20, 35) UiFont("regular.ttf", 26) UiColor(0.9,0.9,0.9) UiText(GetTranslatedStringByKey("UI_TEXT_PRESS_DESIRED"), true) elseif gHoverType == "item" then if gHoverIdLast ~= gHoverId then gHoverIdLast = gHoverId SpawnScreen.gItemTextPos.pos = 0 end local name = "" local src = "" local typ = "" for i=1, #gItems do if gItems[i].path == gHoverId then name = gItems[i].name typ = gItems[i].type local mod = gItems[i].mod if mod == "other-creativemode" then src = GetTranslatedStringByKey("UI_TEXT_CREATIVE_MODE") else src = GetString("mods.available."..mod..".listname") if string.sub(mod, 1, 7)=="builtin" then src = src .. " " .. GetTranslatedStringByKey("TEXT_BUILT-IN") end if string.sub(mod, 1, 7)=="steam" then src = src .. " " .. GetTranslatedStringByKey("TEXT_SUBSCRIBED") end if string.sub(mod, 1, 4)=="dlc" then src = src .. " " .. GetTranslatedStringByKey("DLC") end if string.sub(mod, 1, 5)=="local" then src = src .. " " .. GetTranslatedStringByKey("TEXT_LOCAL_FILES") end end break end end UiTranslate(10, 40) UiFont("regular.ttf", 36) UiColor(0.9,0.9,0.9) UiText(name) UiTranslate(0, 40) UiColor(0.8,0.8,0.8) UiFont("regular.ttf", 26) UiText(GetTranslatedStringByKey("UI_TEXT_MOD") .. " " .. src, true) UiTranslate(0, 18) UiText(GetTranslatedStringByKey("UI_TEXT_CATEGORY") .. " " .. typ, true) elseif gHoverType == "type" then if gHoverIdLast ~= gHoverId then gHoverIdLast = gHoverId SpawnScreen.gTypeTextPos.pos = 0 end local name = gHoverId local count = 0 local sources = {} for i=1, #gSpawnList do local mod = gSpawnList[i].mod local type = gSpawnList[i].type if isSourceValid(mod) and type == gHoverId then count = count + 1 local src = GetString("mods.available."..mod..".listname") local found = false for j=1, #sources do if sources[j] == src then found = true break end end if not found then sources[#sources+1] = src end end end UiTranslate(10, 40) UiFont("regular.ttf", 36) UiColor(0.9,0.9,0.9) UiText(name) UiTranslate(0, 40) UiColor(0.8,0.8,0.8) UiFont("regular.ttf", 22) UiText(GetTranslatedStringByKey("UI_TEXT_SOURCES") .. " " .. commaSeparatedList(sources), true) UiTranslate(0, 10) UiText(GetTranslatedStringByKey("UI_TEXT_ITEMS") .. " " .. count, true) elseif gHoverType == "source" then if gHoverIdLast ~= gHoverId then gHoverIdLast = gHoverId SpawnScreen.catPos = 0 SpawnScreen.gSourceTextPos.pos = 0 end local source = getSource(gHoverId) if source then local name = source.name UiTranslate(10, 40) UiFont("regular.ttf", 36) UiColor(0.9,0.9,0.9) UiText(name) UiTranslate(0, 40) UiColor(0.8,0.8,0.8) UiFont("regular.ttf", 26) if source.enabled or isGamepad then local types = {} local count = 0 for i=1, #gSpawnList do if gSpawnList[i].mod == source.mod then local typ = gSpawnList[i].type if typ ~= "" then local typeFound = false for j=1, #types do if types[j] == typ then typeFound = true break end end if not typeFound then types[#types+1] = typ end count = count + 1 end end end local margin = 18 if LastInputDevice() == UI_DEVICE_MOUSE then margin = 4 UiColor(0.8, 0.8, 0.8, 0.7) UiText(GetTranslatedStringByKey("UI_TEXT_INCLUDED"), true) UiTranslate(0, margin) end UiColor(1, 1, 1, 0.8) UiPush() local catText = GetTranslatedStringByKey("UI_TEXT_CATEGORIES") tw, th = GetTextSize(catText) local categoryText = commaSeparatedList(types) local catTextWidth, catTextHeight = GetTextSize(categoryText) local windowWidth = w-tw-60 local catDeltaWidth = catTextWidth - windowWidth + 4 UiText(catText) UiTranslate(tw+4, -catTextHeight) UiWindow(windowWidth, 32, true) UiColor(1,1,1) if catDeltaWidth > 0 then UiTranslate(-SpawnScreen.catPos, catTextHeight) if SpawnScreen.catPos < catDeltaWidth then SpawnScreen.catPos = SpawnScreen.catPos + SpawnScreen.gScrollSpeed * GetTimeStep() end else UiTranslate(0, catTextHeight) end UiText(categoryText, true) UiPop() UiTranslate(0, margin+th) UiText(GetTranslatedStringByKey("UI_TEXT_ITEMS") .. " " .. count, true) else UiColor(0.8, 0.8, 0.8, 0.7) UiText(GetTranslatedStringByKey("UI_TEXT_EXCLUDED"), true) end end end UiPop() if isGamepad then UiPush() UiTranslate(w / 2, hoverh + 50) UiColor(1,1,1) local hints = {} if gHoverType == "item" then table.insert(hints, { ico = "[[menu:menu_accept;iconsize=42,42]]", txt="loc@SPAWN"}) else table.insert(hints, { ico = "[[menu:menu_accept;iconsize=42,42]]", txt="loc@UI_SPAWN_CHOOSE"}) end if gHoverType == "source" then local source = getSource(gHoverId) if source then if source.enabled then table.insert(hints, { ico = "[[menu:interact;iconsize=42,42]]", txt = "loc@UI_SPAWN_EXCLUDE"}) else table.insert(hints, { ico = "[[menu:interact;iconsize=42,42]]", txt = "loc@UI_SPAWN_INCLUDE"}) end end elseif gHoverType == "item" then local item = getItem(gHoverId) if item ~= nil and item.mod == "other-creativemode" then table.insert(hints, { ico = "[[menu:interact;iconsize=42,42]]", txt = "loc@UI_CREATIVE_DELETE"}) end end table.insert(hints, { ico = "[[menu:mmb;iconsize=42,42]]", txt = "loc@UI_TEXT_SEARCH"}) table.insert(hints, { ico = "[[menu:menu_cancel;iconsize=42,42]]", txt = "loc@UI_BUTTON_EXIT"}) UiDrawHintsCentered(hints) UiPop() else UiPush() UiTranslate(0, hoverh+40) UiFont("regular.ttf", 27) local shortcutText = GetTranslatedStringByKey("UI_TEXT_SPAWN_SHORTCUT") tw,th = UiGetTextSize(shortcutText) local caption = GetString("savegame.spawn.shortcut") if caption ~= "" then caption = string.upper(caption) else caption = " "..GetTranslatedStringByKey("UI_SPAWN_EMPTY") end if gSetShortcut then if InputPressed("pause") then SetString("savegame.spawn.shortcut", "") gSetShortcut = false else local str = InputLastPressedKey() if str ~= "" and str ~= "tab" and str~= "esc" and tonumber(str) == nil then SetString("savegame.spawn.shortcut", str) gSetShortcut = false end end caption = " "..GetTranslatedStringByKey("CAPTION_PRESS_KEY") end local bw,bh = UiGetTextSize(caption) bw = math.max(bw+20, 100) local lineSize = tw + bw UiTranslate((w - lineSize)/2, 0) UiColor(1,1,1) UiAlign("middle") UiText(shortcutText) UiTranslate(tw, -2) if GetString("savegame.spawn.shortcut") ~= "" and not gSetShortcut then UiColor(1,1,1,0.8) UiTranslate(4, 0) UiImageBox("ui/common/box-outline-6.png", 46, 46, 6, 6) UiTranslate(14, 0) else UiColor(1,1,1,0.3) end if gSetShortcut then UiColor(1,1,1) end if UiTextButton(caption, bw, bh) then gSetShortcut = true end UiPop() end -- restore state of lists when spawn ui is shown again if scale > 0 and gIsSpawnUiHidden then gIsSpawnUiHidden = false if gSourceId ~= 0 then sourceScrollbar:scrollToItem(gSourceId) end if gTypeId ~= 0 then typeScrollbar:scrollToItem(gTypeId) end if gItemId ~= 0 then itemScrollbar:scrollToItem(gItemId) end end return file end function spawn(file) if GetBool("game.lowmemory") then SetString("hud.notification", GetTranslatedStringByKey("UI_HUD_NOTE_LOW_MEMORY")) spawnUi = false return end UiSound("spawn/spawn.ogg") spawnRot = 0 local t = GetPlayerCameraTransform() t.pos = VecAdd(t.pos, TransformToParentVec(t, Vec(0, 0, -7))) t.rot = getRotation() local c = t spawnEntities = Spawn(file, t) local mi = Vec(10000, 10000, 10000) local ma = Vec(-10000, -10000, -10000) for i=1, #spawnEntities do local e = spawnEntities[i] if GetEntityType(e) == "shape" then local smi, sma = GetShapeBounds(e) for j=1, 3 do mi[j] = math.min(mi[j], smi[j]) ma[j] = math.max(ma[j], sma[j]) end end end local mid = VecLerp(mi, ma, 0.5) spawnRadius = VecLength(VecSub(ma, mid)) spawnDist = spawnRadius + 2.0 c.pos = mid spawnOffset = {} for i=1, #spawnEntities do local e = spawnEntities[i] local typ = GetEntityType(e) if typ == "body" then spawnOffset[i] = TransformToLocalTransform(c, GetBodyTransform(e)) end if typ == "shape" then SetShapeCollisionFilter(e, 0, 0) end end local currentTool = GetString("game.player.tool") if currentTool ~= "none" then spawnPreviousTool = currentTool SetString("game.player.tool", "none") end spawnPlacement = true spawnFile = file spawnUi = false end function draw() if InputPressed(GetString("savegame.spawn.shortcut")) then if spawnPlacement then spawnAbort() end if spawnEnabled() and not spawnUi then spawnUi = true gFocusText = false gSetShortcut = false gNeedRefresh = true end end if GetBool("game.refreshSpawnable") then SetBool("game.refreshSpawnable", false) gNeedRefresh = true end if spawnUi then UiMakeInteractive() -- for spawn menu we do now want to draw a cursor if LastInputDevice() == UI_DEVICE_GAMEPAD then UiSetCursorState(UI_CURSOR_HIDE_AND_LOCK) end SetBool("game.disablepause", true) SetBool("game.disablemap", true) SetBool("hud.disable", true) SetBool("game.ui.hidemods", true) if gSetShortcut==false and (InputPressed("pause") or InputPressed("menu_cancel")) then spawnUi = false SetBool("game.ui.hidemods", false) end end if spawnUi and spawnUiScale == 0 then -- show spawn window SetValue("spawnUiScale", 1, "easeout", 0.25) UiSound("spawn/open.ogg") end if not spawnUi and spawnUiScale == 1 then -- hide spawn window SetValue("spawnUiScale", 0, "easein", 0.1) UiSound("spawn/close.ogg") end if spawnUi and InputPressed("mmb") then gFocusText = true end if spawnUiScale > 0 then local file = drawSpawnUi(spawnUiScale) if file ~= "" then spawn(file) end else gIsSpawnUiHidden = true end if spawnPlacement then local pcHints = { {ico=string.upper(GetString("game.input.usetool")), label="loc@UI_TEXT_PLACE"}, {ico=string.upper(GetString("game.input.grab")), label="loc@UI_TEXT_ROTATE"}, {ico=GetTranslatedStringByKey("UI_TEXT_SCROLL"), label="loc@UI_TEXT_DISTANCE"}, {ico=string.upper(GetString("game.input.pause")), label="loc@UI_TEXT_ABORT"} } local gamepadHints = { {ico="[[player:usetool;iconsize=42,42]]", label="loc@UI_TEXT_PLACE"}, {ico="[[player:grab;iconsize=42,42]]+[[player:gamepad_right_stick_x;iconsize=42,42]]", label="loc@UI_TEXT_ROTATE"}, {ico="[[player:gamepad_dpad_updown;iconsize=42,42]]", label="loc@UI_TEXT_DISTANCE"}, {ico="[[player:pause;iconsize=42,42]]", label="loc@UI_TEXT_ABORT"} } if IsRunningOnSteam() then gamepadHints[2].ico = "[[player:grab;iconsize=42,42]]+[[player:gamepad_right_stick;iconsize=42,42]]" gamepadHints[3].ico = "[[player:gamepad_dpad_up;iconsize=42,42]], [[player:gamepad_dpad_down;iconsize=42,42]]" end ControlHint.draw(pcHints, gamepadHints) end end function handleCommand(cmd) if cmd == "quickload" then SpawnScreen.initialized = false SpawnScreen.init() end end