Skip to content

Commit

Permalink
Merge pull request #5247 from myk002/myk_spectate
Browse files Browse the repository at this point in the history
initial rewrite of spectate
  • Loading branch information
myk002 authored Feb 3, 2025
2 parents 5f74a67 + c762108 commit 80a13e3
Show file tree
Hide file tree
Showing 11 changed files with 672 additions and 787 deletions.
3 changes: 3 additions & 0 deletions data/init/dfhack.keybindings.init
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ keybinding add Ctrl-T@dwarfmode/ViewSheets/UNIT|dwarfmode/ViewSheets/ITEM|dungeo
# quicksave
keybinding add Ctrl-Alt-S@dwarfmode quicksave

# toggle spectate
keybinding add Ctrl-Shift-S@dwarfmode/Default "spectate toggle"

# designate the whole vein for digging
keybinding add Ctrl-V@dwarfmode digv
keybinding add Ctrl-Shift-V@dwarfmode "digv x"
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,15 @@ Template for new versions:
## New Tools

## New Features
- `spectate`: can now specify number of seconds (in real time) before switching to follow a new unit unit
- `spectate`: new "cinematic-action" mode that dynamically speeds up perspective switches based on intensity of conflict
- `spectate`: new global keybinding for toggling spectate mode: Ctrl-Shift-S

## Fixes
- `spectate`: don't allow temporarily modified announcement settings to be written to disk when "auto-unpause" mode is enabled

## Misc Improvements
- `spectate`: player-set configuration is now stored globally instead of per-fort

## Documentation

Expand Down
158 changes: 122 additions & 36 deletions docs/plugins/spectate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,147 @@ spectate
========

.. dfhack-tool::
:summary: Automatically follow productive dwarves.
:tags: fort interface
:summary: Automated spectator mode.
:tags: fort inspection interface

This tool is for those who like to watch their dwarves go about their business.

When enabled, `spectate` will lock the camera to following the dwarves
scurrying around your fort. Every once in a while, it will automatically switch
to following a different dwarf. It can also switch to following animals,
hostiles, or visiting units. You can switch to the next target (or a previous
target) immediately with the left/right arrow keys.

`spectate` will disengage and turn itself off when you move the map, just like
the vanilla follow mechanic. It will also disengage immediately if you open the
squads menu for military action.

It can also annotate your dwarves on the map with their name, job, and other
information, either as floating tooltips or in a panel that comes up when you
hover the mouse over a target.

Run `gui/spectate` to configure the plugin's settings.

Settings are saved globally, so your preferences for `spectate` and its
overlays will apply to all forts, not just the currently loaded one. Follow
mode is automatically disabled when you load a fort so you can get your
bearings before re-enabling.

Usage
-----

::

enable spectate
spectate
spectate [status]
spectate toggle
spectate set <setting> <value>
spectate enable|disable <feature>

When enabled, the plugin will lock the camera to following the dwarves
scurrying around your fort. Every once in a while, it will automatically switch
to following a different dwarf, preferring dwarves on z-levels with the highest
job activity.

If you have the ``auto-disengage`` feature disabled, you can switch to a new
dwarf immediately by hitting one of the map movement keys (``wasd`` by
default). To stop following dwarves, bring up `gui/launcher` and run
``disable spectate``.

Changes to settings will be saved with your fort, but if `spectate` is enabled
when you save the fort, it will disenable itself when you load so you can get
your bearings before re-enabling follow mode with ``enable spectate`` again.
spectate overlay <name> enable|disable

Examples
--------

``enable spectate``
Starting following dwarves and observing life in your fort.
Start following dwarves and observing life in your fort.

``spectate toggle``
Toggle the plugin on or off. Intended for use with a keybinding. The
default is Ctrl-Shift-S.

``spectate``
The plugin reports its configured status.

``spectate enable auto-unpause``
Enable the spectate plugin to automatically dismiss pause events caused
by the game. Siege events are one example of such a game event.
``spectate set auto-unpause true``
Configure `spectate` to automatically dismiss popups and pause events, like
siege announcements.

