@@ -10,6 +10,7 @@ local utils = require('utils')
1010
1111local presets_file = json .open (" dfhack-config/mod-manager.json" )
1212local GLOBAL_KEY = ' mod-manager'
13+ local INSTALLED_MODS_PATH = ' data/installed_mods/'
1314
1415-- get_newregion_viewscreen and get_modlist_fields are declared as global functions
1516-- so external tools can call them to get the DF mod list
@@ -401,6 +402,120 @@ function ModmanageScreen:init()
401402 }
402403end
403404
405+ ModlistMenu = defclass (ModlistMenu , widgets .Window )
406+ ModlistMenu .ATTRS {
407+ view_id = " modlist_menu" ,
408+ frame_title = " Active Modlist" ,
409+ frame_style = gui .WINDOW_FRAME ,
410+
411+ resize_min = { w = 30 , h = 15 },
412+ frame = { w = 40 , t = 10 , b = 15 },
413+
414+ resizable = true ,
415+ autoarrange_subviews = false ,
416+ }
417+
418+ local function get_mod_id_and_version (path )
419+ local idfile = path .. ' /info.txt'
420+ local ok , lines = pcall (io.lines , idfile )
421+ if not ok then return end
422+ local id , version
423+ for line in lines do
424+ if not id then
425+ _ ,_ ,id = line :find (' ^%[ID:([^%]]+)%]' )
426+ end
427+ if not version then
428+ -- note this doesn't include the closing brace since some people put
429+ -- non-number characters in here, and DF only reads the leading digits
430+ -- as the numeric version
431+ _ ,_ ,version = line :find (' ^%[NUMERIC_VERSION:(%d+)' )
432+ end
433+ -- note that we do *not* want to break out of this loop early since
434+ -- lines has to hit EOF to close the file
435+ end
436+ return id , version
437+ end
438+
439+ local function add_mod_paths (mod_paths , id , base_path , subdir )
440+ local sep = base_path :endswith (' /' ) and ' ' or ' /'
441+ local path = (' %s%s%s' ):format (base_path , sep , subdir )
442+ if dfhack .filesystem .isdir (path ) then
443+ table.insert (mod_paths , {id = id , path = path })
444+ end
445+ end
446+
447+ local function getWorldModlist ()
448+ -- ordered map of mod id -> {handled=bool, versions=map of version -> path}
449+ local mods = utils .OrderedTable ()
450+ local mod_paths = {}
451+
452+ -- if a world is loaded, process active mods first, and lock to active version
453+ if dfhack .isWorldLoaded () then
454+ for _ ,path in ipairs (df .global .world .object_loader .object_load_order_src_dir ) do
455+ path = tostring (path .value )
456+ -- skip vanilla "mods"
457+ if not path :startswith (INSTALLED_MODS_PATH ) then goto continue end
458+ local id = get_mod_id_and_version (path )
459+ if not id then goto continue end
460+ mods [id ] = {handled = true }
461+ add_mod_paths (mod_paths , id , path , ' .' )
462+ :: continue::
463+ end
464+ local modlist = {}
465+ for _ ,mod in ipairs (mod_paths ) do
466+ table.insert (modlist ,mod .id )
467+ end
468+ return modlist
469+ end
470+ qerror (' No world is loaded' )
471+ end
472+
473+ function ModlistMenu :init ()
474+ local modlist = widgets .List {
475+ view_id = ' modlist' ,
476+ frame = {t = 3 },
477+ choices = getWorldModlist ()
478+ }
479+ self :addviews {
480+ widgets .Label {
481+ frame = { l = 0 , t = 0 },
482+ text = {' Active mods:' },
483+ },
484+ widgets .HotkeyLabel {
485+ view_id = ' copy' ,
486+ frame = {t = 1 , r = 1 },
487+ label = ' Copy modlist to clipboard' ,
488+ text_pen = COLOR_YELLOW ,
489+ auto_width = true ,
490+ on_activate = function ()
491+ local mods = ' '
492+ for _ ,mod in ipairs (getWorldModlist ()) do
493+ mods = (mods == ' ' and mod ) or (mods .. ' , ' .. mod )
494+ end
495+ dfhack .internal .setClipboardTextCp437 (mods )
496+ end ,
497+ enabled = function () return # modlist :getChoices () > 0 end ,
498+ },
499+ modlist
500+ }
501+ end
502+
503+
504+ ModlistScreen = defclass (ModlistScreen , gui .ZScreen )
505+ ModlistScreen .ATTRS {
506+ focus_path = " modlist" ,
507+ }
508+
509+ function ModlistScreen :init ()
510+ self :addviews {
511+ ModlistMenu {}
512+ }
513+ end
514+
515+ function ModlistScreen :onDismiss ()
516+ view = nil
517+ end
518+
404519ModmanageOverlay = defclass (ModmanageOverlay , overlay .OverlayWidget )
405520ModmanageOverlay .ATTRS {
406521 frame = { w = 16 , h = 3 },
496611
497612-- TODO: when invoked as a command, should show information on which mods are loaded
498613-- and give the player the option to export the list (or at least copy it to the clipboard)
614+
615+ view = view and view :raise () or ModlistScreen {}:show ()
0 commit comments