381 lines
15 KiB
Ruby
381 lines
15 KiB
Ruby
module DFHack
|
|
class << self
|
|
def building_find(what=:selected, y=nil, z=nil)
|
|
if what == :selected
|
|
case ui.main.mode
|
|
when :LookAround
|
|
k = ui_look_list.items[ui_look_cursor]
|
|
k.building if k.type == :Building
|
|
when :BuildingItems, :QueryBuilding
|
|
world.selected_building
|
|
when :Zones, :ZonesPenInfo, :ZonesPitInfo, :ZonesHospitalInfo
|
|
ui_sidebar_menus.zone.selected
|
|
end
|
|
|
|
elsif what.kind_of?(Integer)
|
|
# search by building.id
|
|
return world.buildings.all.binsearch(what) if not z
|
|
|
|
# search by coordinates
|
|
x = what
|
|
world.buildings.all.find { |b|
|
|
b.z == z and
|
|
if b.room.extents
|
|
dx = x - b.room.x
|
|
dy = y - b.room.y
|
|
dx >= 0 and dx < b.room.width and
|
|
dy >= 0 and dy < b.room.height and
|
|
b.room.extents[ dy*b.room.width + dx ] > 0
|
|
else
|
|
b.x1 <= x and b.x2 >= x and
|
|
b.y1 <= y and b.y2 >= y
|
|
end
|
|
}
|
|
|
|
elsif what.respond_to?(:x) or what.respond_to?(:pos)
|
|
# find the building at the same position
|
|
what = what.pos if what.respond_to?(:pos)
|
|
building_find(what.x, what.y, what.z)
|
|
|
|
else
|
|
raise "what what?"
|
|
end
|
|
end
|
|
|
|
# allocate a new building object
|
|
def building_alloc(type, subtype=-1, custom=-1)
|
|
cls = rtti_n2c[BuildingType::Classname[type].to_sym]
|
|
raise "invalid building type #{type.inspect}" if not cls
|
|
bld = cls.cpp_new
|
|
bld.race = ui.race_id
|
|
subtype = WorkshopType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Workshop
|
|
subtype = FurnaceType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Furnace
|
|
subtype = CivzoneType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Civzone
|
|
subtype = TrapType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Trap
|
|
bld.setSubtype(subtype)
|
|
bld.setCustomType(custom)
|
|
case type
|
|
when :Furnace; bld.melt_remainder[world.raws.inorganics.length] = 0
|
|
when :Coffin; bld.initBurialFlags
|
|
when :Trap; bld.unk_cc = 500 if bld.trap_type == :PressurePlate
|
|
when :Floodgate; bld.gate_flags.closed = true
|
|
end
|
|
bld
|
|
end
|
|
|
|
# used by building_setsize
|
|
def building_check_bridge_support(bld)
|
|
x1 = bld.x1-1
|
|
x2 = bld.x2+1
|
|
y1 = bld.y1-1
|
|
y2 = bld.y2+1
|
|
z = bld.z
|
|
(x1..x2).each { |x|
|
|
(y1..y2).each { |y|
|
|
next if ((x == x1 or x == x2) and
|
|
(y == y1 or y == y2))
|
|
if mb = map_block_at(x, y, z) and tile = mb.tiletype[x%16][y%16] and TiletypeShape::BasicShape[Tiletype::Shape[tile]] != :Open
|
|
bld.gate_flags.has_support = true
|
|
return
|
|
end
|
|
}
|
|
}
|
|
bld.gate_flags.has_support = false
|
|
end
|
|
|
|
# sets x2/centerx/y2/centery from x1/y1/bldtype
|
|
# x2/y2 preserved for :FarmPlot etc
|
|
def building_setsize(bld)
|
|
bld.x2 = bld.x1 if bld.x1 > bld.x2
|
|
bld.y2 = bld.y1 if bld.y1 > bld.y2
|
|
case bld.getType
|
|
when :Bridge
|
|
bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2
|
|
bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2
|
|
building_check_bridge_support(bld)
|
|
when :FarmPlot, :RoadDirt, :RoadPaved, :Stockpile, :Civzone
|
|
bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2
|
|
bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2
|
|
when :TradeDepot, :Shop
|
|
bld.x2 = bld.x1+4
|
|
bld.y2 = bld.y1+4
|
|
bld.centerx = bld.x1+2
|
|
bld.centery = bld.y1+2
|
|
when :SiegeEngine, :Windmill, :Wagon
|
|
bld.x2 = bld.x1+2
|
|
bld.y2 = bld.y1+2
|
|
bld.centerx = bld.x1+1
|
|
bld.centery = bld.y1+1
|
|
when :AxleHorizontal
|
|
if bld.is_vertical == 1
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2
|
|
else
|
|
bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2
|
|
bld.y2 = bld.centery = bld.y1
|
|
end
|
|
when :WaterWheel
|
|
if bld.is_vertical == 1
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.y2 = bld.y1+2
|
|
bld.centery = bld.y1+1
|
|
else
|
|
bld.x2 = bld.x1+2
|
|
bld.centerx = bld.x1+1
|
|
bld.y2 = bld.centery = bld.y1
|
|
end
|
|
when :Workshop, :Furnace
|
|
# Furnace = Custom or default case only
|
|
case bld.type
|
|
when :Quern, :Millstone, :Tool
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.y2 = bld.centery = bld.y1
|
|
when :Siege, :Kennels
|
|
bld.x2 = bld.x1+4
|
|
bld.y2 = bld.y1+4
|
|
bld.centerx = bld.x1+2
|
|
bld.centery = bld.y1+2
|
|
when :Custom
|
|
if bdef = world.raws.buildings.all.binsearch(bld.getCustomType)
|
|
bld.x2 = bld.x1 + bdef.dim_x - 1
|
|
bld.y2 = bld.y1 + bdef.dim_y - 1
|
|
bld.centerx = bld.x1 + bdef.workloc_x
|
|
bld.centery = bld.y1 + bdef.workloc_y
|
|
end
|
|
else
|
|
bld.x2 = bld.x1+2
|
|
bld.y2 = bld.y1+2
|
|
bld.centerx = bld.x1+1
|
|
bld.centery = bld.y1+1
|
|
end
|
|
when :ScrewPump
|
|
case bld.direction
|
|
when :FromEast
|
|
bld.x2 = bld.centerx = bld.x1+1
|
|
bld.y2 = bld.centery = bld.y1
|
|
when :FromSouth
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.y2 = bld.centery = bld.y1+1
|
|
when :FromWest
|
|
bld.x2 = bld.x1+1
|
|
bld.y2 = bld.centery = bld.y1
|
|
bld.centerx = bld.x1
|
|
else
|
|
bld.x2 = bld.x1+1
|
|
bld.y2 = bld.centery = bld.y1
|
|
bld.centerx = bld.x1
|
|
end
|
|
when :Well
|
|
bld.bucket_z = bld.z
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.y2 = bld.centery = bld.y1
|
|
when :Construction
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.y2 = bld.centery = bld.y1
|
|
bld.setMaterialAmount(1)
|
|
return
|
|
else
|
|
bld.x2 = bld.centerx = bld.x1
|
|
bld.y2 = bld.centery = bld.y1
|
|
end
|
|
bld.setMaterialAmount((bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1)
|
|
end
|
|
|
|
# set building at position, with optional width/height
|
|
def building_position(bld, pos, w=nil, h=nil)
|
|
if pos.respond_to?(:x1)
|
|
x, y, z = pos.x1, pos.y1, pos.z
|
|
w ||= pos.x2-pos.x1+1 if pos.respond_to?(:x2)
|
|
h ||= pos.y2-pos.y1+1 if pos.respond_to?(:y2)
|
|
elsif pos.respond_to?(:x)
|
|
x, y, z = pos.x, pos.y, pos.z
|
|
else
|
|
x, y, z = pos
|
|
end
|
|
w ||= pos.w if pos.respond_to?(:w)
|
|
h ||= pos.h if pos.respond_to?(:h)
|
|
bld.x1 = x
|
|
bld.y1 = y
|
|
bld.z = z
|
|
bld.x2 = bld.x1+w-1 if w
|
|
bld.y2 = bld.y1+h-1 if h
|
|
building_setsize(bld)
|
|
end
|
|
|
|
# set map occupancy/stockpile/etc for a building
|
|
def building_setoccupancy(bld)
|
|
stockpile = (bld.getType == :Stockpile)
|
|
complete = (bld.getBuildStage >= bld.getMaxBuildStage)
|
|
extents = (bld.room.extents and bld.isExtentShaped)
|
|
|
|
z = bld.z
|
|
(bld.x1..bld.x2).each { |x|
|
|
(bld.y1..bld.y2).each { |y|
|
|
next if extents and bld.room.extents[bld.room.width*(y-bld.room.y)+(x-bld.room.x)] == 0
|
|
next if not mb = map_block_at(x, y, z)
|
|
des = mb.designation[x%16][y%16]
|
|
des.pile = stockpile
|
|
des.dig = :No
|
|
if complete
|
|
bld.updateOccupancy(x, y)
|
|
else
|
|
mb.occupancy[x%16][y%16].building = :Planned
|
|
end
|
|
}
|
|
}
|
|
end
|
|
|
|
# link bld into other rooms if it is inside their extents or vice versa
|
|
def building_linkrooms(bld)
|
|
world.buildings.other[:ANY_FREE].each { |ob|
|
|
next if ob.z != bld.z
|
|
if bld.is_room and bld.room.extents
|
|
next if ob.is_room or ob.x1 < bld.room.x or ob.x1 >= bld.room.x+bld.room.width or ob.y1 < bld.room.y or ob.y1 >= bld.room.y+bld.room.height
|
|
next if bld.room.extents[bld.room.width*(ob.y1-bld.room.y)+(ob.x1-bld.room.x)] == 0
|
|
ui.equipment.update.buildings = true
|
|
bld.children << ob
|
|
ob.parents << bld
|
|
elsif ob.is_room and ob.room.extents
|
|
next if bld.is_room or bld.x1 < ob.room.x or bld.x1 >= ob.room.x+ob.room.width or bld.y1 < ob.room.y or bld.y1 >= ob.room.y+ob.room.height
|
|
next if ob.room.extents[ob.room.width*(bld.y1-ob.room.y)+(bld.x1-ob.room.x)].to_i == 0
|
|
ui.equipment.update.buildings = true
|
|
ob.children << bld
|
|
bld.parents << ob
|
|
end
|
|
}
|
|
end
|
|
|
|
# link the building into the world, set map data, link rooms, bld.id
|
|
def building_link(bld)
|
|
bld.id = df.building_next_id
|
|
df.building_next_id += 1
|
|
|
|
world.buildings.all << bld
|
|
bld.categorize(true)
|
|
building_setoccupancy(bld) if bld.isSettingOccupancy
|
|
building_linkrooms(bld)
|
|
end
|
|
|
|
# set a design for the building
|
|
def building_createdesign(bld, rough=true)
|
|
job = bld.jobs[0]
|
|
job.mat_type = bld.mat_type
|
|
job.mat_index = bld.mat_index
|
|
if bld.needsDesign
|
|
bld.design = BuildingDesign.cpp_new
|
|
bld.design.flags.rough = rough
|
|
end
|
|
end
|
|
|
|
# creates a job to build bld, return it
|
|
def building_linkforconstruct(bld)
|
|
building_link bld
|
|
ref = GeneralRefBuildingHolderst.cpp_new
|
|
ref.building_id = bld.id
|
|
job = Job.cpp_new
|
|
job.job_type = :ConstructBuilding
|
|
job.pos = [bld.centerx, bld.centery, bld.z]
|
|
job.references << ref
|
|
bld.jobs << job
|
|
job_link job
|
|
job
|
|
end
|
|
|
|
# construct a building with items or JobItems
|
|
def building_construct(bld, items)
|
|
job = building_linkforconstruct(bld)
|
|
rough = false
|
|
items.each { |item|
|
|
if items.kind_of?(JobItem)
|
|
item.quantity = (bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1 if item.quantity < 0
|
|
job.job_items << item
|
|
else
|
|
job_attachitem(job, item, :Hauled)
|
|
end
|
|
rough = true if item.getType == :BOULDER
|
|
bld.mat_type = item.getMaterial if bld.mat_type == -1
|
|
bld.mat_index = item.getMaterialIndex if bld.mat_index == -1
|
|
}
|
|
building_createdesign(bld, rough)
|
|
end
|
|
|
|
# construct an abstract building (stockpile, farmplot, ...)
|
|
def building_construct_abstract(bld)
|
|
case bld.getType
|
|
when :Stockpile
|
|
max = df.world.buildings.other[:STOCKPILE].map { |s| s.stockpile_number }.max
|
|
bld.stockpile_number = max.to_i + 1
|
|
when :Civzone
|
|
max = df.world.buildings.other[:ANY_ZONE].map { |z| z.zone_num }.max
|
|
bld.zone_num = max.to_i + 1
|
|
end
|
|
building_link bld
|
|
if !bld.flags.exists
|
|
bld.flags.exists = true
|
|
bld.initFarmSeasons
|
|
end
|
|
end
|
|
|
|
def building_setowner(bld, unit)
|
|
return unless bld.is_room
|
|
return if bld.owner == unit
|
|
|
|
if bld.owner
|
|
if idx = bld.owner.owned_buildings.index { |ob| ob.id == bld.id }
|
|
bld.owner.owned_buildings.delete_at(idx)
|
|
end
|
|
if spouse = bld.owner.relations.spouse_tg and
|
|
idx = spouse.owned_buildings.index { |ob| ob.id == bld.id }
|
|
spouse.owned_buildings.delete_at(idx)
|
|
end
|
|
end
|
|
bld.owner = unit
|
|
if unit
|
|
unit.owned_buildings << bld
|
|
if spouse = bld.owner.relations.spouse_tg and
|
|
!spouse.owned_buildings.index { |ob| ob.id == bld.id } and
|
|
bld.canUseSpouseRoom
|
|
spouse.owned_buildings << bld
|
|
end
|
|
end
|
|
end
|
|
|
|
# creates a job to deconstruct the building
|
|
def building_deconstruct(bld)
|
|
job = Job.cpp_new
|
|
refbuildingholder = GeneralRefBuildingHolderst.cpp_new
|
|
job.job_type = :DestroyBuilding
|
|
refbuildingholder.building_id = bld.id
|
|
job.references << refbuildingholder
|
|
bld.jobs << job
|
|
job_link job
|
|
job
|
|
end
|
|
|
|
# check item flags to see if it is suitable for use as a building material
|
|
def building_isitemfree(i)
|
|
!i.flags.in_job and
|
|
!i.flags.in_inventory and
|
|
!i.flags.removed and
|
|
!i.flags.in_building and
|
|
!i.flags.owned and
|
|
!i.flags.forbid
|
|
end
|
|
|
|
# exemple usage
|
|
def buildbed(pos=cursor)
|
|
raise 'where to ?' if pos.x < 0
|
|
|
|
item = world.items.all.find { |i|
|
|
i.kind_of?(ItemBedst) and
|
|
building_isitemfree(i)
|
|
}
|
|
raise 'no free bed, build more !' if not item
|
|
|
|
bld = building_alloc(:Bed)
|
|
building_position(bld, pos)
|
|
building_construct(bld, [item])
|
|
end
|
|
end
|
|
end
|