Skip to content
Open
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
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ Template for new versions:

## New Features

- `gui/liquids`: "Drag" and "Click" paint modes have a brush radius option, and can toggle between square and round brushes

## Fixes

- `gui/liquids`: Match initial paint mode with what's shown

## Misc Improvements

- `gui/export-world-map`: export of bridge and road materials and more precise bridge coordinates
Expand Down
223 changes: 200 additions & 23 deletions gui/liquids.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ local SpawnLiquidPaintMode = {
AREA = 3,
}

local SpawnLiquidPaintShape = {
SQUARE = 1,
ROUND = 2,
}

local SpawnLiquidCursor = {
[df.tile_liquid.Water] = dfhack.screen.findGraphicsTile('MINING_INDICATORS', 0, 0),
[df.tile_liquid.Magma] = dfhack.screen.findGraphicsTile('MINING_INDICATORS', 1, 0),
Expand All @@ -30,11 +35,35 @@ SpawnLiquid.ATTRS {
frame={b = 4, r = 4, w = 50, h = 12},
}


function SpawnLiquid:preUpdateLayout()
local level_change_position = 1
local brush_mode_position = 2

if self:useBrushSize() then
level_change_position = level_change_position + 1
brush_mode_position = brush_mode_position + 1
end

for k, v in pairs(self.subviews) do
if type(k) == "string" then
if k:startswith("level_increase") or k:startswith("level_decrease") then
Comment on lines +49 to +50

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

hmm, okay, why are we doing this?

A: seems to be because we we want to adjust both level_decrease and level_decrease_glyph, an dthe same for level_increase and level_increase_glyph.

not sure I like this. I guess it's no worse than the other layout elements, which are also hardcoded.

I'm tempted to say 'use a callback to calculate this dynamically' but that is kind of advanced.

so the effect of this code is to hide the "Decrease radius" and "Increase radius" labels if the Paint mode is Area.
this is opposed to just graying out both labels, the way that "Increase level" is grayed out when the liquid level is 7/7.

the effect here is that UI elements appear and disappear as modes change.

(later) Okay, I've been thinking about this off-and-on for most of a day. I don't like hiding those labels. I strongly believe menu-like UI elements should gray out when they are not valid input.

Important

Please change the label code so that the Increase radius and Decrease radius labels gray out when not valid.

I think that means this entire function can be deleted.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i'm going to agree with Silas on this point: i generally don't like UI elements that appear and disappear as modes change. there are times when this is appropriate, but i don't think this is one of them. making those controls inactive, instead of hiding them, is less intrusive to the user's experience

v.frame.b = level_change_position
elseif k:startswith("brush") then
v.frame.b = brush_mode_position
end
end
end
end


function SpawnLiquid:init()
self.type = df.tile_liquid.Water
self.mode = SpawnLiquidMode.SET
self.level = 3
self.paint_mode = SpawnLiquidPaintMode.AREA
self.paint_mode = SpawnLiquidPaintMode.DRAG
self.paint_shape = SpawnLiquidPaintShape.SQUARE
self.brush_radius = 0
self.tile = SpawnLiquidCursor[self.type]

self:addviews{
Expand All @@ -43,6 +72,7 @@ function SpawnLiquid:init()
text = {{ text = self:callback('getLabel') }}
},
widgets.HotkeyLabel{
view_id = "level_decrease",
frame = {l = 0, b = 1},
label = 'Decrease level',
auto_width = true,
Expand All @@ -51,11 +81,13 @@ function SpawnLiquid:init()
disabled = function() return self.level == 1 end
},
widgets.Label{
view_id = "level_decrease_glyph",
frame = { l = 0, b = 1, w = 1},
text_pen=COLOR_LIGHTGREEN,
text=string.char(27),
},
widgets.HotkeyLabel{
view_id = "level_increase",
frame = { l = 19, b = 1},
label = 'Increase level',
auto_width = true,
Expand All @@ -64,12 +96,44 @@ function SpawnLiquid:init()
disabled = function() return self.level == 7 end
},
widgets.Label{
view_id = "level_increase_glyph",
frame = { l = 19, b = 1, w = 1},
text_pen=COLOR_LIGHTGREEN,
text=string.char(26),
},
widgets.HotkeyLabel{
frame = {l = 0, b = 1},
label = 'Decrease radius',
auto_width = true,
key = 'KEYBOARD_CURSOR_DOWN',
on_activate = self:callback('decreaseBrushSize'),
visible = self:callback("useBrushSize"),
disabled = function() return self.brush_radius <= 0 end
},
widgets.Label{
frame = { l = 0, b = 1, w = 1},
text_pen=COLOR_LIGHTGREEN,
text=string.char(25),
visible = self:callback("useBrushSize"),
},
widgets.HotkeyLabel{
frame = { l = 19, b = 1},
label = 'Increase radius',
auto_width = true,
key = 'KEYBOARD_CURSOR_UP',
on_activate = self:callback('increaseBrushSize'),
visible = self:callback("useBrushSize"),
disabled = function() return self.brush_radius >= 10 end
},
widgets.Label{
frame = { l = 19, b = 1, w = 1},
text_pen=COLOR_LIGHTGREEN,
text=string.char(24),
visible = self:callback("useBrushSize"),
},
widgets.CycleHotkeyLabel{
frame = {l = 0, b = 2},
view_id = "brush",
frame = {l = 0, b = 3},
label = 'Brush:',
auto_width = true,
key = 'CUSTOM_Q',
Expand All @@ -95,11 +159,30 @@ function SpawnLiquid:init()
{ label = "Drag ", value = SpawnLiquidPaintMode.DRAG, pen = COLOR_WHITE },
},
initial_option = SpawnLiquidPaintMode.DRAG,
on_change = function(new, _) self.paint_mode = new end,
on_change = function(new, _)
self.paint_mode = new
self:updateLayout()
end,
},
widgets.CycleHotkeyLabel{
view_id = 'mode1',
frame = {l = 18, b = 2},
frame = {l = 21, b = 0},
label = 'Paint Shape:',
auto_width = true,
key = 'CUSTOM_V',
options = {
{ label = "Square", value = SpawnLiquidPaintShape.SQUARE, pen = COLOR_WHITE },
{ label = "Round", value = SpawnLiquidPaintShape.ROUND, pen = COLOR_WHITE },
},
initial_option = SpawnLiquidPaintMode.SQUARE,
visible = self:callback("useBrushSize"),
on_change = function(new, _)
self.paint_shape = new
self:updateLayout()
end,
},
widgets.CycleHotkeyLabel{
view_id = 'brush_mode1',
frame = {l = 18, b = 3},
label = 'Mode:',
auto_width = true,
key = 'CUSTOM_X',
Expand All @@ -112,13 +195,13 @@ function SpawnLiquid:init()
initial_option = SpawnLiquidMode.SET,
on_change = function(new, _)
self.mode = new
self.subviews.mode2:setOption(new)
self.subviews.brush_mode2:setOption(new)
end,
visible = function() return self.type ~= df.tiletype.RiverSource end
},
widgets.CycleHotkeyLabel{
view_id = 'mode2',
frame = {l = 18, b = 2},
view_id = 'brush_mode2',
frame = {l = 18, b = 3},
label = 'Mode:',
auto_width = true,
key = 'CUSTOM_X',
Expand All @@ -128,7 +211,7 @@ function SpawnLiquid:init()
},
on_change = function(new, _)
self.mode = new
self.subviews.mode1:setOption(new)
self.subviews.brush_mode1:setOption(new)
end,
visible = function() return self.type == df.tiletype.RiverSource end
},
Expand All @@ -137,9 +220,27 @@ end