``spectate set tick-threshold 1000``
Set the tick interval between camera changes back to its default value.
``spectate set follow-seconds 30``
Configure `spectate` to switch targets every 30 seconds when in follow mode.

Features
--------
:auto-unpause: Toggle auto-dismissal of game pause events. (default: disabled)
:auto-disengage: Toggle auto-disengagement of plugin through player
intervention while unpaused. (default: disabled)
:animals: Toggle whether to sometimes follow animals. (default: disabled)
:hostiles: Toggle whether to sometimes follow hostiles (eg. undead,
titans, invaders, etc.) (default: disabled)
:visiting: Toggle whether to sometimes follow visiting units (eg.
diplomats)
``spectate overlay follow enable``
Show informative tooltips that follow each unit on the map.

Settings
--------
:tick-threshold: Set the plugin's tick interval for changing the followed
dwarf. (default: 1000)

``auto-disengage`` (default: enabled)
Toggle automatically disabling the plugin when the player moves the map or
opens the squad panel. If this is disabled, you will need to manually
disable the plugin to turn off follow mode.

``auto-unpause`` (default: disabled)
Toggle auto-dismissal of announcements that pause the game, like sieges,
forgotten beasts, etc.

``cinematic-action`` (default: enabled)
Toggle whether to switch targets more rapidly when there is conflict.

``follow-seconds`` (default: 10)
Set the time interval for changing the followed unit. The interval does not
include time that the game is paused.

``include-animals`` (default: disabled)
Toggle whether to sometimes follow fort animals.

``include-hostiles`` (default: disabled)
Toggle whether to sometimes follow hostiles (eg. undead, titans, invaders,
etc.)

``include-visitors`` (default: disabled)
Toggle whether to sometimes follow visiting units, like diplomats.

``include-wildlife`` (default: disabled)
Toggle whether to sometimes follow wildlife.

``prefer-conflict`` (default: enabled)
Toggle whether to prefer following units in active conflict.

``prefer-new-arrivals`` (default: enabled)
Toggle whether to prefer following (non-siege) units that have newly
arrived on the map.

``tooltip-follow-job`` (default: enabled)
If the ``spectate.follow`` overlay is enabled, toggle whether to show the
job of the dwarf in the tooltip.

``tooltip-follow-name`` (default: enabled)
If the ``spectate.follow`` overlay is enabled, toggle whether to show the
name of the dwarf in the tooltip.

``tooltip-follow-stress`` (default: enabled)
If the ``spectate.follow`` overlay is enabled, toggle whether to show the
happiness level (stress) of the dwarf in the tooltip.

``tooltip-hover-job`` (default: enabled)
If the ``spectate.follow`` overlay is enabled, toggle whether to show the
job of the dwarf in the hover panel.

``tooltip-hover-name`` (default: enabled)
If the ``spectate.follow`` overlay is enabled, toggle whether to show the
name of the dwarf in the hover panel.

``tooltip-hover-stress`` (default: enabled)
If the ``spectate.follow`` overlay is enabled, toggle whether to show the
happiness level (stress) of the dwarf in the hover panel.

Overlays
--------

``spectate`` provides two overlays via the `overlay` framework to add
information and functionality to the main map. These overlays can be controlled
via the ``spectate overlay`` command or the ``Overlays`` tab in
`gui/control-panel`.

The information displayed by these overlays can be configured via the
``spectate set`` command or the `gui/spectate` interface.

``spectate.follow``
Show informative tooltips that follow each unit on the map. You can enable
this overlay by running ``spectate overlay follow enable`` or,
equivalently, ``overlay enable spectate.follow``.

``spectate.hover``
Show a popup panel with selected information when your mouse cursor hovers
over a unit. You can enable this overlay by running
``spectate overlay hover enable`` or, equivalently,
``overlay enable spectate.hover``.
2 changes: 1 addition & 1 deletion plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ if(BUILD_SUPPORTED)
#dfhack_plugin(siege-engine siege-engine.cpp LINK_LIBRARIES lua)
dfhack_plugin(sort sort.cpp LINK_LIBRARIES lua)
#dfhack_plugin(steam-engine steam-engine.cpp)
add_subdirectory(spectate)
dfhack_plugin(spectate spectate.cpp LINK_LIBRARIES lua)
#dfhack_plugin(stockflow stockflow.cpp LINK_LIBRARIES lua)
add_subdirectory(stockpiles)
dfhack_plugin(stocks stocks.cpp LINK_LIBRARIES lua)
Expand Down
149 changes: 149 additions & 0 deletions plugins/lua/spectate.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
local _ENV = mkmodule('plugins.spectate')

