Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Add reorderstops overlay #889

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion changelog.txt
Expand Up @@ -28,7 +28,10 @@ Template for new versions:

## New Tools
- `sync-windmills`: synchronize or randomize movement of active windmills
- `trackstop`: new overlay to allow changing track stop dump direction and friction and roller direction and speed after construction
- `trackstop`: provides 3 new overlays:
- trackstop: allow changing track stop dump direction and friction
- rollers: allow changing roller direction and speed
- reorderstops: reorder stops in hauling routes
Comment on lines +31 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this kind of formatting doesn't translate well to the final rendered changelog. each line should be independent. If you start each of them with `trackstop`: then they will get combined into a list similar to your intention here


## New Features
- `gui/design`: show selected dimensions next to the mouse cursor when designating with vanilla tools, for example when painting a burrow or designating digging
Expand Down
3 changes: 2 additions & 1 deletion docs/trackstop.rst
Expand Up @@ -5,6 +5,7 @@ trackstop
:summary: Add dynamic configuration options for track stops.
:tags: fort buildings interface

This script provides 2 overlays that are managed by the `overlay` framework. The script does nothing when executed.
This script provides 3 overlays that are managed by the `overlay` framework. The script does nothing when executed.
The trackstop overlay allows the player to change the friction and dump direction of a selected track stop after it has been constructed.
The rollers overlay allows the player to change the roller direction and speed of a selected roller after it has been constructed.
The reorderstops overlay allows the player to change the order of stops in a hauling route.
254 changes: 252 additions & 2 deletions trackstop.lua
Expand Up @@ -52,6 +52,31 @@ local DIRECTION_MAP = {

local DIRECTION_MAP_REVERSE = utils.invert(DIRECTION_MAP)

--[[
- swap 2 elements between different indexes in the same table like:
swap_elements({1, 2, 3}, 1, nil, 3) => {3, 2, 1}
- swap 2 elements at the specified indexes between 2 tables like:
swap_elements({1, 2, 3}, 1, {4, 5, 6}, 3) => {6, 2, 3} {4, 5, 1}
]]--
local function swap_elements(tbl1, index1, tbl2, index2)
tbl2 = tbl2 or tbl1
index2 = index2 or index1
tbl1[index1], tbl2[index2] = tbl2[index2], tbl1[index1]
return tbl1, tbl2
end

local function reset_guide_paths(conditions)
for _, condition in ipairs(conditions) do
local gpath = condition.guide_path

if gpath then
gpath.x:resize(0)
gpath.y:resize(0)
gpath.z:resize(0)
end
end
end

TrackStopOverlay = defclass(TrackStopOverlay, overlay.OverlayWidget)
TrackStopOverlay.ATTRS{
default_pos={x=-73, y=29},
Expand Down Expand Up @@ -120,7 +145,11 @@ function TrackStopOverlay:setDumpDirection(direction)
end

function TrackStopOverlay:render(dc)
local building = dfhack.gui.getSelectedBuilding()
local building = dfhack.gui.getSelectedBuilding(true)
if not building then
return
end

local friction = building.friction
local friction_cycle = self.subviews.friction

Expand Down Expand Up @@ -201,7 +230,10 @@ function RollerOverlay:setSpeed(speed)
end

function RollerOverlay:render(dc)
local building = dfhack.gui.getSelectedBuilding()
local building = dfhack.gui.getSelectedBuilding(true)
if not building then
return
end

self.subviews.direction:setOption(DIRECTION_MAP_REVERSE[building.direction])
self.subviews.speed:setOption(SPEED_MAP_REVERSE[building.speed])
Expand Down Expand Up @@ -236,7 +268,225 @@ function RollerOverlay:init()
}
end

ReorderStopsWindow = defclass(ReorderStopsWindow, widgets.Window)
ReorderStopsWindow.ATTRS {
frame={t=4,l=60,w=49, h=26},
frame_title='Reorder Stops',
resizable=true,
}

local SELECT_STOP_HINT = 'Select a stop to move'
local SELECT_ANOTHER_STOP_HINT = 'Select another stop to swap or same to cancel'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right click should also cancel out of a partially completed reorder operation. you can do this by handling keys._MOUSE_R in ReorderStopsWindow:onInput(keys) and returning true if you have handled the right click and don't want the window to close.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed



function ReorderStopsWindow:handleStopSelection(index, item)
-- Skip routes
if item.type == 'route' then return end

-- Select stop if none selected
if not self.first_selected_stop then
self:toggleStopSelection(item)
return
end

-- Swap stops
self:swapStops(index, item)

-- Reset stop properties
self:resetStopProperties(item)

self.first_selected_stop = nil
self:updateList()
end

function ReorderStopsWindow:toggleStopSelection(item)
if not self.first_selected_stop then
self.first_selected_stop = item
else
self.first_selected_stop = nil
end

self:updateList()
end

function ReorderStopsWindow:swapStops(index, second_selected_stop)
local hauling = df.global.plotinfo.hauling
local routes = hauling.routes
local view_stops = hauling.view_stops
local second_selected_stop_route = routes[second_selected_stop.route_index]
local second_selected_stop_index = second_selected_stop.stop_index
local same_route = self.first_selected_stop.route_index == second_selected_stop.route_index

if same_route then
swap_elements(second_selected_stop_route.stops, second_selected_stop_index, nil, self.first_selected_stop.stop_index)

-- find out what index the vehicle is currently at for this route, if there is one
local vehicle_index = nil
local hauling_route = df.hauling_route.get_vector()[second_selected_stop.route_index]

-- this vector will have 0 elements if there is no vehicle or 1 element if there is a vehicle
-- the element will be the index of the vehicle stop
for _, v in ipairs(hauling_route.vehicle_stops) do
vehicle_index = v
end

if vehicle_index == self.first_selected_stop.stop_index then
hauling_route.vehicle_stops[0] = second_selected_stop_index
elseif vehicle_index == second_selected_stop_index then
hauling_route.vehicle_stops[0] = self.first_selected_stop.stop_index
end
else
swap_elements(
routes[self.first_selected_stop.route_index].stops,
self.first_selected_stop.stop_index,
second_selected_stop_route.stops,
second_selected_stop_index
)
end

swap_elements(view_stops, self.first_selected_stop.list_position, nil, index - 1)
end

function ReorderStopsWindow:resetStopProperties(item)
local hauling = df.global.plotinfo.hauling
local routes = hauling.routes
local item_route = routes[item.route_index]
local same_route = self.first_selected_stop.route_index == item.route_index

for i, stop in ipairs(item_route.stops) do
stop.id = i + 1
reset_guide_paths(stop.conditions)
end

if not same_route and self.first_selected_stop then
for i, stop in ipairs(routes[self.first_selected_stop.route_index].stops) do
stop.id = i + 1
reset_guide_paths(stop.conditions)
end
end
end

function ReorderStopsWindow:init()
self.first_selected_stop = nil
self:addviews{
widgets.Label{
frame={t=0,l=0},
view_id='hint',
text=SELECT_STOP_HINT,
},
widgets.List{
view_id='routes',
frame={t=2,l=1},
choices={},
on_select=function(_, item)
if not item then return end
if item.type == 'stop' then
local item_pos = df.global.plotinfo.hauling.routes[item.route_index].stops[item.stop_index].pos
dfhack.gui.revealInDwarfmodeMap(item_pos, true, true)
end
end,
on_submit=function(index, item)
self:handleStopSelection(index, item)
end,
},
}

self:updateList()
end

function ReorderStopsWindow:updateList()
local routes = df.global.plotinfo.hauling.routes
local choices = {}
local list_position = 0

if self.first_selected_stop then
self.subviews.hint:setText(SELECT_ANOTHER_STOP_HINT)
else
self.subviews.hint:setText(SELECT_STOP_HINT)
end

for i, route in ipairs(routes) do
local stops = route.stops
local route_name = route.name

if route_name == '' then
route_name = 'Route ' .. route.id
end

table.insert(choices, {text=route_name, type='route', route_index=i, list_position=list_position})
list_position = list_position + 1

for j, stop in ipairs(stops) do
local stop_name = stop.name

if stop_name == '' then
stop_name = 'Stop ' .. stop.id
end

if self.first_selected_stop and self.first_selected_stop.list_position == list_position then
stop_name = '=> ' .. stop_name
end

stop_name = ' ' .. stop_name

table.insert(choices, {text=stop_name, type='stop', stop_index=j, route_index=i, list_position=list_position})
list_position = list_position + 1
end
end

self.subviews.routes:setChoices(choices)
end

function ReorderStopsWindow:onInput(keys)
if keys.LEAVESCREEN or keys._MOUSE_R then
if self.first_selected_stop then
self.first_selected_stop = nil
self:updateList()
return true
end
end

return ReorderStopsWindow.super.onInput(self, keys)
end

ReorderStopsModal = defclass(ReorderStopsModal, gui.ZScreenModal)

ReorderStopsModal.ATTRS = {
focus_path = 'ReorderStops',
}

function ReorderStopsModal:init()
self:addviews{ReorderStopsWindow{}}
end

function ReorderStopsModal:onDismiss()
df.global.game.main_interface.recenter_indicator_m.x = -30000
df.global.game.main_interface.recenter_indicator_m.y = -30000
df.global.game.main_interface.recenter_indicator_m.z = -30000
end

ReorderStopsOverlay = defclass(ReorderStopsOverlay, overlay.OverlayWidget)
ReorderStopsOverlay.ATTRS{
default_pos={x=6, y=6},
default_enabled=true,
viewscreens='dwarfmode/Hauling',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this specific enough, or should it disappear on hauling subscreens? We might need to extend Gui.cpp if this is the case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it's okay on all of them except them item selection one. Will fix.

frame={w=30, h=1},
frame_background=gui.CLEAR_PEN,
}

function ReorderStopsOverlay:init()
self:addviews{
widgets.TextButton{
frame={t=0, l=0},
label='DFHack reorder stops',
key='CUSTOM_CTRL_E',
on_activate=function() ReorderStopsModal{}:show() end,
},
}
end

OVERLAY_WIDGETS = {
trackstop=TrackStopOverlay,
rollers=RollerOverlay,
reorderstops=ReorderStopsOverlay,
}