Skip to content

Commit 2ef4b23

Browse files
authored
move copy.lua to gui/stamper.lua
1 parent a64b5a9 commit 2ef4b23

1 file changed

Lines changed: 372 additions & 0 deletions

File tree

gui/stamper.lua

Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
--designating tool
2+
3+
--[====[
4+
5+
stamper
6+
=======
7+
stamper that allows manipulation of designations by transforms such as translations, reflections, rotations, and inversion.
8+
designations can also be used as brushes to erase other designations and cancel constructions.
9+
10+
]====]
11+
12+
--[[
13+
Author's Note:
14+
15+
shoutout to twitch.tv/keupo
16+
created by flavorstreet
17+
18+
pipicaca.
19+
]]
20+
local utils = require "utils"
21+
local gui = require "gui"
22+
local guidm = require "gui.dwarfmode"
23+
local dlg = require "gui.dialogs"
24+
25+
StamperUI = defclass(StamperUI, guidm.MenuOverlay)
26+
27+
StamperUI.ATTRS {
28+
state="none",
29+
buffer=nil,
30+
offsetDirection=0,
31+
cull=true,
32+
blink=false,
33+
option="normal"
34+
}
35+
36+
local digSymbols={" ", "X", "_", 30, ">", "<"}
37+
38+
function StamperUI:init()
39+
self.saved_mode = df.global.ui.main.mode
40+
df.global.ui.main.mode=df.ui_sidebar_mode.LookAround
41+
end
42+
43+
function StamperUI:onDestroy()
44+
df.global.ui.main.mode = self.saved_mode
45+
end
46+
47+
local function paintMapTile(dc, vp, cursor, pos, ...)
48+
if not same_xyz(cursor, pos) then
49+
local stile = vp:tileToScreen(pos)
50+
if stile.z == 0 then
51+
dc:map(true):seek(stile.x,stile.y):char(...):map(false)
52+
end
53+
end
54+
end
55+
56+
local function minToMax(...)
57+
local args={...}
58+
table.sort(args,function(a,b) return a < b end)
59+
return table.unpack(args)
60+
end
61+
62+
local function cullBuffer(data) --there"s probably a memory saving way of doing this
63+
local lowerX=math.huge
64+
local lowerY=math.huge
65+
local upperX=-math.huge
66+
local upperY=-math.huge
67+
for x=0,data.xlen do
68+
for y=0,data.ylen do
69+
if data[x][y].dig>0 then
70+
lowerX=math.min(x,lowerX)
71+
lowerY=math.min(y,lowerY)
72+
upperX=math.max(x,upperX)
73+
upperY=math.max(y,upperY)
74+
end
75+
end
76+
end
77+
if lowerX==math.huge then lowerX=0 end
78+
if lowerY==math.huge then lowerY=0 end
79+
if upperX==-math.huge then upperX=data.xlen end
80+
if upperY==-math.huge then upperY=data.ylen end
81+
local buffer={}
82+
for x=lowerX,upperX do
83+
buffer[x-lowerX]={}
84+
for y=lowerY,upperY do
85+
buffer[x-lowerX][y-lowerY]=data[x][y]
86+
end
87+
end
88+
buffer.xlen=upperX-lowerX
89+
buffer.ylen=upperY-lowerY
90+
return buffer
91+
end
92+
local function getTiles(p1,p2,cull)
93+
if cull==nil then cull=true end
94+
local x1,x2=minToMax(p1.x,p2.x)
95+
local y1,y2=minToMax(p1.y,p2.y)
96+
local xlen=x2-x1
97+
local ylen=y2-y1
98+
assert(p1.z==p2.z, "only tiles from the same Z-level can be copied")
99+
local z=p1.z
100+
local data={}
101+
for k, block in ipairs(df.global.world.map.map_blocks) do
102+
if block.map_pos.z==z then
103+
for block_x, row in ipairs(block.designation) do
104+
local x=block_x+block.map_pos.x
105+
if x>=x1 and x<=x2 then
106+
if not data[x-x1] then
107+
data[x-x1]={}
108+
end
109+
for block_y, tile in ipairs(row) do
110+
local y=block_y+block.map_pos.y
111+
if y>=y1 and y<=y2 then
112+
data[x-x1][y-y1]=copyall(tile)
113+
end
114+
end
115+
end
116+
end
117+
end
118+
end
119+
data.xlen=xlen
120+
data.ylen=ylen
121+
if cull then
122+
return cullBuffer(data)
123+
end
124+
return data
125+
end
126+
127+
function StamperUI:getOffset()
128+
if self.offsetDirection==0 then --southeast
129+
return 0, 0
130+
elseif self.offsetDirection==1 then --northeast
131+
return 0, -self.buffer.ylen
132+
elseif self.offsetDirection==2 then --northwest
133+
return -self.buffer.xlen, -self.buffer.ylen
134+
elseif self.offsetDirection==3 then --southwest
135+
return -self.buffer.xlen, 0
136+
else
137+
error("out of range")
138+
end
139+
end
140+
141+
function StamperUI:setBuffer(tiles)
142+
self.buffer=tiles
143+
end
144+
145+
function StamperUI:transformBuffer(callback)
146+
local newBuffer={}
147+
local xlen=0
148+
local ylen=0
149+
for x=0, self.buffer.xlen do
150+
for y=0, self.buffer.ylen do
151+
local x2,y2=callback(x,y,self.buffer.xlen,self.buffer.ylen,self.buffer[x][y])
152+
xlen=math.max(x2,xlen)
153+
ylen=math.max(y2,ylen)
154+
if not newBuffer[x2] then
155+
newBuffer[x2]={}
156+
end
157+
if not newBuffer[x2][y2] then
158+
newBuffer[x2][y2]=self.buffer[x][y]
159+
end
160+
end
161+
end
162+
newBuffer.xlen=xlen
163+
newBuffer.ylen=ylen
164+
return newBuffer
165+
end
166+
167+
function StamperUI:pasteBuffer(position,option)
168+
local z=position.z
169+
local offsetX,offsetY=self:getOffset()
170+
local x1=position.x+offsetX
171+
local x2=position.x+self.buffer.xlen+offsetX
172+
local y1=position.y+offsetY
173+
local y2=position.y+self.buffer.ylen+offsetY
174+
for k, block in ipairs(df.global.world.map.map_blocks) do
175+
if block.map_pos.z==z then
176+
for block_x, row in ipairs(block.designation) do
177+
local x=block_x+block.map_pos.x
178+
if x>=x1 and x<=x2 then
179+
for block_y, tile in ipairs(row) do
180+
local y=block_y+block.map_pos.y
181+
if y>=y1 and y<=y2 and self.buffer[x-x1][y-y1].dig>0 then
182+
if self.option=="erase" then
183+
tile.dig=0
184+
elseif self.option=="construction" then
185+
dfhack.constructions.designateRemove(x,y,z)
186+
else
187+
tile.dig=self.buffer[x-x1][y-y1].dig
188+
end
189+
end
190+
end
191+
end
192+
end
193+
end
194+
end
195+
end
196+
197+
function StamperUI:invertBuffer() --this modifies the buffer instead of copying it
198+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=0 else tile.dig=1 end return x,y end)
199+
end
200+
201+
function StamperUI:renderOverlay()
202+
local vp=self:getViewport()
203+
local dc = gui.Painter.new(self.df_layout.map)
204+
local visible = gui.blink_visible(500)
205+
206+
if gui.blink_visible(120) and self.marking then
207+
paintMapTile(dc, vp, nil, self.mark, "+", COLOR_LIGHTGREEN)
208+
--perhaps draw a rectangle to the point
209+
elseif not marking and (gui.blink_visible(750) or not self.blink) and self.buffer~=nil and (self.state=="brush" or self.state=="convert") then
210+
--draw over cursor in these circumstances
211+
local offsetX,offsetY=self:getOffset()
212+
for x=0, self.buffer.xlen do
213+
for y=0, self.buffer.ylen do
214+
local tile=self.buffer[x][y]
215+
if tile.dig>0 then
216+
if not (gui.blink_visible(750) and x==-offsetX and y==-offsetY) then
217+
local fg=COLOR_BLACK
218+
local bg=COLOR_CYAN
219+
if self.option=="erase" then
220+
bg=COLOR_RED
221+
fg=COLOR_BLACK
222+
elseif self.option=="construction" then
223+
bg=COLOR_GREEN
224+
fg=COLOR_BLACK
225+
end
226+
local symbol=digSymbols[tile.dig]
227+
if self.option~="normal" then
228+
symbol=" "
229+
end
230+
dc:pen(fg,bg)
231+
paintMapTile(dc, vp, nil, xyz2pos(df.global.cursor.x+x+offsetX,df.global.cursor.y+y+offsetY,df.global.cursor.z), symbol, fg)
232+
end
233+
end
234+
end
235+
end
236+
end
237+
end
238+
239+
function StamperUI:onRenderBody(dc)
240+
self:renderOverlay()
241+
242+
dc:clear():seek(1,1):pen(COLOR_WHITE):string("Stamper - "..self.state:gsub("^%a",function(x)return x:upper()end))
243+
dc:seek(2,3)
244+
245+
if self.state=="brush" then
246+
dc:key_string("CUSTOM_S", "Set Brush",COLOR_GREY)
247+
dc:newline():newline(1)
248+
dc:key_string("CUSTOM_H", "Flip Horizontal",COLOR_GREY):newline(1)
249+
dc:key_string("CUSTOM_V", "Flip Vertical",COLOR_GREY):newline(1)
250+
dc:key_string("CUSTOM_R", "Rotate 90",COLOR_GREY):newline(1)
251+
dc:key_string("CUSTOM_T", "Rotate -90",COLOR_GREY):newline(1)
252+
dc:key_string("CUSTOM_G", "Cycle Corner",COLOR_GREY):newline(1)
253+
dc:key_string("CUSTOM_I", "Invert",COLOR_GREY):newline(1)
254+
dc:key_string("CUSTOM_C", "Convert to...",COLOR_GREY):newline(1)
255+
dc:newline(1)
256+
dc:key_string("CUSTOM_E", (self.option=="erase" and "Erasing" or "Erase"),self.option=="erase" and COLOR_RED or COLOR_GREY):newline(1) --make red
257+
dc:key_string("CUSTOM_X", (self.option=="construction" and "Removing" or "Remove").." Constructions",self.option=="construction" and COLOR_GREEN or COLOR_GREY):newline(1) --make red
258+
dc:newline():newline(1)
259+
dc:key_string("CUSTOM_B", "Blink Brush",self.blink and COLOR_WHITE or COLOR_GREY):newline(1)
260+
dc:newline()
261+
elseif self.state=="mark" then
262+
if self.buffer==nil then
263+
dc:string("Select two corners.")
264+
end
265+
dc:newline():newline(1)
266+
dc:key_string("CUSTOM_P", "Cull Selections",self.cull and COLOR_WHITE or COLOR_GREY)
267+
elseif self.state=="convert" then
268+
dc:key_string("CUSTOM_D","Mine",COLOR_GREY):newline(2)
269+
dc:key_string("CUSTOM_H", "Channel",COLOR_GREY):newline(2)
270+
dc:key_string("CUSTOM_U", "Up Stair",COLOR_GREY):newline(2)
271+
dc:key_string("CUSTOM_J", "Up Stair",COLOR_GREY):newline(2)
272+
dc:key_string("CUSTOM_I", "U/D Stair",COLOR_GREY):newline(2)
273+
dc:key_string("CUSTOM_R", "Up Ramp",COLOR_GREY):newline(2)
274+
dc:newline(1)
275+
dc:string("To undesignate use the erase command",COLOR_WHITE)
276+
end
277+
278+
dc:newline():newline():key_string("LEAVESCREEN", "Back")
279+
end
280+
281+
function StamperUI:onInput(keys)
282+
if df.global.cursor.x==-30000 then
283+
local vp=self:getViewport()
284+
df.global.cursor=xyz2pos(math.floor((vp.x1+math.abs((vp.x2-vp.x1))/2)+.5),math.floor((vp.y1+math.abs((vp.y2-vp.y1)/2))+.5), vp.z)
285+
return
286+
end
287+
288+
if self.state=="brush" then
289+
if keys.CUSTOM_S then
290+
self.state="mark"
291+
elseif keys.CUSTOM_D then
292+
self.state="brush"
293+
elseif keys.CUSTOM_H then
294+
self.buffer=self:transformBuffer(function(x,y,xlen,ylen,tile) return xlen-x, y end)
295+
elseif keys.CUSTOM_V then
296+
self.buffer=self:transformBuffer(function(x,y,xlen,ylen,tile) return x, ylen-y end)
297+
elseif keys.CUSTOM_R then
298+
self.buffer=self:transformBuffer(function(x,y,xlen,ylen,tile) return y, xlen-x end)
299+
self.offsetDirection=(self.offsetDirection+1)%4
300+
elseif keys.CUSTOM_T then
301+
self.buffer=self:transformBuffer(function(x,y,xlen,ylen,tile) return ylen-y,x end)
302+
self.offsetDirection=(self.offsetDirection-1)%4
303+
elseif keys.CUSTOM_G then
304+
self.offsetDirection=(self.offsetDirection+1)%4
305+
elseif keys.CUSTOM_E then
306+
self.option=self.option=="erase" and "normal" or "erase"
307+
elseif keys.CUSTOM_X then
308+
self.option=self.option=="construction" and "normal" or "construction"
309+
elseif keys.CUSTOM_I then
310+
self:invertBuffer()
311+
elseif keys.CUSTOM_C then
312+
self.state="convert"
313+
elseif keys.CUSTOM_B then
314+
self.blink = not self.blink
315+
elseif keys.SELECT then
316+
self:pasteBuffer(copyall(df.global.cursor))
317+
end
318+
elseif self.state=="mark" then
319+
if keys.SELECT then
320+
if self.marking then
321+
--set the table
322+
self.state="brush"
323+
self.marking = false
324+
self:setBuffer(getTiles(self.mark,copyall(df.global.cursor),self.cull))
325+
else
326+
self.marking = true
327+
self.mark = copyall(df.global.cursor)
328+
end
329+
elseif keys.LEAVESCREEN and self.buffer~=nil then
330+
self.state="brush"
331+
return
332+
elseif keys.CUSTOM_P then
333+
self.cull = not self.cull
334+
end
335+
elseif self.state=="convert" then
336+
if keys.LEAVESCREEN then
337+
self.state="brush"
338+
return
339+
elseif keys.CUSTOM_D then
340+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=1 end return x,y end)
341+
self.state="brush"
342+
elseif keys.CUSTOM_H then
343+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=3 end return x,y end)
344+
self.state="brush"
345+
elseif keys.CUSTOM_U then
346+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=6 end return x,y end)
347+
self.state="brush"
348+
elseif keys.CUSTOM_J then
349+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=5 end return x,y end)
350+
self.state="brush"
351+
elseif keys.CUSTOM_I then
352+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=2 end return x,y end)
353+
self.state="brush"
354+
elseif keys.CUSTOM_R then
355+
self:transformBuffer(function(x,y,xlen,ylen,tile) if tile.dig>0 then tile.dig=4 end return x,y end)
356+
self.state="brush"
357+
end
358+
end
359+
360+
if keys.LEAVESCREEN then
361+
self:dismiss()
362+
elseif self:propagateMoveKeys(keys) then
363+
return
364+
end
365+
end
366+
367+
if not (dfhack.gui.getCurFocus():match("^dwarfmode/Default") or dfhack.gui.getCurFocus():match("^dwarfmode/Designate") or dfhack.gui.getCurFocus():match("^dwarfmode/LookAround"))then
368+
qerror("This screen requires the main dwarfmode view or the designation screen")
369+
end
370+
371+
local list = StamperUI{state="mark", blink=false,cull=true}
372+
list:show()

0 commit comments

Comments
 (0)