local argparse = require('argparse')
local json = require('json')
local overlay = require('plugins.overlay')
local utils = require('utils')

-- settings starting with 'tooltip-' are not passed to the C++ plugin
local lua_only_settings_prefix = 'tooltip-'

local function get_default_state()
return {
['auto-disengage']=true,
['auto-unpause']=false,
['cinematic-action']=true,
['follow-seconds']=10,
['include-animals']=false,
['include-hostiles']=false,
['include-visitors']=false,
['include-wildlife']=false,
['prefer-conflict']=true,
['prefer-new-arrivals']=true,
['tooltip-follow-job']=true,
['tooltip-follow-name']=true,
['tooltip-follow-stress']=true,
['tooltip-hover-job']=true,
['tooltip-hover-name']=true,
['tooltip-hover-stress']=true,
}
end

local function load_state()
local state = get_default_state()
local config = json.open('dfhack-config/spectate.json')
for key in pairs(config.data) do
if state[key] == nil then
config.data[key] = nil
end
end
utils.assign(state, config.data)
config.data = state
return config
end

local config = load_state()

function refresh_cpp_config()
for name,value in pairs(config.data) do
if not name:startswith(lua_only_settings_prefix) then
if type(value) == 'boolean' then
value = value and 1 or 0
end
spectate_setSetting(name, value)
end
end
end

-----------------------------
-- commandline interface

local function print_status()
print('spectate is:', isEnabled() and 'enabled' or 'disabled')
print()
print('settings:')
for key, value in pairs(config.data) do
print(' ' .. key .. ': ' .. tostring(value))
end
end

local function do_toggle()
if isEnabled() then
dfhack.run_command('disable', 'spectate')
else
dfhack.run_command('enable', 'spectate')
end
end

local function set_setting(key, value)
if config.data[key] == nil then
qerror('unknown setting: ' .. key)
end
if key == 'follow-seconds' then
value = argparse.positiveInt(value, 'follow-seconds')
else
value = argparse.boolean(value, key)
end
config.data[key] = value
config:write()
if not key:startswith(lua_only_settings_prefix) then
if type(value) == 'boolean' then
value = value and 1 or 0
end
spectate_setSetting(key, value)
end
end

local function set_overlay(name, value)
if not name:startswith('spectate.') then
name = 'spectate.' .. name
end
if name ~= 'spectate.follow' and name ~= 'spectate.hover' then
qerror('unknown overlay: ' .. name)
end
value = argparse.boolean(value, name)
dfhack.run_command('overlay', value and 'enable' or 'disable', name)
end

function parse_commandline(args)
local command = table.remove(args, 1)
if not command or command == 'status' then
print_status()
elseif command == 'toggle' then
do_toggle()
elseif command == 'set' then
set_setting(args[1], args[2])
elseif command == 'overlay' then
set_overlay(args[1], args[2])
else
return false
end

return true
end

-----------------------------
-- overlays

FollowOverlay = defclass(FollowOverlay, overlay.OverlayWidget)
FollowOverlay.ATTRS{
desc='Adds info tooltips that follow units on the map.',
default_pos={x=1,y=1},
fullscreen=true,
viewscreens='dwarfmode/Default',
}

HoverOverlay = defclass(HoverOverlay, overlay.OverlayWidget)
HoverOverlay.ATTRS{
desc='Shows info popup when hovering the mouse over units on the map.',
default_pos={x=1,y=1},
fullscreen=true,
viewscreens='dwarfmode/Default',
}

OVERLAY_WIDGETS = {
follow=FollowOverlay,
hover=HoverOverlay,
}

return _ENV
Loading

0 comments on commit 80a13e3

Please sign in to comment.