Skip to content

Commit b478409

Browse files
committed
feat: BootLoadingMode config option
BootLoadingMode config option for adjusting boot preload behavior for portraits, palettes and stage previews. * 0 = Off : preload everything before entering menus / select. (default) * 1 = Wait : preload in the background, but wait before entering select screen. If logo/intro uses waitforloading = 1, that is used instead. * 2 = Immediate : enter menus and select screen immediately and load assets in the background. When BootLoadingMode = 2, highlighted characters are prioritized during selection. Once the highlighted character finishes preloading, normal low-priority queue order resumes. Moving the cursor deprioritizes the previously highlighted character unless another cursor still highlights it.
1 parent 41ce405 commit b478409

10 files changed

Lines changed: 1006 additions & 204 deletions

File tree

external/script/main.lua

Lines changed: 310 additions & 80 deletions
Large diffs are not rendered by default.

external/script/options.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ options.t_itemname = {
170170
--modifyGameOption('Config.EscOpensMenu', true)
171171
modifyGameOption('Config.VsScreenLoading', false)
172172
modifyGameOption('Config.TurnsLoading', false)
173+
--modifyGameOption('Config.BootLoadingMode', 0)
173174
--modifyGameOption('Config.FirstRun', false)
174175
--modifyGameOption('Config.WindowTitle', "Ikemen GO")
175176
--modifyGameOption('Config.WindowIcon', {"external/icons/IkemenCylia_256.png", "external/icons/IkemenCylia_96.png", "external/icons/IkemenCylia_48.png"})

external/script/start.lua

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ function start.f_setStage(num, assigned)
501501
end
502502
end
503503
selectStage(num)
504+
main.f_preloadBoostStage(num)
504505
return num
505506
end
506507

@@ -1699,6 +1700,9 @@ function start.f_selectMode()
16991700
start.f_selectReset(true)
17001701
while true do
17011702
--select screen
1703+
if gameOption('Config.BootLoadingMode') == 1 then
1704+
main.f_waitForPreloads()
1705+
end
17021706
if not start.f_selectScreen() then
17031707
sndPlay(motif.Snd, motif.select_info.cancel.snd[1], motif.select_info.cancel.snd[2])
17041708
bgReset(motif[main.background].BGDef)
@@ -1873,6 +1877,12 @@ function start.f_selectReset(hardReset, preserveProgress)
18731877
t_reservedChars = {{}, {}}
18741878
cursorActive = {}
18751879
cursorDone = {}
1880+
if main.preload ~= nil and main.preload.charHighlight ~= nil then
1881+
for _, ref in pairs(main.preload.charHighlight) do
1882+
queueCharPreload(ref, 1)
1883+
end
1884+
main.preload.charHighlight = {}
1885+
end
18761886
t_portraitPriority = {1, 1}
18771887
if start.challenger == 0 and not preserveProgress then
18781888
start.t_roster = {}
@@ -2332,6 +2342,41 @@ end
23322342
--;===========================================================
23332343
--; SELECT SCREEN
23342344
--;===========================================================
2345+
local function refreshActiveFacePortraits()
2346+
for side = 1, 2 do
2347+
for k, v in ipairs(start.p[side].t_selCmd) do
2348+
local member = main.f_tableLength(start.p[side].t_selected) + k
2349+
if main.coop and (side == 1 or gameMode('versuscoop')) then
2350+
member = k
2351+
end
2352+
local st = start.p[side].t_selTemp[member]
2353+
local player = v.player
2354+
local selRef = start.c[player].selRef
2355+
if v.selectState == 0 and st ~= nil and selRef ~= nil and st.ref == selRef then
2356+
local state = getCharPreloadStatus(selRef)
2357+
if state == 'ready' then
2358+
local pn = 2 * (member - 1) + side
2359+
local pCfg = f_getMotifP(motif.select_info, pn, side)
2360+
local updated = false
2361+
if st.face_data == nil then
2362+
st.face_anim = pCfg.face.anim
2363+
st.face_data = start.f_animGet(selRef, side, member, pCfg.face, nil, true, st.face_data)
2364+
updated = updated or st.face_data ~= nil
2365+
end
2366+
if st.face2_data == nil then
2367+
st.face2_anim = pCfg.face2.anim
2368+
st.face2_data = start.f_animGet(selRef, side, member, pCfg.face2, nil, true, st.face2_data)
2369+
updated = updated or st.face2_data ~= nil
2370+
end
2371+
if updated then
2372+
start.needUpdateDrawList = true
2373+
end
2374+
end
2375+
end
2376+
end
2377+
end
2378+
end
2379+
23352380
function start.updateDrawList()
23362381
local drawList = {}
23372382

@@ -2496,6 +2541,8 @@ function start.f_selectScreen()
24962541
start.needUpdateDrawList = false
24972542

24982543
while not selScreenEnd do
2544+
main.f_preloadTick(4)
2545+
refreshActiveFacePortraits()
24992546
counter = counter + 1
25002547
--draw clearcolor
25012548
clearColor(motif.selectbgdef.bgclearcolor[1], motif.selectbgdef.bgclearcolor[2], motif.selectbgdef.bgclearcolor[3])
@@ -2688,10 +2735,29 @@ function start.f_selectScreen()
26882735
)
26892736
end
26902737
if not stageEnd then
2691-
if (getInput(-1, motif.select_info.done.key) and not screenDelayInterrupted) or timerSelect == -1 then
2692-
sndPlay(motif.Snd, motif.select_info.stage.done.snd[1], motif.select_info.stage.done.snd[2])
2693-
stageTextData = motif.select_info.stage.done.TextSpriteData
2694-
stageEnd = true
2738+
local canConfirmStage = (getInput(-1, motif.select_info.done.key) and not screenDelayInterrupted) or timerSelect == -1
2739+
if canConfirmStage then
2740+
local preloadReady = true
2741+
if stageListNo > 0 then
2742+
local stageRef = main.t_selectableStages[stageListNo]
2743+
local state = getStagePreloadStatus(stageRef)
2744+
preloadReady = state == 'ready'
2745+
if not preloadReady then
2746+
main.f_preloadBoostStage(stageRef)
2747+
else
2748+
if main.f_materializeStagePortrait(stageRef) then
2749+
start.needUpdateDrawList = true
2750+
end
2751+
end
2752+
end
2753+
if not preloadReady and getInput(-1, motif.select_info.done.key) and not screenDelayInterrupted then
2754+
sndPlay(motif.Snd, motif.select_info.cancel.snd[1], motif.select_info.cancel.snd[2])
2755+
end
2756+
if preloadReady then
2757+
sndPlay(motif.Snd, motif.select_info.stage.done.snd[1], motif.select_info.stage.done.snd[2])
2758+
stageTextData = motif.select_info.stage.done.TextSpriteData
2759+
stageEnd = true
2760+
end
26952761
elseif stageActiveCount < motif.select_info.stage.active.switchtime then --delay change
26962762
stageActiveCount = stageActiveCount + 1
26972763
else
@@ -3281,6 +3347,7 @@ function start.f_selectMenu(side, cmd, player, member, selectState)
32813347
start.c[player].selX, start.c[player].selY = start.f_cellMovement(start.c[player].selX, start.c[player].selY, cmd, side, start.f_getCursorData(player).cursor.move.snd)
32823348
start.c[player].cell = start.c[player].selX + motif.select_info.columns * start.c[player].selY
32833349
start.c[player].selRef = start.f_selGrid(start.c[player].cell + 1).char_ref
3350+
main.f_preloadSetCharHighlight(player, start.c[player].selRef)
32843351
-- temp data not existing yet
32853352
if start.p[side].t_selTemp[member] == nil then
32863353
t_portraitPriority[side] = member
@@ -3299,6 +3366,9 @@ function start.f_selectMenu(side, cmd, player, member, selectState)
32993366
local timerExpired = motif.select_info.timer.count ~= -1 and timerSelect == -1
33003367
needUpdateDrawList = slotChanged
33013368
local velCopy = false
3369+
if slotChanged then
3370+
start.c[player].selRef = start.f_selGrid(start.c[player].cell + 1).char_ref
3371+
end
33023372
if timerExpired then
33033373
if start.c[player].selRef == nil or main.t_selChars[start.c[player].selRef + 1] == nil then
33043374
start.c[player].selRef = start.f_randomChar(side)
@@ -3354,6 +3424,7 @@ function start.f_selectMenu(side, cmd, player, member, selectState)
33543424
else
33553425
start.p[side].t_selTemp[member].inRandom = false
33563426
end
3427+
main.f_preloadSetCharHighlight(player, start.c[player].selRef)
33573428
-- update anim data
33583429
if updateAnim then
33593430
local face_data = velCopy and start.p[side].t_selTemp[member].face_data or nil
@@ -3362,7 +3433,26 @@ function start.f_selectMenu(side, cmd, player, member, selectState)
33623433
start.p[side].t_selTemp[member].face2_data = start.f_animGet(start.c[player].selRef, side, member, pCfg.face2, nil, true, face2_data)
33633434
end
33643435
-- cell selected or select screen timer reached 0
3365-
if (slotSelected and start.f_selGrid(start.c[player].cell + 1).char ~= nil and start.f_selGrid(start.c[player].cell + 1).hidden ~= 2) or timerExpired then
3436+
local canConfirm = (slotSelected and start.f_selGrid(start.c[player].cell + 1).char ~= nil and start.f_selGrid(start.c[player].cell + 1).hidden ~= 2) or timerExpired
3437+
if canConfirm then
3438+
local preloadReady = true
3439+
if start.c[player].selRef ~= nil then
3440+
local state = getCharPreloadStatus(start.c[player].selRef)
3441+
preloadReady = state == 'ready'
3442+
if not preloadReady then
3443+
main.f_preloadBoostChar(start.c[player].selRef)
3444+
else
3445+
main.f_materializeCharByRef(start.c[player].selRef)
3446+
end
3447+
end
3448+
if not preloadReady then
3449+
if slotSelected then
3450+
sndPlay(motif.Snd, motif.select_info.cancel.snd[1], motif.select_info.cancel.snd[2])
3451+
end
3452+
canConfirm = false
3453+
end
3454+
end
3455+
if canConfirm then
33663456
if motif.select_info.paletteselect ~= 0 then
33673457
timerSelect = motif.select_info.timer.displaytime
33683458
end
@@ -3476,6 +3566,7 @@ function start.f_selectMenu(side, cmd, player, member, selectState)
34763566
pn = start.f_getPlayerNo(side, member),
34773567
cursor = {start.c[player].selX, start.c[player].selY},
34783568
}
3569+
main.f_preloadSetCharHighlight(player, nil)
34793570
hook.run("start.f_selectMenu.selected", side, member, start.p[side].t_selected[member], start.p[side], player)
34803571
if not gameOption('Options.Team.Duplicates') then
34813572
t_reservedChars[side][start.c[player].selRef] = true
@@ -3572,6 +3663,9 @@ function start.f_stageMenu()
35723663
animReset(main.t_selStages[main.t_selectableStages[stageListNo]].anim_data)
35733664
animUpdate(main.t_selStages[main.t_selectableStages[stageListNo]].anim_data)
35743665
end
3666+
if stageListNo > 0 then
3667+
main.f_preloadBoostStage(main.t_selectableStages[stageListNo])
3668+
end
35753669
end
35763670

