File size: 10,350 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
if FirstLoad then
LoadingScreenTipsRate = 10000
LoadingScreenLog = {}
end
LoadingScreenOrgSize = point(1920, 1080)
local lsprintf = CreatePrint{
--"loading screen",
format = "printf",
timestamp = true,
}
function LoadingScreenGetClassById(id)
if id == "idSaveProfile" then
return "BaseSavingScreen"
elseif id == "idAutosaveScreen" then
return "AutosaveScreen"
elseif id == "idQuickSaveScreen" then
return "QuickSaveScreen"
end
return "XLoadingScreen"
end
function GetLoadingScreenDialog(exceptAccountStorage)
local dlg = GetDialog(LoadingScreenLog[#LoadingScreenLog])
if exceptAccountStorage and dlg then
if not dlg.context or dlg.context.id ~= "idSaveProfile" then
return dlg
end
else
return dlg
end
end
local function LoadingScreenCreate(class, id, reason, info_text, metadata)
lsprintf("Creating: class = %s, id = %s, reason = %s", class, tostring(id), tostring(reason))
table.insert(LoadingScreenLog, class)
local dlg = OpenDialog(class, nil, {id = id,reason = reason, info_text = info_text, metadata = metadata}, reason)
--[[
-- the saving animation cannot be implemented using a transition effect
if dlg.saving and rawget(dlg, "idSavingAnim") then
dlg.idSavingAnim:CreateTransition{
keeprender = true,
fadeIn = 500,
fadeOut = 1000,
angle = 360, angleRevert = true, angleTime = 1000,
srcangle = 0,
time = 500000,
}
end
--]]
if dlg.game_blocking then
Pause(dlg)
LockCamera(dlg)
ChangeGameState("loading", true)
if info_text and rawget(dlg, "idInfoText") then
dlg.idInfoText:SetText(info_text)
end
local atTips = dlg.show_tips and (rawget(dlg, "idTips") or rawget(dlg, "idContainer") and rawget(dlg.idContainer, "idTips"))
if atTips and tips.InitTips() then
local last_tip_id = 0
local selected_tips = {}
for i = 1, 5 do
local tip, id
repeat
tip, id = tips.GetNextTip()
until id ~= last_tip_id
last_tip_id = id
selected_tips[#selected_tips + 1] = _InternalTranslate(tip)
end
dlg:CreateThread(function()
local idx = 1
while true do
atTips:SetText(Untranslated(selected_tips[idx]))
idx = (idx + 1) > #selected_tips and 1 or (idx + 1)
Sleep(LoadingScreenTipsRate)
end
end)
end
end
dlg.clock_opened = GetClock()
return dlg
end
function LoadingScreenExecute(id, reason, func)
LoadingScreenOpen(id, reason)
local result = func()
LoadingScreenClose(id, reason)
return result
end
function LoadingScreenOpen(id, reason, first_tip, metadata)
assert(IsRealTimeThread(), "The loading screen requires a real time thread")
lsprintf("Opening %s, reason = %s", tostring(id), tostring(reason))
local class = LoadingScreenGetClassById(id)
if not class then
assert(false, "No loading screen class matches " .. tostring(id))
return
end
local dlg = GetDialog(class)
if dlg and dlg.window_state == "closing" then
local modifier = dlg:FindModifier("fade")
if modifier then
modifier.on_complete = function() end
end
dlg:delete()
dlg = nil
end
dlg = dlg or LoadingScreenCreate(class, id, reason, first_tip, metadata)
dlg:AddOpenReason(reason)
lsprintf("Opened %s, reason = %s", tostring(id), tostring(reason))
if dlg.game_blocking then
WaitNextFrame(5) -- wait for UI to be rendered
end
end
function LoadingScreenClose(id, reason)
lsprintf("Closing %s, reason = %s", tostring(id), tostring(reason))
local class = LoadingScreenGetClassById(id)
local dlg = class and GetDialog(class)
if not dlg then
lsprintf("Closing %s cancelled, no dialog", tostring(id))
return
end
if not dlg:GetOpenReasons()[reason] then
print("Trying to close a Loading/Saving screen with id/reason that aren't used for opening: " .. tostring(reason))
print("Active reasons:", table.concat(table.keys2(dlg:GetOpenReasons()), " "))
lsprintf("Closing %s cancelled, no reason", tostring(id))
return
end
if dlg:RemoveOpenReason(reason) then -- return true in case we should close the dialog
lsprintf("Closing %s, no reasons left", tostring(id))
-- add the reason back to make sure LoadingScreenOpen doesn't try to open the same dialog
dlg:AddOpenReason(reason)
local parent_thread = CurrentThread()
local game_blocking = dlg.game_blocking
CreateRealTimeThread(function()
while dlg.clock_opened == 0 do
Sleep(17)
end
local clock_closed = dlg.clock_opened
if dlg.saving then
clock_closed = clock_closed + 3141
elseif game_blocking then
clock_closed = clock_closed + (dlg.close_delay or 1200)
end
lsprintf("Closing %s, waiting clock", tostring(id))
while GetClock() - clock_closed < 0 do
Sleep(30)
end
-- check if we still need to close the dialog
lsprintf("Closing %s, checking for reopen", tostring(id))
if dlg:RemoveOpenReason(reason) then
lsprintf("Closing %s, final closing", tostring(id))
if game_blocking then
-- if there are other loading screens opened at the moment, don't bother with the render mode, the last one will take care
local dlgs = ListDialogs()
local unblock = true
for i = 1, #dlgs do
local d = GetDialog(dlgs[i])
if d ~= dlg and d:IsKindOf("BaseLoadingScreen") and d.game_blocking then
unblock = false
break
end
end
if unblock and GetMap() ~= "" then
if not dlg.saving then
WaitNextFrame(3)
SetupViews()
end
end
ChangeGameState("loading", not unblock)
UnlockCamera(dlg)
Resume(dlg)
end
--assert(next(dlg.ids_and_reasons) == nil, "Loading screen reopened too soon after being closed! Please use a single screen that encompasses both operations!")
-- Recheck again if we should close: while waiting for "scene" render mode above, someone could have closed it
if not next(dlg:GetOpenReasons()) then
-- Give a chance to those who want to open something before the loading screen closes
Msg("LoadingScreenPreClose")
WaitNextFrame()
-- Recheck again if we should close: while waiting above, someone could have added a new open reason
if not next(dlg:GetOpenReasons()) then
if dlg.window_state ~= "destroying" then
dlg:Close("final")
table.remove_entry(LoadingScreenLog, class)
end
lsprintf("Closed %s, reason = %s", tostring(id), tostring(reason))
end
end
if next(dlg:GetOpenReasons()) then
lsprintf("Cancelled closing, we have a new reason to live", next(dlg:GetOpenReasons()))
end
end
if game_blocking then
Wakeup(parent_thread)
end
end)
if game_blocking then
WaitWakeup()
end
end
end
DefineClass.BaseLoadingScreen = {
__parents = { "XDialog" },
properties = {
{ category = "LoadingScreen", id = "game_blocking", editor = "bool", default = true, },
},
clock_opened = 0,
close_delay = false,
saving = false,
show_tips = true,
ZOrder = 1000000000,
MouseCursor = "CommonAssets/UI/waitcursor.tga",
HandleMouse = true,
image = "UI/SplashScreen.tga",
transparent = false,
FocusOnOpen = "",
}
function BaseLoadingScreen:Open(...)
XDialog.Open(self, ...)
ShowMouseCursor("Loading screen")
if self.game_blocking then
self:SetModal()
self:SetFocus()
end
if self.transparent then
self:SetMouseCursor(false)
self.HandleMouse = false
self.ChildrenHandleMouse = false
end
if rawget(self, "idImage") then
self.idImage:SetImage(self.image)
end
end
function BaseLoadingScreen:OnShortcut(shortcut, source, ...)
if (Platform.publisher or Platform.developer) and shortcut == "Ctrl-F1" then
return "continue" -- allow bug reporter
end
if self.game_blocking and not AreMessageBoxesOpen() then
return "break"
end
end
function BaseLoadingScreen:Close(result)
if result == "final" then
HideMouseCursor("Loading screen")
XWindow.Close(self)
end
end
DefineClass.BaseSavingScreen = {
__parents = { "BaseLoadingScreen" },
saving = true,
game_blocking = false,
image = false,
transparent = true,
}
function GetOpenLoadingScreen(id)
local class = LoadingScreenGetClassById(id)
return class and GetDialog(class) and true or false
end
function DrawSplashScreen()
-- stub, redefine per project
-- ATTN: this is called before the initialization of the classes system, only use pure UIL calls
-- mimic the bink player behavior, stretch full screen
-- do not measure the image size, because in some cases this fails, use 16:9 image
local screen = UIL.GetScreenSize()
local size = ScaleToFit(LoadingScreenOrgSize, screen, not "clip")
local pos = (screen - size) / 2
local rc = box(pos, pos + size)
UIL.DrawSolidRect(box(point20, screen), RGB(0,0,0))
UIL.DrawImage("UI/SplashScreen.tga", rc)
end
DefineClass.XLoadingScreenClass =
{
__parents = { "BaseLoadingScreen" },
FadeOutTime = 300,
Background = RGB(0, 0, 0),
}
g_LoadingScreens = {
"UI/SplashScreen.tga",
}
if FirstLoad then
g_FirstLoadingScreen = true
end
function XLoadingScreenClass:Open(...)
self.image = g_FirstLoadingScreen and "UI/SplashScreen.tga" or table.rand(g_LoadingScreens)
g_FirstLoadingScreen = false
BaseLoadingScreen.Open(self, ...)
end
function GetGameBlockingLoadingScreen()
for _, d in pairs(Dialogs) do
if d and IsKindOf(d, "BaseLoadingScreen") and d.game_blocking then
return d
end
end
end
function WaitLoadingScreenClose() -- only checks for game blocking loading screens
while GetGameBlockingLoadingScreen() do
WaitNextFrame()
end
end
--[[
--test cases
function SST_CloseAndReopen()
CreateRealTimeThread(function()
LoadingScreenOpen("idSavingScreen")
Sleep(1000) -- write something
LoadingScreenClose("idSavingScreen")
assert(GetOpenLoadingScreen("idSavingScreen"))
Sleep(3000)
assert(GetOpenLoadingScreen("idSavingScreen"))
LoadingScreenOpen("idSavingScreen")
Sleep(1000)
LoadingScreenClose("idSavingScreen")
Sleep(499)
assert(GetOpenLoadingScreen("idSavingScreen")) -- should stay for 500 ms after the last 'Close'
Sleep(2)
assert(not GetOpenLoadingScreen("idSavingScreen")) -- and still be closed properly (more than 4 seconds have passed since it has been opened)
end)
end
function SST_OpenCloseAndChangeMap(map)
CreateRealTimeThread(function()
LoadingScreenOpen("idSavingScreen", "test")
Sleep(1000) -- write something
LoadingScreenClose("idSavingScreen", "test")
print("changing map, the saving screen should disappear in ~3 sec")
ChangeMap(map)
end)
end
--]] |