-
Notifications
You must be signed in to change notification settings - Fork 227
Expand file tree
/
Copy pathbiomes.lua
More file actions
371 lines (307 loc) · 10.8 KB
/
biomes.lua
File metadata and controls
371 lines (307 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
-- Visualize and inspect biome regions on the map.
local RELOAD = false -- set to true when actively working on this script
local gui = require('gui')
local widgets = require('gui.widgets')
local guidm = require('gui.dwarfmode')
local INITIAL_LIST_HEIGHT = 5
local INITIAL_INFO_HEIGHT = 15
local texturesOnOff8x12 = dfhack.textures.loadTileset(dfhack.getHackPath()..'/data/art/on-off.png', 8, 12, true)
local LIST_ITEM_HIGHLIGHTED = dfhack.textures.getTexposByHandle(texturesOnOff8x12[1]) -- yellow-ish indicator
local texturesOnOff = dfhack.textures.loadTileset(dfhack.getHackPath()..'/data/art/on-off_top-left.png', 32, 32, true)
local TILE_HIGHLIGHTED = dfhack.textures.getTexposByHandle(texturesOnOff[1]) -- yellow-ish indicator
if TILE_HIGHLIGHTED < 0 then -- use a fallback
TILE_HIGHLIGHTED = 88 -- `X`
end
local texturesSmallLetters = dfhack.textures.loadTileset(dfhack.getHackPath()..'/data/art/curses-small-letters_top-left.png', 32, 32, true)
local TILE_STARTING_SYMBOL = dfhack.textures.getTexposByHandle(texturesSmallLetters[1])
if TILE_STARTING_SYMBOL < 0 then -- use a fallback
TILE_STARTING_SYMBOL = 97 -- `a`
end
local function find(t, predicate)
for k, item in pairs(t) do
if predicate(k, item) then
return k, item
end
end
return nil
end
local regionBiomeMap = {}
local biomesMap = {}
local biomeList = {}
local function gatherBiomeInfo(z)
local maxX, maxY, maxZ = dfhack.maps.getTileSize()
maxX = maxX - 1; maxY = maxY - 1; maxZ = maxZ - 1
z = z or df.global.window_z
--for z = 0, maxZ do
for y = 0, maxY do
for x = 0, maxX do
local rgnX, rgnY = dfhack.maps.getTileBiomeRgn(x,y,z)
if rgnX == nil then goto continue end
local regionBiomesX = regionBiomeMap[rgnX]
if not regionBiomesX then
regionBiomesX = {}
regionBiomeMap[rgnX] = regionBiomesX
end
local regionBiomesXY = regionBiomesX[rgnY]
if not regionBiomesXY then
regionBiomesXY = {
biomeTypeId = dfhack.maps.getBiomeType(rgnX, rgnY),
biome = dfhack.maps.getRegionBiome(rgnX, rgnY),
}
regionBiomesX[rgnY] = regionBiomesXY
end
local biomeTypeId = regionBiomesXY.biomeTypeId
local biome = regionBiomesXY.biome
local biomesZ = biomesMap[z]
if not biomesZ then
biomesZ = {}
biomesMap[z] = biomesZ
end
local biomesZY = biomesZ[y]
if not biomesZY then
biomesZY = {}
biomesZ[y] = biomesZY
end
local function currentBiome(_, item)
return item.biome == biome
end
local ix = find(biomeList, currentBiome)
if not ix then
local ch = string.char(string.byte('a') + #biomeList)
table.insert(biomeList, {biome = biome, char = ch, typeId = biomeTypeId})
ix = #biomeList
end
biomesZY[x] = ix
::continue::
end
end
--end
end
-- always gather info at the very bottom first: this ensures the important biomes are
-- always in the same order (high up in the air strange things happen)
gatherBiomeInfo(0)
--------------------------------------------------------------------------------
local TITLE = "Biomes"
if RELOAD then BiomeVisualizerLegend = nil end
BiomeVisualizerLegend = defclass(BiomeVisualizerLegend, widgets.Window)
BiomeVisualizerLegend.ATTRS {
frame_title=TITLE,
frame_inset=0,
resizable=true,
resize_min={w=25},
frame = {
w = 47,
h = INITIAL_LIST_HEIGHT + 2 + INITIAL_INFO_HEIGHT,
-- just under the minimap:
r = 2,
t = 18,
},
}
local function GetBiomeName(biome, biomeTypeId)
-- based on probe.cpp
local sav = biome.savagery
local evi = biome.evilness;
local sindex = sav > 65 and 2 or sav < 33 and 0 or 1
local eindex = evi > 65 and 2 or evi < 33 and 0 or 1
local surr = sindex + eindex * 3 +1; --in Lua arrays are 1-based
local surroundings = {
"Serene", "Mirthful", "Joyous Wilds",
"Calm", "Wilderness", "Untamed Wilds",
"Sinister", "Haunted", "Terrifying"
}
return ([[%s %s]]):format(surroundings[surr], df.biome_type.attrs[biomeTypeId].caption)
end
function BiomeVisualizerLegend:init()
local list = widgets.List{
view_id = 'list',
frame = { t = 1, b = INITIAL_INFO_HEIGHT + 1 },
icon_width = 1,
text_pen = { fg = COLOR_GREY, bg = COLOR_BLACK }, -- this makes selection stand out more
on_select = self:callback('onSelectEntry'),
}
local tooltip_panel = widgets.Panel{
view_id='tooltip_panel',
autoarrange_subviews=true,
frame = { b = 0, h = INITIAL_INFO_HEIGHT },
frame_style=gui.INTERIOR_FRAME,
frame_background=gui.CLEAR_PEN,
subviews={
widgets.Label{
view_id='label',
auto_height=false,
scroll_keys={},
},
},
}
self:addviews{
list,
tooltip_panel,
}
self.list = list
self.tooltip_panel = tooltip_panel
self:UpdateChoices()
end
local PEN_ACTIVE_ICON = dfhack.pen.parse{tile=LIST_ITEM_HIGHLIGHTED}
local PEN_NO_ICON = nil
function BiomeVisualizerLegend:get_icon_pen_callback(ix)
return function ()
if self.SelectedIndex == ix then
return PEN_ACTIVE_ICON
else
return PEN_NO_ICON
end
end
end
function BiomeVisualizerLegend:get_text_pen_callback(ix)
return function ()
if self.MapHoverIndex == ix then
return self.SelectedIndex == ix
and { fg = COLOR_BLACK, bg = COLOR_LIGHTCYAN }
or { fg = COLOR_BLACK, bg = COLOR_GREY }
else
return nil
end
end
end
function BiomeVisualizerLegend:onSelectEntry(idx, option)
self.SelectedIndex = idx
self.SelectedOption = option
self:ShowTooltip(option)
end
function BiomeVisualizerLegend:UpdateChoices()
local choices = self.list:getChoices() or {}
for i = #choices + 1, #biomeList do
local biomeExt = biomeList[i]
table.insert(choices, {
text = {{
pen = self:get_text_pen_callback(#choices+1),
text = ([[%s: %s]]):format(biomeExt.char, GetBiomeName(biomeExt.biome, biomeExt.typeId)),
}},
icon = self:get_icon_pen_callback(#choices+1),
biomeTypeId = biomeExt.typeId,
biome = biomeExt.biome,
})
end
self.list:setChoices(choices)
end
function BiomeVisualizerLegend:onRenderFrame(dc, rect)
BiomeVisualizerLegend.super.onRenderFrame(self, dc, rect)
local list = self.list
local currentHoverIx = list:getIdxUnderMouse()
local oldIx = self.HoverIndex
if currentHoverIx ~= oldIx then
self.HoverIndex = currentHoverIx
if self.onMouseHoverEntry then
local choices = list:getChoices()
self:onMouseHoverEntry(currentHoverIx, choices[currentHoverIx])
end
end
end
local function add_field_text(lines, biome, field_name)
lines[#lines+1] = ("%s: %s"):format(field_name, biome[field_name])
lines[#lines+1] = NEWLINE
end
local function get_tooltip_text(option)
if not option then
return ""
end
local text = {}
text[#text+1] = ("type: %s"):format(df.biome_type[option.biomeTypeId])
text[#text+1] = NEWLINE
local biome = option.biome
add_field_text(text, biome, "savagery")
add_field_text(text, biome, "evilness")
table.insert(text, NEWLINE)
add_field_text(text, biome, "elevation")
add_field_text(text, biome, "rainfall")
add_field_text(text, biome, "drainage")
add_field_text(text, biome, "vegetation")
add_field_text(text, biome, "temperature")
add_field_text(text, biome, "volcanism")
table.insert(text, NEWLINE)
local flags = biome.flags
if flags.is_lake then
text[#text+1] = "lake"
text[#text+1] = NEWLINE
end
if flags.is_brook then
text[#text+1] = "brook"
text[#text+1] = NEWLINE
end
return text
end
function BiomeVisualizerLegend:onMouseHoverEntry(idx, option)
self:ShowTooltip(option or self.SelectedOption)
end
function BiomeVisualizerLegend:ShowTooltip(option)
local text = get_tooltip_text(option)
local tooltip_panel = self.tooltip_panel
local lbl = tooltip_panel.subviews.label
lbl:setText(text)
end
function BiomeVisualizerLegend:onRenderBody(painter)
local thisPos = self:getMouseFramePos()
local pos = dfhack.gui.getMousePos()
if not thisPos and pos then
local N = safe_index(biomesMap, pos.z, pos.y, pos.x)
if N then
local choices = self.list:getChoices()
local option = choices[N]
self.MapHoverIndex = N
self:ShowTooltip(option)
end
else
self.MapHoverIndex = nil
end
BiomeVisualizerLegend.super.onRenderBody(self, painter)
end
--------------------------------------------------------------------------------
if RELOAD then BiomeVisualizer = nil end
BiomeVisualizer = defclass(BiomeVisualizer, gui.ZScreen)
BiomeVisualizer.ATTRS{
focus_path='BiomeVisualizer',
pass_movement_keys=true,
}
function BiomeVisualizer:init()
local legend = BiomeVisualizerLegend{view_id = 'legend'}
self:addviews{legend}
end
function BiomeVisualizer:onRenderFrame(dc, rect)
BiomeVisualizer.super.onRenderFrame(self, dc, rect)
if not dfhack.screen.inGraphicsMode() and not gui.blink_visible(500) then
return
end
local z = df.global.window_z
if not biomesMap[z] then
gatherBiomeInfo(z)
self.subviews.legend:UpdateChoices()
end
local function get_overlay_pen(pos)
local self = self
local safe_index = safe_index
local biomes = biomesMap
local N = safe_index(biomes, pos.z, pos.y, pos.x)
if not N then return end
local idxSelected = self.subviews.legend.SelectedIndex
local idxTile = (N == idxSelected)
and TILE_HIGHLIGHTED
or TILE_STARTING_SYMBOL + (N-1)
local color = (N == idxSelected)
and COLOR_CYAN
or COLOR_GREY
local ch = string.char(string.byte('a') + (N-1))
return color, ch, idxTile
end
guidm.renderMapOverlay(get_overlay_pen, nil) -- nil for bounds means entire viewport
end
function BiomeVisualizer:onDismiss()
view = nil
end
if not dfhack.isMapLoaded() then
qerror('gui/biomes requires a map to be loaded')
end
if RELOAD and view then
view:dismiss()
-- view is nil now
end
view = view and view:raise() or BiomeVisualizer{}:show()