35773671
--;===========================================================
@@ -3755,6 +3849,7 @@ function start.f_selectVersus(active, t_orderSelect, loadStartArg)
37553849
end
37563850
end
37573851
while true do
3852+
main.f_preloadTick(4)
37583853
local snd = false
37593854
-- CPU order select: randomize first, then selectChar() using randomized slot order
37603855
for side = 1, 2 do

src/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ type Config struct {
115115
EscOpensMenu bool `ini:"EscOpensMenu" sync:"host"`
116116
VsScreenLoading bool `ini:"VsScreenLoading" sync:"host"`
117117
TurnsLoading bool `ini:"TurnsLoading" sync:"host"`
118+
BootLoadingMode int32 `ini:"BootLoadingMode" sync:"host"`
118119
FirstRun bool `ini:"FirstRun"`
119120
WindowTitle string `ini:"WindowTitle"`
120121
WindowIcon []string `ini:"WindowIcon"`

src/motif.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,11 @@ type TitleInfoProperties struct {
640640
Cancel struct {
641641
Snd [2]int32 `ini:"snd" default:"-1,0"`
642642
} `ini:"cancel"`
643-
Loading TextProperties `ini:"loading"`
643+
Loading struct {
644+
TextProperties
645+
Wait AnimationTextProperties `ini:"wait"`
646+
Storyboard string `ini:"storyboard" lookup:"def,,data/"`
647+
} `ini:"loading"`
644648
Footer struct {
645649
Title TextProperties `ini:"title"`
646650
Info TextProperties `ini:"info"`
@@ -2196,6 +2200,8 @@ func (m *Motif) customResultsScreenSections() []string {
21962200
func (m *Motif) overrideParams() {
21972201
// Define inheritance rules (section/prefix based).
21982202
specs := []InheritSpec{
2203+
// [Title Info]
2204+
{SrcSec: "Title Info", SrcPrefix: "loading.", DstSec: "Title Info", DstPrefix: "loading.wait."},
21992205
// [Option Info]
22002206
{SrcSec: "Option Info", SrcPrefix: "menu.", DstSec: "Option Info", DstPrefix: "keymenu."},
22012207
// [Select Info]

src/resources/defaultConfig.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ EscOpensMenu = 1
127127
VsScreenLoading = 0
128128
; Toggles Turns member loading during the match.
129129
TurnsLoading = 0
130+
; Boot preload behavior for portraits, palettes and stage previews.
131+
; 0 = Off : preload everything before entering menus / select. (default)
132+
; 1 = Wait : preload in the background, but wait before entering select screen.
133+
; If logo/intro uses waitforloading = 1, that is used instead.
134+
; 2 = Immediate : enter menus and select screen immediately and load assets in the background.
135+
BootLoadingMode = 0
130136
; This is 1 the first time you run IKEMEN.
131137
FirstRun = 1
132138
; Title of the application window.

src/resources/defaultMotif.ini

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,27 @@
460460
loading.window =
461461
loading.localcoord =
462462

463+
; Boot-loading wait element rendered on black screen when BootLoadingMode = 1
464+
loading.wait.anim = -1
465+
loading.wait.spr = -1, 0
466+
loading.wait.font = default-3x5.def, 0, -1, 191, 191, 191, 255, -1
467+
loading.wait.offset = 0, 0
468+
loading.wait.facing = 1
469+
loading.wait.scale = 1.0, 1.0
470+
loading.wait.xshear = 0
471+
loading.wait.angle = 0
472+
loading.wait.xangle = 0
473+
loading.wait.yangle = 0
474+
loading.wait.projection = orthographic
475+
loading.wait.focallength = 2048
476+
loading.wait.text = LOADING...
477+
loading.wait.layerno = 0
478+
loading.wait.window =
479+
loading.wait.localcoord = 320, 240
480+
481+
; Optional storyboard used by BootLoadingMode = 1 instead of loading.wait
482+
loading.storyboard =
483+
463484
; Footer parameters control rendering of engine version info. Separated into
464485
; 3 elements to allow left/center/right text align at the same time.
465486
; Unless overridden, offsets and version text are set when loading the motif.

src/script.go

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,14 @@ func systemScriptInit(l *lua.LState) {
11631163
@treturn Anim|nil anim A new `Anim` userdata wrapping the preloaded animation,
11641164
or `nil` if no matching animation exists.
11651165
function animGetPreloadedCharData(charRef, group, number, keepLoop) end*/
1166-
if anim := sys.sel.GetChar(int(numArg(l, 1))).anims.get(int32(numArg(l, 2)), int32(numArg(l, 3))); anim != nil {
1166+
sys.sel.preloadMu.Lock()
1167+
ch := sys.sel.GetChar(int(numArg(l, 1)))
1168+
var anim *Animation
1169+
if ch != nil {
1170+
anim = ch.anims.get(int32(numArg(l, 2)), int32(numArg(l, 3)))
1171+
}
1172+
sys.sel.preloadMu.Unlock()
1173+
if anim != nil {
11671174
a := NewAnim(nil, "")
11681175
a.anim = anim
11691176
if !nilArg(l, 4) && !boolArg(l, 4) && a.anim.totaltime == a.anim.looptime {
@@ -1188,7 +1195,14 @@ func systemScriptInit(l *lua.LState) {
11881195
@treturn Anim|nil anim A new `Anim` userdata wrapping the preloaded animation,
11891196
or `nil` if no matching animation exists.
11901197
function animGetPreloadedStageData(stageRef, group, number, keepLoop) end*/
1191-
if anim := sys.sel.GetStage(int(numArg(l, 1))).anims.get(int32(numArg(l, 2)), int32(numArg(l, 3))); anim != nil {
1198+
sys.sel.preloadMu.Lock()
1199+
st := sys.sel.GetStage(int(numArg(l, 1)))
1200+
var anim *Animation
1201+
if st != nil {
1202+
anim = st.anims.get(int32(numArg(l, 2)), int32(numArg(l, 3)))
1203+
}
1204+
sys.sel.preloadMu.Unlock()
1205+
if anim != nil {
11921206
a := NewAnim(nil, "")
11931207
a.anim = anim
11941208
if !nilArg(l, 4) && !boolArg(l, 4) && a.anim.totaltime == a.anim.looptime {
@@ -3397,6 +3411,18 @@ func systemScriptInit(l *lua.LState) {
33973411
l.Push(lua.LString(c.name))
33983412
return 1
33993413
})
3414+
luaRegister(l, "getCharPreloadStatus", func(l *lua.LState) int {
3415+
/*Query the background preload state of a character slot.
3416+
@function getCharPreloadStatus
3417+
@tparam int charRef 0-based character index in the select list.
3418+
@treturn string state Preload state: `"idle"`, `"queued"`, `"loading"`, `"ready"`, or `"error"`.
3419+
@treturn string err Error message (empty string if no error).
3420+
function getCharPreloadStatus(charRef) end*/
3421+
state, err := sys.sel.CharPreloadStatus(int(numArg(l, 1)))
3422+
l.Push(lua.LString(state.String()))
3423+
l.Push(lua.LString(err))
3424+
return 2
3425+
})
34003426
luaRegister(l, "getCharRandomPalette", func(*lua.LState) int {
34013427
/*Get a random valid palette number for a character slot.
34023428
@function getCharRandomPalette
@@ -4005,6 +4031,18 @@ func systemScriptInit(l *lua.LState) {
40054031
l.Push(lua.LNumber(sys.sel.selectedStageNo))
40064032
return 1
40074033
})
4034+
luaRegister(l, "getStagePreloadStatus", func(l *lua.LState) int {
4035+
/*Query the background preload state of a stage slot.
4036+
@function getStagePreloadStatus
4037+
@tparam int stageRef Stage index as used by the select system.
4038+
@treturn string state Preload state: `"idle"`, `"queued"`, `"loading"`, `"ready"`, or `"error"`.
4039+
@treturn string err Error message (empty string if no error).
4040+
function getStagePreloadStatus(stageRef) end*/
4041+
state, err := sys.sel.StagePreloadStatus(int(numArg(l, 1)))
4042+
l.Push(lua.LString(state.String()))
4043+
l.Push(lua.LString(err))
4044+
return 2
4045+
})
40084046
luaRegister(l, "getStageSelectParams", func(*lua.LState) int {
40094047
/*Get parsed select parameters for a stage entry.
40104048
@function getStageSelectParams
@@ -5443,6 +5481,14 @@ func systemScriptInit(l *lua.LState) {
54435481
l.Push(lua.LBool(sys.postMatchFlg))
54445482
return 1
54455483
})
5484+
luaRegister(l, "preloading", func(l *lua.LState) int {
5485+
/*Check whether resources are currently being preloaded.
5486+
@function preloading
5487+
@treturn boolean `true` if assets are still being preloaded.
5488+
function preloading() end*/
5489+
l.Push(lua.LBool(!sys.sel.AllPreloadsReady()))
5490+
return 1
5491+
})
54465492
luaRegister(l, "preloadListChar", func(*lua.LState) int {
54475493
/*Mark a character sprite or animation for preloading.
54485494
@function preloadListChar
@@ -5493,6 +5539,34 @@ func systemScriptInit(l *lua.LState) {
54935539
fmt.Println(strArg(l, 1))
54945540
return 0
54955541
})
5542+
luaRegister(l, "queueCharPreload", func(l *lua.LState) int {
5543+
/*Queue a character for background preloading of portraits and palettes.
5544+
@function queueCharPreload
5545+
@tparam int charRef 0-based character index in the select list.
5546+
@tparam[opt=1] int priority Priority level (`1` = low, `2` = high).
5547+
function queueCharPreload(charRef, priority) end*/
5548+
ref := int(numArg(l, 1))
5549+
priority := PreloadPriorityLow
5550+
if !nilArg(l, 2) {
5551+
priority = int(numArg(l, 2))
5552+
}
5553+
sys.sel.QueueCharPreload(ref, priority)
5554+
return 0
5555+
})
5556+
luaRegister(l, "queueStagePreload", func(l *lua.LState) int {
5557+
/*Queue a stage for background preloading of portraits.
5558+
@function queueStagePreload
5559+
@tparam int stageRef Stage index as used by the select system.
5560+
@tparam[opt=1] int priority Priority level (`1` = low, `2` = high).
5561+
function queueStagePreload(stageRef, priority) end*/
5562+
ref := int(numArg(l, 1))
5563+
priority := PreloadPriorityLow
5564+
if !nilArg(l, 2) {
5565+
priority = int(numArg(l, 2))
5566+
}
5567+
sys.sel.QueueStagePreload(ref, priority)
5568+
return 0
5569+
})
54965570
luaRegister(l, "rectDebug", func(*lua.LState) int {
54975571
/*Print a rectangle's debug information.
54985572
@function rectDebug

0 commit comments

Comments
 (0)