dfhack/plugins/ruby/map.rb

275 lines
8.5 KiB
Ruby

module DFHack
class << self
# return a map block by tile coordinates
# you can also use find_map_block(cursor) or anything that respond to x/y/z
def map_block_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if x >= 0 and x < world.map.x_count and y >= 0 and y < world.map.y_count and z >= 0 and z < world.map.z_count
world.map.block_index[x/16][y/16][z]
end
end
def map_designation_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if b = map_block_at(x, y, z)
b.designation[x%16][y%16]
end
end
def map_occupancy_at(x, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
if b = map_block_at(x, y, z)
b.occupancy[x%16][y%16]
end
end
def map_tile_at(x=df.cursor, y=nil, z=nil)
x = x.pos if x.respond_to?(:pos)
x, y, z = x.x, x.y, x.z if x.respond_to?(:x)
b = map_block_at(x, y, z)
MapTile.new(b, x, y, z) if b
end
# yields every map block
def each_map_block
(0...world.map.x_count_block).each { |xb|
xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb|
yl = xl[yb]
(0...world.map.z_count_block).each { |z|
p = yl[z]
yield p if p
}
}
}
end
# yields every map block for a given z level
def each_map_block_z(z)
(0...world.map.x_count_block).each { |xb|
xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb|
p = xl[yb][z]
yield p if p
}
}
end
end
class MapTile
attr_accessor :x, :y, :z, :dx, :dy, :mapblock
def initialize(b, x, y, z)
@x, @y, @z = x, y, z
@dx, @dy = @x&15, @y&15
@mapblock = b
end
def offset(dx, dy=nil, dz=0)
if dx.respond_to?(:x)
dz = dx.z if dx.respond_to?(:z)
dx, dy = dx.x, dx.y
end
df.map_tile_at(@x+dx, @y+dy, @z+dz)
end
def designation
@mapblock.designation[@dx][@dy]
end
def occupancy
@mapblock.occupancy[@dx][@dy]
end
def tiletype
@mapblock.tiletype[@dx][@dy]
end
def tiletype=(t)
@mapblock.tiletype[@dx][@dy] = t
end
def caption
Tiletype::Caption[tiletype]
end
def shape
Tiletype::Shape[tiletype]
end
def tilemat
Tiletype::Material[tiletype]
end
def variant
Tiletype::Variant[tiletype]
end
def special
Tiletype::Special[tiletype]
end
def direction
Tiletype::Direction[tiletype]
end
def shape_caption
TiletypeShape::Caption[shape]
end
def shape_basic
TiletypeShape::BasicShape[shape]
end
def shape_passablelow
TiletypeShape::PassableLow[shape]
end
def shape_passablehigh
TiletypeShape::PassableHigh[shape]
end
def shape_passableflow
TiletypeShape::PassableFlow[shape]
end
def shape_walkable
TiletypeShape::Walkable[shape]
end
# return all veins for current mapblock
def all_veins
mapblock.block_events.grep(BlockSquareEventMineralst)
end
# return the vein applicable to current tile
def vein
# last vein wins
all_veins.reverse.find { |v|
(v.tile_bitmask.bits[@dy] & (1 << @dx)) > 0
}
end
# return the mat_index for the current tile (if in vein)
def mat_index_vein
v = vein
v.inorganic_mat if v
end
# return the world_data.geo_biome for current tile
def geo_biome
b = designation.biome
wd = df.world.world_data
# region coords + [[-1, -1], [0, -1], ..., [1, 1]][b]
# clipped to world dimensions
rx = df.world.map.region_x/16
rx -= 1 if b % 3 == 0 and rx > 0
rx += 1 if b % 3 == 2 and rx < wd.world_width-1
ry = df.world.map.region_y/16
ry -= 1 if b < 3 and ry > 0
ry += 1 if b > 5 and ry < wd.world_height-1
wd.geo_biomes[ wd.region_map[rx][ry].geo_index ]
end
# return the world_data.geo_biome.layer for current tile
def stone_layer
geo_biome.layers[designation.geolayer_index]
end
# current tile mat_index (vein if applicable, or base material)
def mat_index
mat_index_vein or stone_layer.mat_index
end
# MaterialInfo: inorganic token for current tile
def mat_info
MaterialInfo.new(0, mat_index)
end
def inspect
"#<MapTile pos=[#@x, #@y, #@z] shape=#{shape} tilemat=#{tilemat} material=#{mat_info.token}>"
end
def dig(mode=:Default)
if mode == :Smooth
if (tilemat == :STONE or tilemat == :MINERAL) and caption !~ /smooth|pillar|fortification/i and # XXX caption..
designation.smooth == 0 and (designation.hidden or not df.world.job_list.find { |j|
# the game removes 'smooth' designation as soon as it assigns a job, if we
# re-set it the game may queue another :DetailWall that will carve a fortification
(j.job_type == :DetailWall or j.job_type == :DetailFloor) and df.same_pos?(j, self)
})
designation.dig = :No
designation.smooth = 1
mapblock.flags.designated = true
end
else
return if mode != :No and designation.dig == :No and not designation.hidden and df.world.job_list.find { |j|
# someone already enroute to dig here, avoid 'Inappropriate dig square' spam
JobType::Type[j.job_type] == :Digging and df.same_pos?(j, self)
}
designation.dig = mode
mapblock.flags.designated = true if mode != :No
end
end
def spawn_liquid(quantity, is_magma=false, flowing=true)
designation.flow_size = quantity
designation.liquid_type = (is_magma ? :Magma : :Water)
designation.flow_forbid = true if is_magma or quantity >= 4
if flowing
mapblock.flags.update_liquid = true
mapblock.flags.update_liquid_twice = true
zf = df.world.map.z_level_flags[z]
zf.update = true
zf.update_twice = true
end
end
def spawn_water(quantity=7)
spawn_liquid(quantity)
end
def spawn_magma(quantity=7)
spawn_liquid(quantity, true)
end
# yield a serie of tiles until the block returns true, returns the matching tile
# the yielded tiles form a (squared) spiral centered here in the current zlevel
# eg for radius 4, yields (-4, -4), (-4, -3), .., (-4, 3),
# (-4, 4), (-3, 4), .., (4, 4), .., (4, -4), .., (-3, -4)
# then move on to radius 5
def spiral_search(maxradius=100, minradius=0, step=1)
if minradius == 0
return self if yield self
minradius += step
end
sides = [[0, 1], [1, 0], [0, -1], [-1, 0]]
(minradius..maxradius).step(step) { |r|
sides.length.times { |s|
dxr, dyr = sides[(s-1) % sides.length]
dx, dy = sides[s]
(-r...r).step(step) { |v|
t = offset(dxr*r + dx*v, dyr*r + dy*v)
return t if t and yield t
}
}
}
nil
end
# returns dx^2+dy^2+dz^2
def distance_to(ot)
(x-ot.x)**2 + (y-ot.y)**2 + (z-ot.z)**2
end
end
end