mcimport/mc2mt.lua
2013-02-03 21:34:04 +01:00

247 lines
8.9 KiB
Lua

--[[
Import maps from Minecraft Classic
Copyright (C) 2013 Sokomine
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
--]]
-- TODO: use real water if the water-node is sourrounded by anything but air (what is on top doesn't matter)
-- reads a level.dat file from minecraft classic and imports it
-- based on worldedit:deserialize
-- returns the number of nodes changed
mcimport.deserialize_mc = function(originpos, value, dim, name)
local max_x = 0; -- can be set to override automatic detection
local max_y;
local max_z;
local grass_level = 0; -- z-value/heigth at which the grass is
local offset = 486; -- position where the block data starts in file
local block_transform = {
[0] = "air", -- field 0 doesn't exist in LUA? anyway - air has to be handled differently (and ignored most of the time)
[1] = "mcimport:stone",
[2] = "mcimport:dirt_with_grass",
[3] = "mcimport:dirt",
[4] = "mcimport:cobble",
[5] = "mcimport:wood",
[6] = "mcimport:sapling",
[7] = "mcimport:bedrock",
[8] = "mcimport:stationary_water", -- water that does not flow
[9] = "mcimport:stationary_water", -- stationary water
[10] = "mcimport:stationary_lava", -- lava that does not flow
[11] = "mcimport:stationary_lava", -- stationary lava
[12] = "mcimport:sand",
[13] = "mcimport:gravel",
[14] = "mcimport:gold_ore",
[15] = "mcimport:iron_ore",
[16] = "mcimport:coal_ore",
[17] = "mcimport:tree",
[18] = "mcimport:leaves",
[19] = "mcimport:sponge",
[20] = "mcimport:glass",
[21] = "mcimport:wool_red",
[22] = "mcimport:wool_orange",
[23] = "mcimport:wool_yellow",
[24] = "mcimport:wool_lime",
[25] = "mcimport:wool_green",
[26] = "mcimport:wool_aqua",
[27] = "mcimport:wool_cyan",
[28] = "mcimport:wool_blue",
[29] = "mcimport:wool_purple",
[30] = "mcimport:wool_indigo",
[31] = "mcimport:wool_violet",
[32] = "mcimport:wool_magenta",
[33] = "mcimport:wool_pink",
[34] = "mcimport:wool_black",
[35] = "mcimport:wool_grey",
[36] = "mcimport:wool_white",
[37] = "mcimport:flower_yellow",
[38] = "mcimport:flower_red",
[39] = "mcimport:brown_mushroom",
[40] = "mcimport:red_mushroom",
[41] = "mcimport:gold",
[42] = "mcimport:steelblock",
[43] = "mcimport:double_stairs",
[44] = "mcimport:stairs",
[45] = "mcimport:brick",
[46] = "mcimport:tnt",
[47] = "mcimport:bookshelf",
[48] = "mcimport:mossycobble",
[49] = "mcimport:obsidian",
}
-- for node changes
local pos = {x=0, y=0, z=0}
local node = {name="", param1=0, param2=0}
local env = minetest.env;
local anz_transformed = 0;
local i, x, y, z;
local neuer_block;
-- the dimensions of the map have to be supplied by the user
max_x = dim.x;
max_y = dim.y;
max_z = dim.z;
anz_blocks = max_x * max_y * max_z;
grass_level = max_x / 2;
print( "Reading information about "..
tostring( offset+anz_blocks+2 ).." blocks.");
if( max_x<1 or max_y<1 or max_z<1 ) then
print "Impossible map size.";
return;
end
if( offset+anz_blocks+2 > string.len( value )) then
print( "Wrong map size. Found less blocks than expected.");
return;
end
if( offset+anz_blocks+2 < string.len( value )) then
print( "Wrong map size. Found more blocks than expected.");
return;
end
print( "Importing them might take a while.");
x = 0;
y = 0;
z = 0;
-- check the tailing bytes (which are supposed to have the value 112)
for i = (offset+anz_blocks), (offset+anz_blocks+2) do
nr = string.byte( value, i, i );
if (not(nr==112)) then
print( "Warning: Byte at the end does not have expected value 70 (hex)");
end
end
-- move originpos as needed to avoid recalculation for each block
originpos.x = originpos.x + max_x; -- somehow it got mirrored
originpos.y = originpos.y - grass_level; -- grass ought to be on the same level as the sourrounding grass; half sink the level into the floor
-- -- dirt beneath the "water"-level is of no interest - but air is
-- if( (y < grass_level and (nr ~= 3))
--- -- on the water/floor level grass is of no interest
-- or (y ==grass_level and (nr ~= 2))
-- -- above that air is of no interest
-- or (y > grass_level and (nr ~= 0))) then
-- if( i-offset ~= ( z*(max_x*max_y)+ (max_x * y) + x )) then
sub_length = 16;
air_etc = 0;
local player;
local target_coords;
for start_z = 0, math.floor(max_z/sub_length)-1 do
for start_y = 0, math.floor(max_y/sub_length)-1 do
for start_x = 0, math.floor(max_x/sub_length)-1 do
i = offset+( (max_x * max_y) * (start_z*sub_length) + (max_x * (start_y*sub_length)) + (start_x*sub_length) ) ;
--print("Starting at "..tostring( i-offset ).." for offset "..tostring( start_x ).." "..tostring( start_y ).." "..tostring( start_z ));
-- move the player who issued the command around in order to make sure that that part of the world has been generated
player = minetest.env:get_player_by_name( name );
if( player ~= nil ) then
target_coords = { x = (originpos.x - start_x + (sub_length/2)), y = (originpos.y+ start_z - (sub_length/2)), z = (originpos.z+ start_y - (sub_length/2))};
player:moveto(target_coords, false);
end
for z = (start_z*sub_length),(((start_z+1)*sub_length)-1) do
for y = (start_y*sub_length),(((start_y+1)*sub_length)-1) do
for x = (start_x*sub_length),(((start_x+1)*sub_length)-1) do
nr = string.byte( value, i, i );
--if( nr ~= 0 ) then -- if( not( nr==3 ) and not( nr==0 )) then -- ignore air and dirt
if( (nr ~= 3) and (nr ~= 0) ) then -- ignore air and dirt completely
neuer_block = block_transform[ nr ];
-- print( i, (i-offset), nr, x, y, z, neuer_block );
-- actually place the block
pos.x = originpos.x - x; -- somehow got mirrord; max_x has been added previously
pos.y = originpos.y + z; -- otherwise it ends up sideways; grass_level has already been substracted
pos.z = originpos.z + y;
node.name = neuer_block;
node.param1 = param1;
node.param2 = param2;
if( pos ~= nil and node ~= nil and neuer_block ~= nil) then
--print( "Placed "..tostring( neuer_block ).." at "..tostring( pos.x )..":"..tostring( pos.y )..":"..tostring( pos.z ));
env:add_node(pos, node);
else
print( "Error: Could not place "..tostring( neuer_block ).." [Code:"..tostring( nr ).."] at "..
tostring( pos.x )..":"..tostring( pos.y )..":"..tostring( pos.z ));
end
anz_transformed = anz_transformed + 1;
else
air_etc = air_etc + 1;
end
i = i+1; -- next block
end
i = i + max_x - sub_length; -- next row; skip blocks inbetween
end
i = i + ( (max_y - sub_length ) * max_x );
end
end
end
end
print( "Changed "..tostring( anz_transformed ).." blocks. Ignored "..tostring( air_etc ).." blocks of air and dirt beneath grass_level.");
return anz_transformed;
end
------ just for testing/debugging
-- local filename = "level.dat";
-- local file, err = io.open(filename, "rb");
-- if err ~= nil then
-- print "File not found.";
-- return;
-- end
-- local value = file:read("*a");
-- file:close();
-- local originpos = { x=0, y=0, z=0 };
---- local dimension = { x=256, y=256, z=256 };
-- local dimension = { x=16, y=16, z=16 };
-- mcimport.deserialize_mc( originpos, value, dimension );