-- TODO: More reactive label dependant on options selected.
function SpawnLiquid:getLabel()
return ([[Click on a tile to spawn a %s/7 level of %s]]):format(
local tile_string = "a tile"
local liquid_string

if self.type == 0 then
liquid_string = "Water"
elseif self.type == 1 then
liquid_string = "Magma"
else
liquid_string = "River"
end

if self:useBrushSize() and self.brush_radius > 0 then
local brush_size = self.brush_radius*2
tile_string = string.format("%dx%d tiles", brush_size, brush_size)
end


return ([[Click to spawn %s with %s/7 %s]]):format(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Tip

There's no need for this to use [[longstring]] marks, "double-quotes" are preferred.

Suggested change
return ([[Click to spawn %s with %s/7 %s]]):format(
return ("Click to spawn %s with %s/7 %s"):format(

tile_string,
self.level,
self.type == 0 and "Water" or self.type == 1 and "Magma" or "River"
liquid_string
)
end

Expand All @@ -163,6 +264,15 @@ function SpawnLiquid:decreaseLiquidLevel()
self.level = math.max(self.level - 1, 1)
end

function SpawnLiquid:increaseBrushSize()
self.brush_radius = math.min(self.brush_radius + 1, 10)
end

function SpawnLiquid:decreaseBrushSize()
self.brush_radius = math.max(self.brush_radius - 1, 0)
end


Comment on lines +267 to +275

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

setting the brush radius to 1, 2, 3, 4, etc. results in brush sizes of 1x1, 3x3, 5x5, 7x7, etc.
radius 10 yields a brush of 21x21, I think.
it might be useful to increment it by 0.5, which would also yield brush sizes of 2x2, 4x4, 6x6, etc.
the minimum would still have to be 1

local function isFlowPassable(pos)
local tt = dfhack.maps.getTileType(pos)
local tile = dfhack.maps.getTileFlags(pos)
Expand Down Expand Up @@ -203,10 +313,61 @@ function SpawnLiquid:spawn(pos)
end
end

function SpawnLiquid:paintLiquid(pos, bounds)
if not bounds then
local start_position, end_position = self:getBrushSizeBounds(pos)

bounds = self:getBounds(start_position, end_position)
end

for x = bounds.x1, bounds.x2 do
for y = bounds.y1, bounds.y2 do
if self:inBounds(pos, {x=x, y=y}) then
self:spawn({x=x, y=y, z=pos.z})
end
end
end
end


function SpawnLiquid:getPen()
return self.type == df.tile_liquid.Water and COLOR_BLUE or COLOR_RED, "X", self.tile
end

function SpawnLiquid:inBounds(mouse_pos, pos)
if not self:useBrushSize() or self.paint_shape ~= SpawnLiquidPaintShape.ROUND then
return true
end

local diff = {
x=mouse_pos.x - pos.x,
y=mouse_pos.y - pos.y
}
local dist = diff.x*diff.x+diff.y*diff.y

return dist <= self.brush_radius*self.brush_radius
end


function SpawnLiquid:getBrushSizeBounds(mouse_pos)
local start_position = xyz2pos(
mouse_pos.x - self.brush_radius,
mouse_pos.y - self.brush_radius,
mouse_pos.z)

local end_position = xyz2pos(
mouse_pos.x + self.brush_radius,
mouse_pos.y + self.brush_radius,
mouse_pos.z)

return start_position, end_position
end


function SpawnLiquid:useBrushSize()
return self.paint_mode ~= SpawnLiquidPaintMode.AREA
end

function SpawnLiquid:getBounds(start_position, end_position)
return {
x1=math.min(start_position.x, end_position.x),
Expand All @@ -221,22 +382,38 @@ end
function SpawnLiquid:onRenderFrame(dc, rect)
SpawnLiquid.super.onRenderFrame(self, dc, rect)

if self.is_dragging and df.global.enabler.mouse_lbut_down == 0 then
self.is_dragging = false
end

local mouse_pos = dfhack.gui.getMousePos()

if self.is_dragging then
if df.global.enabler.mouse_lbut_down == 0 then
self.is_dragging = false
elseif mouse_pos and not self:getMouseFramePos() then
self:spawn(mouse_pos)
end
if not mouse_pos then
return
end

local start_position = copyall(mouse_pos)
local end_position = copyall(mouse_pos)

if self.is_dragging_area then
start_position = self.area_first_pos or mouse_pos
end

if mouse_pos then
guidm.renderMapOverlay(self:callback('getPen'), self:getBounds(
self.is_dragging_area and self.area_first_pos or mouse_pos,
mouse_pos
))
if self:useBrushSize() then
start_position, end_position = self:getBrushSizeBounds(mouse_pos)
end

local bounds = self:getBounds(start_position, end_position)

if self.is_dragging and not self:getMouseFramePos() then
self:paintLiquid(mouse_pos, bounds)
end

guidm.renderMapOverlay(function(pos, _)
if self:inBounds(mouse_pos, pos) then
return self:getPen()
end
end, bounds)
end

function SpawnLiquid:onInput(keys)
Expand All @@ -246,7 +423,7 @@ function SpawnLiquid:onInput(keys)
local mouse_pos = dfhack.gui.getMousePos()

if self.paint_mode == SpawnLiquidPaintMode.CLICK and mouse_pos then
self:spawn(mouse_pos)
self:paintLiquid(mouse_pos)
return true
elseif self.paint_mode == SpawnLiquidPaintMode.AREA and mouse_pos then
if self.is_dragging_area then
Expand Down
Loading