Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Template for new versions:
## New Tools

## New Features
- `gui/mod-manager`: when run in a loaded world, shows a list of active mods -- click to export the list to the clipboard for easy sharing or posting

## Fixes
- `starvingdead`: properly restore to correct enabled state when loading a new game that is different from the first game loaded in this session
Expand Down
24 changes: 21 additions & 3 deletions docs/gui/mod-manager.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,33 @@ gui/mod-manager
===============

.. dfhack-tool::
:summary: Save and restore lists of active mods.
:summary: Manange your active mods.
:tags: dfhack interface

Adds an optional overlay to the mod list screen that allows you to save and
load mod list presets, as well as set a default mod list preset for new worlds.
When run with a world loaded, shows a list of active mods. You can copy the
list to the system clipboard for easy sharing or posting.

Usage
-----

::

gui/mod-manager

Overlay
-------

This tool also provides two overlays that are managed by the `overlay`
framework.

gui/mod-manager.button
~~~~~~~~~~~~~~~~~~~~~~

Adds a widget to the mod list screen that allows you to save and load mod list
presets. You can also set a default mod list preset for new worlds so you don't
have to manualy re-select the same mods every time you generate a world.

gui/mod-manager.notification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Displays a message when a mod preset has been auto-applied.
165 changes: 159 additions & 6 deletions gui/mod-manager.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
-- Save and restore lists of active mods.
-- Show, save, and restore lists of active mods.
--@ module = true

local overlay = require('plugins.overlay')
local gui = require('gui')
local widgets = require('gui.widgets')
local dialogs = require('gui.dialogs')
local gui = require('gui')
local json = require('json')
local overlay = require('plugins.overlay')
local scriptmanager = require('script-manager')
local utils = require('utils')
local widgets = require('gui.widgets')

local presets_file = json.open("dfhack-config/mod-manager.json")
local GLOBAL_KEY = 'mod-manager'
Expand Down Expand Up @@ -119,6 +120,9 @@ local function swap_modlist(viewscreen, modlist)
return failures
end

--------------------
-- ModmanageMenu

ModmanageMenu = defclass(ModmanageMenu, widgets.Window)
ModmanageMenu.ATTRS {
view_id = "modman_menu",
Expand Down Expand Up @@ -389,6 +393,9 @@ function ModmanageMenu:init()
}
end

--------------------
-- ModmanageScreen

ModmanageScreen = defclass(ModmanageScreen, gui.ZScreen)
ModmanageScreen.ATTRS {
focus_path = "mod-manager",
Expand All @@ -401,6 +408,149 @@ function ModmanageScreen:init()
}
end

--------------------
-- ModlistWindow

ModlistWindow = defclass(ModlistWindow, widgets.Window)
ModlistWindow.ATTRS{
frame_title="Active Mods",
frame={w=55, h=20},
resizable=true,
}

local function get_num_vanilla_mods()
local count = 0
for _,mod in ipairs(scriptmanager.get_active_mods()) do
if mod.vanilla then
count = count + 1
end
end
return count
end

local function get_num_non_vanilla_mods()
local count = 0
for _,mod in ipairs(scriptmanager.get_active_mods()) do
if not mod.vanilla then
count = count + 1
end
end
return count
end

function ModlistWindow:init()
self:addviews{
widgets.CycleHotkeyLabel{
view_id='vanilla',
frame={l=0, t=0, w=24},
key='CUSTOM_V',
label='Vanilla mods:',
options={
{label='Include', value=true, pen=COLOR_LIGHTBLUE},
{label='Exclude', value=false, pen=COLOR_LIGHTRED},
},
initial_option=false,
on_change=function() self:refresh_list() end,
},
widgets.HotkeyLabel{
frame={t=0, r=0},
label='Copy list to clipboard',
text_pen=COLOR_YELLOW,
auto_width=true,
on_activate=function()
local text = {}
for _,choice in ipairs(self.subviews.list:getChoices()) do
table.insert(text, choice.export_text)
end
dfhack.internal.setClipboardTextCp437Multiline(table.concat(text, NEWLINE))
end,
enabled=function() return #self.subviews.list:getChoices() > 0 end,
},
widgets.Divider{
frame={t=2, h=1},
frame_style=gui.FRAME_THIN,
frame_style_l=false,
frame_style_r=false,
},
widgets.Label{
frame={l=0, t=3},
text={
'Load',
NEWLINE,
'order',
},
},
widgets.Label{
frame={l=7, t=4},
text='Mod',
},
widgets.List{
view_id='list',
frame={t=6, b=2},
},
widgets.Label{
frame={l=0, b=0},
text={
{text=('%d'):format(get_num_vanilla_mods()), pen=COLOR_LIGHTBLUE},
' vanilla mods',
{text=function() return self.subviews.vanilla:getOptionValue() and '' or ' (hidden)' end},
', ',
{text=('%d'):format(get_num_non_vanilla_mods()), pen=COLOR_BROWN},
' non-vanilla mods',
},
},
}

self:refresh_list()
end

function ModlistWindow:refresh_list()
local include_vanilla = self.subviews.vanilla:getOptionValue()

local choices = {}
for idx,mod in ipairs(scriptmanager.get_active_mods()) do
if not include_vanilla and mod.vanilla then goto continue end
local steam_id = scriptmanager.get_mod_info_metadata(mod.path, 'STEAM_FILE_ID').STEAM_FILE_ID
local url = steam_id and (': https://steamcommunity.com/sharedfiles/filedetails/?id=%s'):format(steam_id) or ''
table.insert(choices, {
text={
{text=idx, width=2, rjustify=true},
') ',
{text=mod.name, gap=3},
' (',
{text=mod.version, pen=COLOR_LIGHTGREEN},
')',
},
data=mod,
export_text=('- %s (%s)%s'):format(mod.name, mod.version, url),
})
::continue::
end

self.subviews.list:setChoices(choices)
end

--------------------
-- ModlistScreen

ModlistScreen = defclass(ModlistScreen, gui.ZScreen)
ModlistScreen.ATTRS{
focus_path="mod-manager",
}

function ModlistScreen:init()
self:addviews{
ModlistWindow{}
}
end

function ModlistScreen:onDismiss()
view = nil
end

--------------------
-- Overlays

ModmanageOverlay = defclass(ModmanageOverlay, overlay.OverlayWidget)
ModmanageOverlay.ATTRS {
frame = { w=16, h=3 },
Expand Down Expand Up @@ -494,5 +644,8 @@ if dfhack_flags.module then
return
end

-- TODO: when invoked as a command, should show information on which mods are loaded
-- and give the player the option to export the list (or at least copy it to the clipboard)
if not dfhack.isWorldLoaded() then
qerror("Please load a game before using the mod manager to see active mods.")
end

view = view and view:raise() or ModlistScreen{}:show()
Loading