@@ -335,22 +335,22 @@ local function save_popup()
335335 end
336336end
337337
338- --- @return string []
339- local function get_active_units_with_missing_nemesis_records ()
340- local namelist = {}
341- for _ , unit in ipairs (df . global . world . units .active ) do
338+ --- @return df.unit []
339+ local function _get_active_units_with_missing_nemesis_records ()
340+ local list = {}
341+ for _ , unit in ipairs (units .active ) do
342342 local ref = dfhack .units .getGeneralRef (unit , df .general_ref_type .IS_NEMESIS )
343343 if ref then
344344 local nrec = ref :getNemesis ()
345345 if nrec == nil then
346- table.insert (namelist , dfhack . units . getReadableName ( unit ) )
346+ table.insert (list , unit )
347347 end
348348 end
349349 end
350- return namelist
350+ return list
351351end
352352
353- --- @param vector any[] # a df vector or array, or a Lua list.
353+ --- @param vector any[] # a df vector or array, or a Lua list (not tested yet) .
354354--- @param field string ? # nil, or the field name to sort on.
355355--- @param comparator fun ( a : any , b : any ): integer | nil
356356--- # an optional comparator that returns -1,0,1 per utils.compare_* .
@@ -378,17 +378,49 @@ local function verify_vector_is_sorted(vector, field, comparator)
378378 return sorted
379379end
380380
381- local cache_nemesis_all_is_sorted = {}
382- --- only verifies if the vector length has changed.
383- --- @return boolean
384- local function verify_nemesis_all_is_sorted ()
385- local vector = df .global .world .nemesis .all
386- if # vector == cache_nemesis_all_is_sorted .length then
387- return cache_nemesis_all_is_sorted .sorted
381+ local Cache_nemesis_all = defclass (Cache_nemesis_all , nil ) -- singleton, don't need to instantiate.
382+ Cache_nemesis_all .ATTRS {}
383+
384+ --- @param force boolean ? # true to force updating the cached data
385+ function Cache_nemesis_all :populate (force )
386+ if not force
387+ and self .ATTRS .cached_on_year == dfhack .world .ReadCurrentYear ()
388+ and (self .ATTRS .cached_on_tick or 0 ) + 600 < dfhack .world .ReadCurrentTick ()
389+ and self .ATTRS .nemesis_all_length == # df .global .world .nemesis .all
390+ and self .ATTRS .units_active_length == # df .global .world .units .active
391+ then
392+ return
388393 end
389- cache_nemesis_all_is_sorted .length = # vector
390- cache_nemesis_all_is_sorted .sorted = verify_vector_is_sorted (vector , ' id' )
391- return cache_nemesis_all_is_sorted .sorted
394+ print (" Cache_nemesis_all:populate" , " \n " ,
395+ " force" , force , " \n " ,
396+ " year" , self .ATTRS .cached_on_year , dfhack .world .ReadCurrentYear (), " \n " ,
397+ " tick" , self .ATTRS .cached_on_tick , dfhack .world .ReadCurrentTick (), " \n " ,
398+ " #nemall" , self .ATTRS .nemesis_all_length , # df .global .world .nemesis .all , " \n " ,
399+ " #active" , self .ATTRS .units_active_length , # df .global .world .units .active , " \n " ,
400+ " " )
401+
402+ self .ATTRS .cached_on_year = dfhack .world .ReadCurrentYear ()
403+ self .ATTRS .cached_on_tick = dfhack .world .ReadCurrentTick ()
404+ self .ATTRS .nemesis_all_length = # df .global .world .nemesis .all
405+ self .ATTRS .units_active_length = # df .global .world .units .active
406+ self .ATTRS .nemesis_all_is_sorted = verify_vector_is_sorted (df .global .world .nemesis .all , ' id' )
407+ self .ATTRS .affected_units = _get_active_units_with_missing_nemesis_records ()
408+ end
409+
410+ function Cache_nemesis_all :init ()
411+ self :populate (true )
412+ end
413+
414+ --- @return boolean
415+ function Cache_nemesis_all :is_sorted ()
416+ self :populate ()
417+ return self .ATTRS .nemesis_all_is_sorted
418+ end
419+
420+ --- @return df.unit[]
421+ function Cache_nemesis_all :get_affected_units ()
422+ self :populate ()
423+ return self .ATTRS .affected_units
392424end
393425
394426-- the order of this list controls the order the notifications will appear in the overlay
@@ -398,7 +430,7 @@ NOTIFICATIONS_BY_IDX = {
398430 desc = ' Reports missing nemesis records, indicating savegame corruption.' ,
399431 default = true ,
400432 fn = function ()
401- if not verify_nemesis_all_is_sorted () then
433+ if not Cache_nemesis_all : is_sorted () then
402434 return { {
403435 pen = COLOR_LIGHTRED ,
404436 text = ' nemesis vector not sorted'
@@ -407,12 +439,13 @@ NOTIFICATIONS_BY_IDX = {
407439 local count = df .global .nemesis_next_id - # df .global .world .nemesis .all
408440 if count == 0 then return end
409441 return { {
410- pen = COLOR_LIGHTRED ,
442+ pen = # Cache_nemesis_all :get_affected_units () > 0
443+ and COLOR_LIGHTRED or COLOR_YELLOW ,
411444 text = (' missing %d nemesis record%s' ):format (count , count == 1 and ' ' or ' s' )
412445 } }
413446 end ,
414447 on_click = function ()
415- if not verify_nemesis_all_is_sorted () then
448+ if not Cache_nemesis_all : is_sorted () then
416449 local message =
417450 ' This save game is corrupt.\n\n The world.nemesis.global vector\n ' ..
418451 ' of this savegame is not sorted.\n\n Some attempts to lookup the\n ' ..
@@ -421,6 +454,7 @@ NOTIFICATIONS_BY_IDX = {
421454 dlg .showMessage (' nemesis vector not sorted' , message , COLOR_RED )
422455 return
423456 end
457+ local list = Cache_nemesis_all :get_affected_units ()
424458 local message = {
425459 { pen = COLOR_RED , text = ' This save game may be corrupt.' }, NEWLINE ,
426460 NEWLINE ,
@@ -431,23 +465,29 @@ NOTIFICATIONS_BY_IDX = {
431465 { pen = COLOR_WHITE , text = ' crashes during game save and when retiring forts.' }, NEWLINE ,
432466 NEWLINE ,
433467 { pen = COLOR_WHITE , text = ' Units with missing nemesis records will' }, NEWLINE ,
434- { pen = COLOR_RED , text = ' permanently disappear' },
468+ { pen = # list > 0 and COLOR_RED or COLOR_WHITE ,
469+ text = ' permanently disappear' },
435470 { pen = COLOR_WHITE , text = ' if they leave the map or' }, NEWLINE ,
436471 { pen = COLOR_WHITE , text = ' if the fort is retired.' }, NEWLINE ,
437472 NEWLINE ,
438473 }
439- local redtext = get_active_units_with_missing_nemesis_records ()
440- if # redtext > 0 then
474+ if # list > 0 then
441475 table.insert (message , { pen = COLOR_RED ,
442- text = ' These active units are missing their nemesis records:' })
476+ text = ' These units on the map are missing their nemesis records:' })
443477 table.insert (message , NEWLINE )
444- for _ , line in ipairs (redtext ) do
445- table.insert (message , { pen = COLOR_LIGHTRED , text = ' ' .. line })
478+ for _ , unit in ipairs (list ) do
479+ local text = dfhack .units .getReadableName (unit )
480+ if # text > 55 then text = dfhack .units .getReadableName (unit , true ); end
481+ table.insert (message , { pen = COLOR_LIGHTRED , text = text })
446482 table.insert (message , NEWLINE )
447483 end
484+ else
485+ table.insert (message , { pen = COLOR_YELLOW ,
486+ text = ' No units on the map are missing their nemesis records.' })
487+ table.insert (message , NEWLINE )
448488 end
449- dlg .showMessage ((# redtext > 0 and ' Active units are' or ' This world is' )
450- .. ' missing nemesis records' ,message , COLOR_WHITE )
489+ dlg .showMessage ((# list > 0 and ' Units on the map are' or ' This world is' )
490+ .. ' missing nemesis records' , message , COLOR_WHITE )
451491 end ,
452492 },
453493 {
728768config = get_config ()
729769
730770dfhack .onStateChange [' internal/notify/notifications' ] = function (event )
731- if event == SC_WORLD_LOADED or event == SC_WORLD_UNLOADED then
732- cache_nemesis_all_is_sorted = {}
771+ if event == SC_WORLD_LOADED or SC_WORLD_UNLOADED or SC_MAP_LOADED or SC_MAP_UNLOADED then
772+ Cache_nemesis_all : populate ( true )
733773 end
734774end
0 commit comments