Skip to content

Instantly share code, notes, and snippets.

@Quit
Last active August 30, 2016 23:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Quit/c25203e4ffc95230c33547d86d98f073 to your computer and use it in GitHub Desktop.
Save Quit/c25203e4ffc95230c33547d86d98f073 to your computer and use it in GitHub Desktop.
local terrain_config = radiant.resources.load_json('stonehearth:terrain_blocks')
local biome_metadata = radiant.resources.load_json('stonehearth:biome:temperate')
local biome_config = radiant.resources.load_json(biome_metadata.generation_file)
function log(msg, ...)
_host:log('dump', 0, string.format(msg, ...))
end
log('start dumping...')
local types_by_tag = {}
for t, d in pairs(terrain_config.block_types) do
types_by_tag[d.tag] = t
end
local colors_by_type = biome_config.palettes.spring
local f = io.open('terrain.txt', 'w')
-- Terrain
local cubes , water, entities, buildings, farms = 0, 0, 0, 0, 0
log('dump terrain')
local terrain_component = radiant.terrain.get_terrain_component()
local world_bounds = terrain_component:get_bounds()
local tiled_region = terrain_component:get_tiles()
local tile_size = tiled_region:get_tile_size()
local index_bounds = Cube3(
Point3(
math.floor(world_bounds.min.x / tile_size.x),
math.floor(world_bounds.min.y / tile_size.y),
math.floor(world_bounds.min.z / tile_size.z)
),
Point3(
math.floor(world_bounds.max.x / tile_size.x),
math.floor(world_bounds.max.y / tile_size.y),
math.floor(world_bounds.max.z / tile_size.z)
)
)
local cubes = 0
local CLUSTER_SIZE = 4 -- bigger values equals longer loading times because of optimization, but probably faster loading speed.
for point in index_bounds:each_point() do
local tile_index = math.floor(point.x / CLUSTER_SIZE) * (world_bounds.max.z - world_bounds.min.z) + math.floor(point.z / CLUSTER_SIZE)
local boxed_region = tiled_region:find_tile(point)
if boxed_region then
local region = boxed_region:modify(function(cursor)
cursor:force_optimize_by_merge('SHVR Terrain Export')
for cube in cursor:each_cube() do
cubes = cubes + 1
-- cube.tag is the tag
local bt = types_by_tag[cube.tag]
local bc = colors_by_type[bt]
f:write(table.concat({ 'CUBE', cube.min.x, cube.min.y, cube.min.z, cube.max.x, cube.max.y, cube.max.z, bc, tile_index }, '|'))
f:write('\n')
end
end)
end
end
-- Waterasd
log('dump water')
for _, water_entity in pairs(stonehearth.hydrology:get_water_bodies()) do
for cube in water_entity:get_component('stonehearth:water'):get_region():get():each_cube() do
water = water + 1
local world_pos = radiant.entities.get_world_location(water_entity)
local min, max = world_pos + cube.min, world_pos + cube.max
f:write(table.concat({ 'WATER', min.x, min.y, min.z, max.x, max.y, max.z, water }, '|'))
f:write('\n')
end
end
-- Helper function to dump buildings - we'll iterate through all entities anyway.
local building_component = radiant.mods.require('stonehearth.components.building.building_component')
-- Get STRUCTURE_TYPES. This is a local variable only available to the building component
-- (and certified lua users. Such as me.)
-- Everything related to building dumping is extremely fragile and, as such, subject to local witchcraft
-- laws. Please consult with your magician lawyer of your choice about the legality of this script
-- in your state.
local STRUCTURE_TYPES
for i = 1, 10 do
local name, value = debug.getupvalue(building_component._fill_in_structures_map, i)
if name == 'STRUCTURE_TYPES' then
STRUCTURE_TYPES = value
break
end
end
if not STRUCTURE_TYPES then
error('Could not "find" structure types')
end
local function dump_building(entity, building)
local components = {}
for _, kind in ipairs(STRUCTURE_TYPES) do
for id, entry in pairs(building._sv._structures[kind]) do
-- if not next(entry.inverse_dependencies) then
table.insert(components, entry.entity)
-- end
end
end
-- Ein Satz mit x: Das war wohl nix.
if #components == 0 then
return
end
buildings = buildings + 1
-- This code doesn't just look like BuildingComponent:blow_up()
-- it _is_ BuildingComponent:blow_up().
-- Minus the blowing up bit. We might split the stuff apart later too
-- for "aesthetical" reasons; but for now, let's not.
for _, entity in pairs(components) do
if entity:is_valid() then
local cp = entity:get_component('stonehearth:construction_progress')
if cp and entity:get_uri() ~= 'stonehearth:build:prototypes:scaffolding' and cp:get_fabricator_component() and cp:get_color_region() then
local fab = cp:get_fabricator_component()
local world_origin = radiant.entities.get_world_grid_location(entity)
local proj = fab:get_project()
-- Project region doesn't contain color info! So, just use it as a 'stencil', and intersect the construction_data's
-- color region with that stencil to get our color region.
local color_region = proj:get_component('stonehearth:construction_data'):get_color_region():get():intersect_region(
proj:get_component('destination'):get_region():get())
for cube in color_region:each_cube() do
cube:translate(radiant.entities.get_world_location(entity))
f:write(table.concat(
{
'BUILDING',
cube.min.x, -- adjust some values because otherwise, the entities don't line up properly
cube.min.y,
cube.min.z,
cube.max.x,
cube.max.y,
cube.max.z,
string.format('#%06x', cube.tag),
buildings
}, '|'))
f:write('\n')
end
end
end
end
end
local errors = {}
local function dump_farm(entity, farmer_field)
local data = farmer_field._sv
local farmer_field_data = radiant.entities.get_component_data(entity, 'stonehearth:farmer_field')
local _tilled_dirt_model = farmer_field_data.tilled_dirt
local _furrow_dirt_model = farmer_field_data.furrow_dirt
local size_x = data.size.x
local size_y = data.size.y
local contents = data.contents
local dirt_node_array = {}
local world_pos = radiant.entities.get_world_location(entity)
for x = 1, size_x do
for y = 1, size_y do
local dirt_plot = contents[x][y]
if dirt_plot and dirt_plot.x ~= nil then -- need to check for nil for backward compatibility reasons
local model = dirt_plot.is_furrow and _furrow_dirt_model or _tilled_dirt_model
f:write(table.concat({
'ENTITY',
entity:get_uri(),
world_pos.x + dirt_plot.x - 0.45,
world_pos.y + 0.05,
world_pos.z + dirt_plot.y - 0.55,
radiant.entities.get_facing(entity),
model,
0.1, -- scale is undefined, but we HEAVILY assume it's the default 0.1
'', -- no collision box
}, '|'))
f:write('\n')
end
end
end
farms = farms + 1
end
-- Entities
log('dump entities')
-- local done = {}
radiant.entities.for_all_children(radiant.entities.get_root_entity(), function(entity)
local world_pos = radiant.entities.get_world_location(entity)
if not world_pos then
return
end
local ret, err = pcall(function()
local uri = entity:get_uri()
if not uri or uri == '' then
return
end
-- -- It's possible that an entity is reached twice...
-- -- for whatever reason.
-- => Nope, it's not, but buildings still have ghost entities and I have no clue how to distinguish those
-- (short of filtering everything with "_ghost.json" in its uri)
-- if done[entity:get_id()] then
-- log('%s (%d; %s) already reached once!', tostring(entity), entity:get_id(), entity:get_uri())
-- return
-- end
-- done[entity:get_id()] = true
local building_component = entity:get_component('stonehearth:building')
if building_component then
dump_building(entity, building_component)
return
end
local farmer_field = entity:get_component('stonehearth:farmer_field')
if farmer_field then
dump_farm(entity, farmer_field)
return
end
local render_info = entity:get_component('render_info')
if not render_info then
return
end
local variant_name = entity:get_component('render_info'):get_model_variant()
if variant_name == '' then
variant_name = 'default'
end
local layer = entity:get_component('model_variants'):get_variant(variant_name)
local models = {}
for model in layer:each_model() do
table.insert(models, model)
end
local mob = entity:get_component('mob')
local scale = render_info:get_scale()
local region_offset = mob:get_region_origin()
local model_offset = mob:get_model_origin() / scale
local collision_regions = {}
local rcs = entity:get_component('region_collision_shape')
if rcs then
local total_offset = -region_offset + model_offset
for cube in rcs:get_region():get():each_cube() do
local min, max = cube.min + total_offset, cube.max + total_offset
table.insert(collision_regions, table.concat({
min.x,
min.y,
min.z,
max.x,
max.y,
max.z
}, '/'))
end
end
f:write(table.concat({
'ENTITY',
uri .. '(' .. entity:get_id() .. ')',
world_pos.x - model_offset.x, -- Yes, minus, because Unity and Stonehearth had a little fallout about which hand is the better one.
world_pos.y + model_offset.y,
world_pos.z + model_offset.z,
radiant.entities.get_facing(entity),
table.concat(models, ','),
scale,
table.concat(collision_regions, ';')
}, '|'))
f:write('\n')
entities = entities + 1
end)
if not ret then
log('CANNOT LOAD STUFF: ' .. entity:get_uri() .. ': ' .. err)
end
end)
log('dump complete')
f:close()
return { cubes = cubes, water = water, entities = entities, buildings = buildings, farms = farms, errors = errors }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment