Confirmed. The fix is one line: the underground-view tool was missing its
exit() implementation.
Cause
tool_selector_t takes a different path when a tool reports itself as selected:
if (env_t::reselect_closes_tool && tool && tool->is_selected()) {
if (tool->exit(welt->get_active_player())) {
// Reset to query tool.
}
}
else {
welt->set_tool(tool, welt->get_active_player());
// This path calls init().
}
The important detail is that
is_selected() has two related but different meanings in the tool system.
The base implementation of
tool_t::is_selected() checks whether the tool is the player's currently selected tool:
welt->get_tool(...) == this
However,
tool_show_underground_t overrides it to report a global display state:
grund_t::underground_mode == ugm_all
The sequence was therefore:
- Underground mode is initially
ugm_none. - On the first click,
is_selected() returns false. - The selector calls
welt->set_tool(...). tool_show_underground_t::init() runs and changes the mode to
ugm_all.- On the second click,
is_selected() now returns true. - With
reselect_closes_tool enabled, the selector calls
exit() instead of
init(). - Since the tool did not override
exit(), the base
tool_t::exit() ran, returned true and changed no display state. init() was never called again, so underground mode remained enabled.
Fix
Tools that override
is_selected() to represent a global toggle state normally implement
exit() by invoking
init() again.
The neighbouring tools in the same file already follow that pattern:
class tool_show_grid_t : public tool_t {
bool is_selected() const OVERRIDE { return grund_t::show_grid; }
bool exit(player_t *s) OVERRIDE { return init(s); }
};
class tool_day_night_toggle_t : public tool_t {
bool exit(player_t *s) OVERRIDE { return init(s); }
};
The same pattern is used by the display toggles in
simtool.h,
tool_toggle_control_t, and the tools in
simtool-dialogs.h.
tool_show_underground_t was the exception.
The complete fix is therefore:
bool exit(player_t *s) OVERRIDE { return init(s); }
This also fixes sliced underground mode, configured with
K. Its
is_selected() reports
ugm_level, and its
init() already changes
ugm_level back to
ugm_none.
Why this does not disable underground view when another tool is selected
Adding
exit() might initially suggest that selecting another construction tool would now leave underground mode. That does not happen.
karte_t::local_set_tool() only calls
exit() on the outgoing selected tool, and a tool is recorded as selected only when its
init() returns true:
bool init_result = tool_in->init(player);
if (player && init_result && !tool_in->is_scripted()) {
sp_tool->exit(player);
selected_tool[player->get_player_nr()] = tool_in;
}
tool_show_underground_t::init() returns
needs_click.
For the underground-view modes configured by pak64 and pak128,
needs_click is false. Their menu configurations use:
U
K
I
D
The tool therefore changes the display mode but never becomes the player's persistent selected tool. As a result, selecting a road, query or construction tool does not call its
exit() and does not leave underground view.
I also inspected the
C mode. It is the only mode that would return true, but neither pak64 nor pak128 configures it. The class also has no specialised
work() implementation, so this mode appears unused or vestigial.
The new
exit() is therefore reached through the toolbar's reselect and right-click paths, matching the behaviour already used by tools such as
tool_show_grid_t.
Scope
No other behaviour or data is modified:
- no simulation changes;
- no savegame or serialisation changes;
- no network synchronisation changes;
- no translation changes;
- no rendering changes;
- no new persistent state.
is_init_keeps_game_state() already returns true, so this tool continues to be handled locally and is not queued as a network command.
Right-click behaviour
There is one intentional behaviour change worth documenting.
Previously, right-clicking the underground-view button while underground mode was active reset the active tool to the query tool but left underground view enabled.
With the new
exit(), right-clicking the button also disables underground view.
This matches the existing right-click behaviour of the dialog tools and
tool_toggle_control_t, so the underground-view tool now behaves consistently with comparable toggle tools.
If preserving the previous right-click behaviour is preferred, the fix would need to be placed specifically in the left-click reselect path instead. That would be a more specialised change than adding the missing standard
exit() implementation.
Testing
Tested in pak128 on a map containing an actual tunnel.
Pak128 contains two buttons using icon 44:
- the main-toolbar button,
Ctrl+U, configured as
K for sliced underground mode; - the display-submenu button configured as
U for full underground mode, which is the case reported in the bug.
With
Re-selecting a tool de-selects it enabled:
U correctly toggles full underground view on and off;K correctly toggles sliced underground view on and off;- selecting another construction or query tool while underground mode is active leaves the display mode unchanged;
- normal persistent tools still close when reselected.
The patch is a single-line addition to
tool_show_underground_t.
Fair observation — I used AI-assisted tooling to help organise the explanation, and it ended up much longer and more formal than necessary.
The diagnosis and patch were verified against the source and tested in pak128. The actual change is only:
bool exit(player_t *s) OVERRIDE { return init(s); }
tool_show_underground_t::is_selected() reports the underground display state, so when
reselect_closes_tool is enabled, the second click calls
exit() rather than
init(). The class was missing the same
exit() pattern already used by the neighbouring toggle tools.
I will keep future reports shorter.
The answer is not so easy, as there are more than one underground mode, needed some more renovation. Fixed in r12088.