-
Notifications
You must be signed in to change notification settings - Fork 496
Expand file tree
/
Copy pathtile-material.lua
More file actions
359 lines (302 loc) · 13.7 KB
/
tile-material.lua
File metadata and controls
359 lines (302 loc) · 13.7 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
-- tile-material: Functions to help retrieve the material for a tile.
--[[
Original code provided by Milo Christiansen in 2015 under the MIT license. Relicensed under the ZLib license to align with the rest of DFHack, with his permission.
]]
local _ENV = mkmodule("tile-material")
--[====[
tile-material
=============
This module contains functions for finding the material of a tile.
There is a function that will find the material of the tile based on it's type (in other words
it will return the material DF is using for that tile), and there are functions that will attempt
to return only a certain class of materials.
Most users will be most interested in the generic "GetTileMat" function, but the other functions
should be useful in certain cases. For example "GetLayerMat" will always return the material of
the stone (or soil) in the current layer, ignoring any veins or other inclusions.
Some tile types/materials have special behavior with the "GetTileMat" function.
* Open space and other "material-less" tiles (such as semi-molten rock or eerie glowing pits)
will return nil.
* Ice will return the hard-coded water material ("WATER:NONE").
The specialized functions will return nil if a material of their type is not possible for a tile.
For example calling "GetVeinMat" for a tile that does not have (and has never had) a mineral vein
will always return nil.
There are two functions for dealing with constructions, one to get the material of the construction
and one that gets the material of the tile the construction was built over.
All the functions take coordinates as either three arguments (x, y, z) or one argument containing
a table with numeric x, y, and z keys.
I am not sure how caved in tiles are handled, but after some quick testing it appears that the
game creates mineral veins for them. I am not 100% sure if these functions will reliably work
with all caved in tiles, but I can confirm that they do in at least some cases...
]====]
-- Since there isn't any consistent style for module documentation I documented every function in
-- the style used by GoDoc (which is what I am most used to).
-- Internal
local function prepPos(x, y, z)
if x ~= nil and y == nil and z == nil then
if type(x) ~= "table" or type(x.x) ~= "number" or type(x.y) ~= "number" or type(x.z) ~= "number" or x.x == -30000 then
error "Invalid coordinate argument(s)."
end
return x
else
if type(x) ~= "number" or type(y) ~= "number" or type(z) ~= "number" or x == -30000 then
error "Invalid coordinate argument(s)."
end
return {x = x, y = y, z = z}
end
end
-- Internal
local function fixedMat(id)
local mat = dfhack.matinfo.find(id)
return function(x, y, z)
return mat
end
end
-- GetLayerMat returns the layer material for the given tile.
-- AFAIK this will never return nil.
function GetLayerMat(x, y, z)
local pos = prepPos(x, y, z)
local region_info = dfhack.maps.getRegionBiome(dfhack.maps.getTileBiomeRgn(pos))
local map_block = dfhack.maps.ensureTileBlock(pos)
local biome = df.world_geo_biome.find(region_info.geo_index)
local layer_index = map_block.designation[pos.x%16][pos.y%16].geolayer_index
local layer_mat_index = biome.layers[layer_index].mat_index
return dfhack.matinfo.decode(0, layer_mat_index)
end
-- GetLavaStone returns the biome lava stone material (generally obsidian).
function GetLavaStone(x, y, z)
local pos = prepPos(x, y, z)
local regions = df.global.world.world_data.midmap_data.region_details
local rx, ry = dfhack.maps.getTileBiomeRgn(pos)
for _, region in ipairs(regions) do
if region.pos.x == rx and region.pos.y == ry then
return dfhack.matinfo.decode(0, region.lava_stone)
end
end
return nil
end
-- GetVeinMat returns the vein material of the given tile or nil if the tile has no veins.
-- Multiple veins in one tile should be handled properly (smallest vein type, last in the list wins,
-- which seems to be the rule DF uses).
function GetVeinMat(x, y, z)
local pos = prepPos(x, y, z)
local map_block = dfhack.maps.ensureTileBlock(pos)
local events = {}
for _, event in ipairs(map_block.block_events) do
if getmetatable(event) == "block_square_event_mineralst" then
if dfhack.maps.getTileAssignment(event.tile_bitmask, pos.x, pos.y) then
table.insert(events, event)
end
end
end
if #events == 0 then
return nil
end
local event_priority = function(event)
if event.flags.cluster then
return 1
elseif event.flags.vein then
return 2
elseif event.flags.cluster_small then
return 3
elseif event.flags.cluster_one then
return 4
else
return 5
end
end
local priority = events[1]
for _, event in ipairs(events) do
if event_priority(event) >= event_priority(priority) then
priority = event
end
end
return dfhack.matinfo.decode(0, priority.inorganic_mat)
end
-- GetConstructionMat returns the material of the construction at the given tile or nil if the tile
-- has no construction.
function GetConstructionMat(x, y, z)
local pos = prepPos(x, y, z)
for _, construction in ipairs(df.global.world.event.constructions) do
if construction.pos.x == pos.x and construction.pos.y == pos.y and construction.pos.z == pos.z then
return dfhack.matinfo.decode(construction)
end
end
return nil
end
-- GetConstructOriginalTileMat returns the material of the tile under the construction at the given
-- tile or nil if the tile has no construction.
function GetConstructOriginalTileMat(x, y, z)
local pos = prepPos(x, y, z)
for _, construction in ipairs(df.global.world.event.constructions) do
if construction.pos.x == pos.x and construction.pos.y == pos.y and construction.pos.z == pos.z then
return GetTileTypeMat(construction.original_tile, BasicMats, pos)
end
end
return nil
end
-- GetTreeMat returns the material of the tree at the given tile or nil if the tile does not have a
-- tree or giant mushroom.
function GetTreeMat(x, y, z)
local pos = prepPos(x, y, z)
local plant = dfhack.maps.getPlantAtTile(pos)
return plant and plant.tree_info and dfhack.matinfo.decode(419, plant.material) or nil
end
-- GetShrubMat returns the material of the shrub at the given tile or nil if the tile does not
-- contain a shrub or sapling.
function GetShrubMat(x, y, z)
local pos = prepPos(x, y, z)
for _, shrub in ipairs(df.global.world.plants.all) do
if shrub.tree_info == nil then
if shrub.pos.x == pos.x and shrub.pos.y == pos.y and shrub.pos.z == pos.z then
return dfhack.matinfo.decode(419, shrub.material)
end
end
end
return nil
end
-- GetGrassMat returns the material of the grass at the given tile or nil if the tile is not
-- covered in grass.
function GetGrassMat(x, y, z)
local pos = prepPos(x, y, z)
local map_block = dfhack.maps.ensureTileBlock(pos)
for _, event in ipairs(map_block.block_events) do
if getmetatable(event) == "block_square_event_grassst" then
local amount = event.amount[pos.x%16][pos.y%16]
if amount > 0 then
return df.plant_raw.find(event.plant_index).material
end
end
end
return nil
end
-- GetFeatureMat returns the material of the feature (adamantine tube, underworld surface, etc) at
-- the given tile or nil if the tile is not made of a feature stone.
function GetFeatureMat(x, y, z)
local pos = prepPos(x, y, z)
local map_block = dfhack.maps.ensureTileBlock(pos)
if df.tiletype.attrs[map_block.tiletype[pos.x%16][pos.y%16]].material ~= df.tiletype_material.FEATURE then
return nil
end
if map_block.designation[pos.x%16][pos.y%16].feature_local then
-- adamantine tube, etc
for id, idx in ipairs(df.global.world.features.feature_local_idx) do
if idx == map_block.local_feature then
return dfhack.matinfo.decode(df.global.world.features.map_features[id])
end
end
elseif map_block.designation[pos.x%16][pos.y%16].feature_global then
-- cavern, magma sea, underworld, etc
for id, idx in ipairs(df.global.world.features.feature_global_idx) do
if idx == map_block.global_feature then
return dfhack.matinfo.decode(df.global.world.features.map_features[id])
end
end
end
return nil
end
-- BasicMats is a matspec table to pass to GetTileMatSpec or GetTileTypeMat. This particular
-- matspec table covers the common case of returning plant materials for plant tiles and other
-- materials for the remaining tiles.
BasicMats = {
[df.tiletype_material.AIR] = nil, -- Empty
[df.tiletype_material.SOIL] = GetLayerMat,
[df.tiletype_material.STONE] = GetLayerMat,
[df.tiletype_material.FEATURE] = GetFeatureMat,
[df.tiletype_material.LAVA_STONE] = GetLavaStone,
[df.tiletype_material.MINERAL] = GetVeinMat,
[df.tiletype_material.FROZEN_LIQUID] = fixedMat("WATER:NONE"),
[df.tiletype_material.CONSTRUCTION] = GetConstructionMat,
[df.tiletype_material.GRASS_LIGHT] = GetGrassMat,
[df.tiletype_material.GRASS_DARK] = GetGrassMat,
[df.tiletype_material.GRASS_DRY] = GetGrassMat,
[df.tiletype_material.GRASS_DEAD] = GetGrassMat,
[df.tiletype_material.PLANT] = GetShrubMat,
[df.tiletype_material.HFS] = nil, -- Eerie Glowing Pit
[df.tiletype_material.CAMPFIRE] = GetLayerMat,
[df.tiletype_material.FIRE] = GetLayerMat,
[df.tiletype_material.ASHES] = GetLayerMat,
[df.tiletype_material.MAGMA] = nil, -- SMR
[df.tiletype_material.DRIFTWOOD] = GetLayerMat,
[df.tiletype_material.POOL] = GetLayerMat,
[df.tiletype_material.BROOK] = GetLayerMat,
[df.tiletype_material.ROOT] = GetTreeMat,
[df.tiletype_material.TREE] = GetTreeMat,
[df.tiletype_material.MUSHROOM] = GetTreeMat,
[df.tiletype_material.UNDERWORLD_GATE] = nil, -- I guess this is for the gates found in vaults?
}
-- NoPlantMats is a matspec table to pass to GetTileMatSpec or GetTileTypeMat. This particular
-- matspec table will ignore plants, returning layer materials (or nil for trees) instead.
NoPlantMats = {
[df.tiletype_material.SOIL] = GetLayerMat,
[df.tiletype_material.STONE] = GetLayerMat,
[df.tiletype_material.FEATURE] = GetFeatureMat,
[df.tiletype_material.LAVA_STONE] = GetLavaStone,
[df.tiletype_material.MINERAL] = GetVeinMat,
[df.tiletype_material.FROZEN_LIQUID] = fixedMat("WATER:NONE"),
[df.tiletype_material.CONSTRUCTION] = GetConstructionMat,
[df.tiletype_material.GRASS_LIGHT] = GetLayerMat,
[df.tiletype_material.GRASS_DARK] = GetLayerMat,
[df.tiletype_material.GRASS_DRY] = GetLayerMat,
[df.tiletype_material.GRASS_DEAD] = GetLayerMat,
[df.tiletype_material.PLANT] = GetLayerMat,
[df.tiletype_material.CAMPFIRE] = GetLayerMat,
[df.tiletype_material.FIRE] = GetLayerMat,
[df.tiletype_material.ASHES] = GetLayerMat,
[df.tiletype_material.DRIFTWOOD] = GetLayerMat,
[df.tiletype_material.POOL] = GetLayerMat,
[df.tiletype_material.BROOK] = GetLayerMat,
[df.tiletype_material.ROOT] = GetLayerMat,
}
-- OnlyPlantMats is a matspec table to pass to GetTileMatSpec or GetTileTypeMat. This particular
-- matspec table will return nil for any non-plant tile. Plant tiles return the plant material.
OnlyPlantMats = {
[df.tiletype_material.GRASS_LIGHT] = GetGrassMat,
[df.tiletype_material.GRASS_DARK] = GetGrassMat,
[df.tiletype_material.GRASS_DRY] = GetGrassMat,
[df.tiletype_material.GRASS_DEAD] = GetGrassMat,
[df.tiletype_material.PLANT] = GetShrubMat,
[df.tiletype_material.ROOT] = GetTreeMat,
[df.tiletype_material.TREE] = GetTreeMat,
[df.tiletype_material.MUSHROOM] = GetTreeMat,
}
-- GetTileMat will return the material of the specified tile as determined by its tile type and the
-- world geology data, etc.
-- The returned material should exactly match the material reported by DF except in cases where is
-- is impossible to get a material.
-- This is equivalent to calling GetTileMatSpec with the BasicMats matspec table.
function GetTileMat(x, y, z)
return GetTileMatSpec(BasicMats, x, y, z)
end
-- GetTileMatSpec is exactly like GetTileMat except you may specify an explicit matspec table.
--
-- "matspec" tables are simply tables with tiletype material classes as keys and functions
-- taking a coordinate table and returning a material as values. These tables are used to
-- determine how a specific material for a given tiletype material classification is determined.
-- Any tiletype material class that is unset (left nil) in a matspec table will result in tiles
-- of that type returning nil for their material.
function GetTileMatSpec(matspec, x, y, z)
local pos = prepPos(x, y, z)
local typ = dfhack.maps.getTileType(pos)
if typ == nil then
return nil
end
return GetTileTypeMat(typ, matspec, pos)
end
-- GetTileTypeMat returns the material of the given tile assuming it is the given tiletype.
--
-- Use this function when you want to check to see what material a given tile would be if it
-- was a specific tiletype. For example you can check to see if the tile used to be part of
-- a mineral vein or similar. Note that you can do the same basic thing by calling the individual
-- material finders directly, but this is sometimes simpler.
--
-- Unless the tile could be the given type this function will probably return nil.
function GetTileTypeMat(typ, matspec, x, y, z)
local pos = prepPos(x, y, z)
local type_mat = df.tiletype.attrs[typ].material
local mat_getter = matspec[type_mat]
if mat_getter == nil then
return nil
end
return mat_getter(pos)
end
return _ENV