@@ -12,10 +12,27 @@ local widgets = require('gui.widgets')
12
12
local presets_file = json .open (" dfhack-config/mod-manager.json" )
13
13
local GLOBAL_KEY = ' mod-manager'
14
14
15
- -- get_newregion_viewscreen and get_modlist_fields are declared as global functions
16
- -- so external tools can call them to get the DF mod list
17
- function get_newregion_viewscreen ()
15
+ local function vanilla (dir )
16
+ return dir :startswith (' data/vanilla' )
17
+ end
18
+
19
+ -- get_moddable_viewscreen(), get_any_moddable_viewscreen() and get_modlist_fields are declared
20
+ -- as global functions so external tools can call them to get the DF mod list
21
+ function get_moddable_viewscreen (type )
22
+ local vs = nil
23
+ if type == ' region' then
24
+ vs = dfhack .gui .getViewscreenByType (df .viewscreen_new_regionst , 0 )
25
+ elseif type == ' arena' then
26
+ vs = dfhack .gui .getViewscreenByType (df .viewscreen_new_arenast , 0 )
27
+ end
28
+ return vs
29
+ end
30
+
31
+ function get_any_moddable_viewscreen ()
18
32
local vs = dfhack .gui .getViewscreenByType (df .viewscreen_new_regionst , 0 )
33
+ if not vs then
34
+ vs = dfhack .gui .getViewscreenByType (df .viewscreen_new_arenast , 0 )
35
+ end
19
36
return vs
20
37
end
21
38
@@ -55,21 +72,30 @@ function get_modlist_fields(kind, viewscreen)
55
72
end
56
73
end
57
74
58
- local function move_mod_entry (viewscreen , to , from , mod_id , mod_version )
75
+ --- @return boolean # true if the mod entry was copied over; false if the mod or mod version was not found.
76
+ --- @return string | nil # loaded version - DISPLAYED_VERSION from the mod's info.txt
77
+ local function copy_mod_entry (viewscreen , to , from , mod_id , mod_version )
59
78
local to_fields = get_modlist_fields (to , viewscreen )
60
79
local from_fields = get_modlist_fields (from , viewscreen )
61
80
62
81
local mod_index = nil
82
+ local loaded_version = nil
63
83
for i , v in ipairs (from_fields .id ) do
64
84
local version = from_fields .numeric_version [i ]
65
- if v .value == mod_id and version == mod_version then
85
+ local src_dir = from_fields .src_dir [i ]
86
+ local displayed_version = from_fields .displayed_version [i ].value
87
+ -- assumes that vanilla mods will not have multiple possible indices.
88
+ if v .value == mod_id and (vanilla (src_dir ) or version == mod_version ) then
89
+ if version ~= mod_version then
90
+ loaded_version = displayed_version
91
+ end
66
92
mod_index = i
67
93
break
68
94
end
69
95
end
70
96
71
97
if mod_index == nil then
72
- return false
98
+ return false , nil
73
99
end
74
100
75
101
for k , v in pairs (to_fields ) do
@@ -80,19 +106,49 @@ local function move_mod_entry(viewscreen, to, from, mod_id, mod_version)
80
106
end
81
107
end
82
108
83
- for k , v in pairs (from_fields ) do
84
- v :erase (mod_index )
85
- end
86
-
87
- return true
109
+ return true , loaded_version
88
110
end
89
111
112
+ --- @return boolean # true if the mod entry was copied over; false if the mod or mod version was not found.
113
+ --- @return string | nil # loaded version - DISPLAYED_VERSION from the mod's info.txt
90
114
local function enable_mod (viewscreen , mod_id , mod_version )
91
- return move_mod_entry (viewscreen , " object_load_order" , " available" , mod_id , mod_version )
115
+ return copy_mod_entry (viewscreen , " object_load_order" , " base_available" , mod_id , mod_version )
116
+ end
117
+
118
+ --- @return boolean # true if the mod entry was copied over; false if the mod or mod version was not found.
119
+ --- @return string | nil # loaded version - DISPLAYED_VERSION from the mod's info.txt
120
+ local function make_available_mod (viewscreen , mod_id , mod_version )
121
+ return copy_mod_entry (viewscreen , " available" , " base_available" , mod_id , mod_version )
122
+ end
123
+
124
+ local function clear_mods (viewscreen )
125
+ local active_modlist = get_modlist_fields (' object_load_order' , viewscreen )
126
+ local avail_modlist = get_modlist_fields (' available' , viewscreen )
127
+ for _ , modlist in ipairs ({active_modlist , avail_modlist }) do
128
+ for _ , v in pairs (modlist ) do
129
+ for i = # v - 1 , 0 , - 1 do
130
+ v :erase (i )
131
+ end
132
+ end
133
+ end
92
134
end
93
135
94
- local function disable_mod (viewscreen , mod_id , mod_version )
95
- return move_mod_entry (viewscreen , " available" , " object_load_order" , mod_id , mod_version )
136
+ local function set_available_mods (viewscreen , loaded )
137
+ local base_avail = get_modlist_fields (' base_available' , viewscreen )
138
+ local unused = {}
139
+ for i , id in ipairs (base_avail .id ) do
140
+ if not loaded [id .value ] then
141
+ local version = base_avail .numeric_version [i ]
142
+ table.insert (unused , { id = id .value , version = version })
143
+ end
144
+ end
145
+
146
+ for _ , v in ipairs (unused ) do
147
+ local success , _ = make_available_mod (viewscreen , v .id , v .version )
148
+ if not success then
149
+ dfhack .printerr (' failed to show ' .. v .id .. ' in available list' )
150
+ end
151
+ end
96
152
end
97
153
98
154
local function get_active_modlist (viewscreen )
@@ -105,19 +161,28 @@ local function get_active_modlist(viewscreen)
105
161
return t
106
162
end
107
163
164
+ --- @return string[]
165
+ --- @return { id : string , new : string } []
108
166
local function swap_modlist (viewscreen , modlist )
109
- local current = get_active_modlist (viewscreen )
110
- for _ , v in ipairs (current ) do
111
- disable_mod (viewscreen , v .id , v .version )
112
- end
167
+ clear_mods (viewscreen )
113
168
114
169
local failures = {}
170
+ local changed = {}
171
+ local loaded = {}
115
172
for _ , v in ipairs (modlist ) do
116
- if not enable_mod (viewscreen , v .id , v .version ) then
173
+ local success , version = enable_mod (viewscreen , v .id , v .version )
174
+ if not success then
117
175
table.insert (failures , v .id )
176
+ else
177
+ if version then
178
+ table.insert (changed , { id = v .id , new = version })
179
+ end
180
+ loaded [v .id ] = true
118
181
end
119
182
end
120
- return failures
183
+
184
+ set_available_mods (viewscreen , loaded )
185
+ return failures , changed
121
186
end
122
187
123
188
---- ----------------
@@ -137,7 +202,7 @@ ModmanageMenu.ATTRS {
137
202
}
138
203
139
204
local function save_new_preset (preset_name )
140
- local viewscreen = get_newregion_viewscreen ()
205
+ local viewscreen = get_any_moddable_viewscreen ()
141
206
local modlist = get_active_modlist (viewscreen )
142
207
table.insert (presets_file .data , { name = preset_name , modlist = modlist })
143
208
presets_file :write ()
@@ -157,27 +222,17 @@ local function overwrite_preset(idx)
157
222
return
158
223
end
159
224
160
- local viewscreen = get_newregion_viewscreen ()
225
+ local viewscreen = get_any_moddable_viewscreen ()
161
226
local modlist = get_active_modlist (viewscreen )
162
227
presets_file .data [idx ].modlist = modlist
163
228
presets_file :write ()
164
229
end
165
230
166
- local function load_preset (idx , unset_default_on_failure )
167
- if idx > # presets_file .data then
168
- return
169
- end
231
+ local function prepare_warning (text , failed , changed , unset_default_on_failure )
232
+ if not failed and not changed then return end
170
233
171
- local viewscreen = get_newregion_viewscreen ()
172
- local modlist = presets_file .data [idx ].modlist
173
- local failures = swap_modlist (viewscreen , modlist )
174
-
175
- if # failures > 0 then
176
- local text = {}
234
+ if failed then
177
235
if unset_default_on_failure then
178
- presets_file .data [idx ].default = false
179
- presets_file :write ()
180
-
181
236
table.insert (text , {
182
237
text = ' Failed to load some mods from your default preset.' ,
183
238
pen = COLOR_LIGHTRED ,
@@ -193,19 +248,70 @@ local function load_preset(idx, unset_default_on_failure)
193
248
pen = COLOR_LIGHTRED ,
194
249
})
195
250
end
251
+ end
252
+
253
+ if failed and changed then
196
254
table.insert (text , NEWLINE )
197
- table.insert (text , NEWLINE )
198
- table.insert (text , ' Please re-create your preset with mods you currently have installed.' )
199
- table.insert (text , NEWLINE )
255
+ end
256
+
257
+ if changed then
258
+ table.insert (text , {
259
+ text = ' Some vanilla mods have been updated.' ,
260
+ pen = COLOR_LIGHTRED ,
261
+ })
262
+ end
263
+ table.insert (text , NEWLINE )
264
+ table.insert (text , ' Please re-create your preset with mods you currently have installed.' )
265
+ table.insert (text , NEWLINE )
266
+ table.insert (text , NEWLINE )
267
+ end
268
+
269
+ local function load_preset (idx , unset_default_on_failure )
270
+ if idx > # presets_file .data then
271
+ return
272
+ end
273
+
274
+ local viewscreen = get_any_moddable_viewscreen ()
275
+ local modlist = presets_file .data [idx ].modlist
276
+ local failures , changes = swap_modlist (viewscreen , modlist )
277
+ local text = {}
278
+
279
+ local failed = # failures > 0
280
+ local changed = # changes > 0
281
+
282
+ prepare_warning (text , failed , changed )
283
+ if failed and unset_default_on_failure then
284
+ presets_file .data [idx ].default = false
285
+ presets_file :write ()
286
+ end
287
+
288
+ if failed then
200
289
table.insert (text , ' Here are the mods that failed to load:' )
201
290
table.insert (text , NEWLINE )
202
291
table.insert (text , NEWLINE )
203
292
for _ , v in ipairs (failures ) do
204
293
table.insert (text , (' - %s' ):format (v ))
205
294
table.insert (text , NEWLINE )
206
295
end
296
+ end
297
+
298
+ if failed and changed then
299
+ table.insert (text , NEWLINE ) -- just to separate the sections
300
+ end
301
+
302
+ if changed then
303
+ table.insert (text , ' Here are the vanilla mods that have been updated:' )
304
+ table.insert (text , NEWLINE )
305
+ table.insert (text , NEWLINE )
306
+ for _ , v in ipairs (changes ) do
307
+ table.insert (text , (' - %s to %s' ):format (v .id , v .new ))
308
+ table.insert (text , NEWLINE )
309
+ end
310
+ end
311
+
312
+ if failed or changed then
207
313
dialogs .showMessage (" Warning" , text )
208
- end
314
+ end
209
315
end
210
316
211
317
local function find_preset_by_name (name )
@@ -573,7 +679,7 @@ ModmanageOverlay.ATTRS {
573
679
desc = " Adds a link to the mod selection screen for accessing the mod manager." ,
574
680
default_pos = { x = 5 , y =- 6 },
575
681
version = 2 ,
576
- viewscreens = { " new_region/Mods" },
682
+ viewscreens = { " new_region/Mods" , " new_arena/Mods " },
577
683
default_enabled = true ,
578
684
}
579
685
@@ -636,7 +742,7 @@ notification_timer_fn()
636
742
local default_applied = false
637
743
dfhack .onStateChange [GLOBAL_KEY ] = function (sc )
638
744
if sc == SC_VIEWSCREEN_CHANGED then
639
- local vs = get_newregion_viewscreen ()
745
+ local vs = get_any_moddable_viewscreen ()
640
746
if vs and not default_applied then
641
747
default_applied = true
642
748
for i , v in ipairs (presets_file .data ) do
0 commit comments