Index: trunk/nv/lua/lua_map_tile.hh
===================================================================
--- trunk/nv/lua/lua_map_tile.hh	(revision 221)
+++ trunk/nv/lua/lua_map_tile.hh	(revision 221)
@@ -0,0 +1,26 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+/**
+ * @file lua_map_tile.hh
+ * @author Kornel Kisielewicz epyon@chaosforge.org
+ */
+
+#ifndef NV_LUA_MAP_TILE_HH
+#define NV_LUA_MAP_TILE_HH
+
+#include <nv/common.hh>
+#include <nv/lua/lua_state.hh>
+
+namespace nv
+{
+	namespace lua
+	{
+		void register_map_tile( lua_State* L );
+	}
+}
+
+#endif // NV_LUA_MAP_TILE_HH
Index: trunk/src/lua/lua_map_tile.cc
===================================================================
--- trunk/src/lua/lua_map_tile.cc	(revision 221)
+++ trunk/src/lua/lua_map_tile.cc	(revision 221)
@@ -0,0 +1,368 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/lua/lua_map_tile.hh"
+
+#include <numeric>
+#include "nv/lua/lua_map_area.hh"
+#include "nv/flags.hh"
+#include "nv/random.hh"
+#include "nv/lua/lua_area.hh"
+#include "nv/lua/lua_glm.hh"
+#include "nv/lua/lua_values.hh"
+#include "nv/lua/lua_raw.hh"
+
+static const char* NLUA_MAP_TILE_METATABLE = "map_tile";
+
+struct map_tile
+{
+	nv::uint8* data;
+	nv::char8* ascii;
+	nv::uint16 size_x;
+	nv::uint16 size_y;
+};
+
+
+bool nlua_is_map_tile( lua_State* L, int index )
+{
+	return luaL_testudata( L, index, NLUA_MAP_TILE_METATABLE ) != 0;
+}
+
+map_tile nlua_to_map_tile( lua_State* L, int index )
+{
+	return *(map_tile*)luaL_checkudata( L, index, NLUA_MAP_TILE_METATABLE );
+}
+
+map_tile* nlua_to_pmap_tile( lua_State* L, int index )
+{
+	return (map_tile*)luaL_checkudata( L, index, NLUA_MAP_TILE_METATABLE );
+}
+
+void nlua_push_map_tile( lua_State* L, const map_tile& tile )
+{
+	map_tile* result = (map_tile*)lua_newuserdata( L, sizeof(map_tile) );
+	*result = tile;
+	luaL_setmetatable( L, NLUA_MAP_TILE_METATABLE );
+}
+
+static int nlua_map_tile_new( lua_State* L )
+{
+	bool ascii = lua_toboolean( L, 4 ) != 0;
+	nv::map_area* map_area = nv::lua::detail::to_map_area( L, 3 );
+	lua_settop( L, 2 );
+	std::string code = nv::trimmed( lua_tostring( L, 1 ) );
+	nv::remove_chars( code, " \r\t" );
+
+	map_tile tile;
+
+	tile.size_y = (nv::uint16)( std::count( code.begin(), code.end(), '\n' ) + 1 );
+	tile.size_x = (nv::uint16)( code.find( '\n' ) );
+	if ( tile.size_x == 0 )
+	{
+		tile.size_x = (nv::uint16)code.length();
+	}
+	tile.data  = new nv::uint8[ tile.size_x * tile.size_y ];
+	tile.ascii = ( ascii ? new nv::char8[ tile.size_x * tile.size_y ] : nullptr );
+
+	nv::uint8 translation[256] = { 0 };
+	
+	// TODO: error reporting
+	if ( lua_istable( L, 2 ) )
+	{
+		lua_pushnil( L );
+		while ( lua_next( L, 2 ) != 0 )
+		{
+			// uses 'key' (at index -2) and 'value' (at index -1) */
+			if ( lua_isstring( L, -2 ) && lua_objlen( L, -2 ) == 1 )
+			{
+				translation[ lua_tostring( L, -2 )[0] ] = nv::uint8( map_area->string_to_id( lua_tostring( L, -1 ) ) );
+			}
+			// removes 'value'; keeps 'key' for next iteration */
+			lua_pop( L, 1 );
+		}
+	}
+
+	for ( int line = 0; line < tile.size_x; line++ )
+		for ( int row = 0; row < tile.size_y; row++ )
+		{
+			nv::char8 gylph = code[ row * ( tile.size_x + 1 ) + line ];
+			// TODO: check for errors
+			tile.data[ row * tile.size_x + line ] = translation[ gylph ];
+			if ( ascii ) tile.ascii[row * tile.size_x + line] = gylph;
+		}
+
+	nlua_push_map_tile( L, tile );
+	return 1;
+}
+
+static int nlua_map_tile_clone( lua_State* L )
+{
+	map_tile* old_tile = nlua_to_pmap_tile( L, 1 );
+	map_tile* new_tile = (map_tile*) lua_newuserdata( L, sizeof( map_tile ) );
+	new_tile->size_x = old_tile->size_x;
+	new_tile->size_y = old_tile->size_y;
+	nv::uint32 size  = new_tile->size_x * new_tile->size_y;
+	new_tile->data   = new nv::uint8[ size ];
+	new_tile->ascii  = nullptr;
+	if ( old_tile->ascii )
+	{
+		new_tile->ascii = new nv::char8[ size ];
+	}
+	std::copy( old_tile->data, old_tile->data + size, new_tile->data );
+	if ( old_tile->ascii ) std::copy( old_tile->ascii, old_tile->ascii + size, new_tile->ascii );
+
+	luaL_getmetatable( L, NLUA_MAP_TILE_METATABLE );
+	lua_setmetatable( L, -2 );
+	return 1;
+}
+
+static int nlua_map_tile_place( lua_State* L )
+{
+	map_tile* tile     = nlua_to_pmap_tile( L, 1 );
+	nv::map_area* area = nv::lua::detail::to_map_area( L, 2 );
+	nv::ivec2 coord    = nv::lua::detail::to_coord( L, 3 );
+
+	for ( nv::uint16 x = 0; x < tile->size_x; ++x )
+		for ( nv::uint16 y = 0; y < tile->size_y; ++y )
+		{
+			nv::uint8 c = tile->data[ y * tile->size_x + x ];
+			if ( c != 0 ) area->set_cell( coord + nv::ivec2( x, y ), tile->data[ y * tile->size_x + x ] );
+		}
+
+	return 0;
+}
+
+static nv::uint8* nlua_map_tile_flip_x_helper( nv::uint8* src, nv::uint16 sx, nv::uint16 sy )
+{
+	if ( src == nullptr ) return nullptr;
+	nv::uint8* data = new nv::uint8[ sx * sy ];
+	for ( nv::uint16 x = 0; x < sx; ++x )
+		for ( nv::uint16 y = 0; y < sy; ++y )
+		{
+			data[ y * sx + x ] = src[ ( y + 1 ) * sx - x - 1 ];
+		}
+	delete src;
+	return data;
+}
+
+static nv::uint8* nlua_map_tile_flip_y_helper( nv::uint8* src, nv::uint16 sx, nv::uint16 sy )
+{
+	if ( src == nullptr ) return nullptr;
+	nv::uint8* data = new nv::uint8[ sx * sy ];
+	for ( nv::uint16 x = 0; x < sx; ++x )
+		for ( nv::uint16 y = 0; y < sy; ++y )
+		{
+			data[ y * sx + x ] = src[ ( sy - y - 1 ) * sx + x ];
+		}
+	delete src;
+	return data;
+}
+
+static nv::uint8* nlua_map_tile_flip_xy_helper( nv::uint8* src, nv::uint16 sx, nv::uint16 sy )
+{
+	if ( src == nullptr ) return nullptr;
+	nv::uint8* data = new nv::uint8[ sx * sy ];
+	for ( nv::uint16 x = 0; x < sx; ++x )
+		for ( nv::uint16 y = 0; y < sy; ++y )
+		{
+			data[ y * sx + x ] = src[ ( sy - y ) * sx - x - 1 ];
+		}
+	delete src;
+	return data;
+}
+
+static int nlua_map_tile_flip_x( lua_State* L )
+{
+	map_tile* tile = nlua_to_pmap_tile( L, 1 );
+	tile->data     = nlua_map_tile_flip_x_helper( tile->data,  tile->size_x, tile->size_y );
+	tile->ascii    = nlua_map_tile_flip_x_helper( tile->ascii, tile->size_x, tile->size_y );
+	return 0;
+}
+
+static int nlua_map_tile_flip_y( lua_State* L )
+{
+	map_tile* tile = nlua_to_pmap_tile( L, 1 );
+	tile->data     = nlua_map_tile_flip_y_helper( tile->data,  tile->size_x, tile->size_y );
+	tile->ascii    = nlua_map_tile_flip_y_helper( tile->ascii, tile->size_x, tile->size_y );
+	return 0;
+}
+
+static int nlua_map_tile_flip_xy( lua_State* L )
+{
+	map_tile* tile = nlua_to_pmap_tile( L, 1 );
+	tile->data     = nlua_map_tile_flip_xy_helper( tile->data,  tile->size_x, tile->size_y );
+	tile->ascii    = nlua_map_tile_flip_xy_helper( tile->ascii, tile->size_x, tile->size_y );
+	return 0;
+}
+
+static int nlua_map_tile_flip_random( lua_State* L )
+{
+	switch ( nv::random::get().urand( 4 ) )
+	{
+	case 1 : nlua_map_tile_flip_x( L );
+	case 2 : nlua_map_tile_flip_y( L );
+	case 3 : nlua_map_tile_flip_xy( L );
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int nlua_map_tile_get_size( lua_State* L )
+{
+	map_tile* tile = nlua_to_pmap_tile( L, 1 );
+	nv::lua::detail::push_coord( L, nv::ivec2( tile->size_x, tile->size_y ) );
+	return 1;
+}
+
+static int nlua_map_tile_get_area( lua_State* L )
+{
+	map_tile* tile = nlua_to_pmap_tile( L, 1 );
+	nv::lua::detail::push_area( L, nv::rectangle( nv::ivec2(1,1), nv::ivec2( tile->size_x, tile->size_y ) ) );
+	return 1;
+}
+
+static int nlua_map_tile_expand( lua_State* L )
+{
+	// TODO: lots of error checking
+	map_tile* tile = nlua_to_pmap_tile( L, 1 );
+	// assert( tile^.ascii == nullptr );
+	std::vector< nv::uint8 > sizes_x = nlua_tobytearray( L, 2 );
+	std::vector< nv::uint8 > sizes_y = ( lua_istable( L, 3 ) ? nlua_tobytearray( L, 3 ) : sizes_x );
+
+	nv::uint16 org_x = tile->size_x;
+	nv::uint16 org_y = tile->size_y;
+	nv::uint16 new_x = (nv::uint16)std::accumulate( sizes_x.begin(), sizes_x.end(), 0 );
+	nv::uint16 new_y = (nv::uint16)std::accumulate( sizes_y.begin(), sizes_y.end(), 0 );
+
+	nv::uint8* data = new nv::uint8[ new_x * new_y ];
+
+	nv::uint16 line_count = 0;
+	for ( nv::uint16 line = 0; line < org_y; ++line )
+		for ( nv::uint16 count = 0; count < sizes_y[ line ]; ++count )
+		{
+			nv::uint16 px = 0;
+			for ( nv::uint16 x = 0; x < org_x; ++x )
+				for ( nv::uint16 c = 0; c < sizes_x[ x ]; ++c )
+				{
+					data[ px + line_count * new_x ] = tile->data[ x + line * org_x ];
+					px++;
+				}
+			line_count++;
+		}
+
+	delete tile->data;
+	tile->size_x = new_x;
+	tile->size_y = new_y;
+	tile->data   = data;
+	return 0;
+}
+
+static int nlua_map_tile_raw_get( lua_State* L )
+{
+	map_tile* tile = (map_tile*)lua_touserdata( L, 1 );
+	if ( lua_type( L, 2 ) == LUA_TNUMBER )
+	{
+		lua_pushinteger( L, tile->data[ ( lua_tointeger( L, 2 ) - 1 ) + ( lua_tointeger( L, 3 ) - 1 ) * tile->size_x ] );
+	}
+	else
+	{
+		nv::ivec2 coord = nv::lua::detail::to_coord( L, 2 );
+		lua_pushinteger( L, tile->data[ ( coord.x - 1 ) + ( coord.y - 1 ) * tile->size_x ] );
+	}
+	return 1;
+}
+
+static int nlua_map_tile_raw_set( lua_State* L )
+{
+	map_tile* tile = (map_tile*)lua_touserdata( L, 1 );
+	if ( lua_type( L, 2 ) == LUA_TNUMBER )
+	{
+		tile->data[ ( lua_tointeger( L, 2 ) - 1 ) + ( lua_tointeger( L, 3 ) - 1 ) * tile->size_x ] = (nv::uint8)lua_tointeger( L, 3 );
+	}
+	else
+	{
+		nv::ivec2 coord = nv::lua::detail::to_coord( L, 2 );
+		tile->data[ ( coord.x - 1 ) + ( coord.y - 1 ) * tile->size_x ] = (nv::uint8)lua_tointeger( L, 3 );
+	}
+	return 0;
+}
+
+static int nlua_map_tile_ascii_get( lua_State* L )
+{
+	map_tile* tile = (map_tile*)lua_touserdata( L, 1 );
+	if ( lua_type( L, 2 ) == LUA_TNUMBER )
+	{
+		lua_pushinteger( L, tile->ascii[ ( lua_tointeger( L, 2 ) - 1 ) + ( lua_tointeger( L, 3 ) - 1 ) * tile->size_x ] );
+	}
+	else
+	{
+		nv::ivec2 coord = nv::lua::detail::to_coord( L, 2 );
+		lua_pushinteger( L, tile->ascii[ ( coord.x - 1 ) + ( coord.y - 1 ) * tile->size_x ] );
+	}
+	return 1;
+}
+
+static int nlua_map_tile_ascii_set( lua_State* L )
+{
+	map_tile* tile = (map_tile*)lua_touserdata( L, 1 );
+	if ( lua_type( L, 2 ) == LUA_TNUMBER )
+	{
+		tile->ascii[ ( lua_tointeger( L, 2 ) - 1 ) + ( lua_tointeger( L, 3 ) - 1 ) * tile->size_x ] = (nv::uint8)lua_tointeger( L, 3 );
+	}
+	else
+	{
+		nv::ivec2 coord = nv::lua::detail::to_coord( L, 2 );
+		tile->ascii[ ( coord.x - 1 ) + ( coord.y - 1 ) * tile->size_x ] = (nv::uint8)lua_tointeger( L, 3 );
+	}
+	return 0;
+}
+
+static int nlua_map_tile_gc( lua_State* L )
+{
+	map_tile* tile = (map_tile*)lua_touserdata( L, 1 );
+	if ( tile != nullptr )
+	{
+		delete tile->data;
+		if ( tile->ascii ) delete tile->ascii;
+	}
+	return 0;
+}
+
+static const luaL_Reg nlua_map_tile_sf[] = {
+	{ "new",   nlua_map_tile_new },
+	{ NULL, NULL }
+};
+
+static const luaL_Reg nlua_map_tile_f[] = {
+	{ "place",      nlua_map_tile_place },
+	{ "clone",      nlua_map_tile_clone },
+	{ "flip_x",     nlua_map_tile_flip_x },
+	{ "flip_y",     nlua_map_tile_flip_y },
+	{ "flip_xy",    nlua_map_tile_flip_xy },
+	{ "flip_random",nlua_map_tile_flip_random },
+	{ "get_size",   nlua_map_tile_get_size },
+	{ "get_area",   nlua_map_tile_get_area },
+	{ "expand",     nlua_map_tile_expand },
+	{ "raw_get",    nlua_map_tile_raw_get },
+	{ "raw_set",    nlua_map_tile_raw_set },
+	{ "get_ascii",  nlua_map_tile_ascii_get },
+	{ "set_ascii",  nlua_map_tile_ascii_set },
+	{ "__gc",       nlua_map_tile_gc },
+	{ NULL, NULL }
+};
+
+void nv::lua::register_map_tile( lua_State * L )
+{
+	// TODO: check if __gc is used!
+	luaL_newmetatable( L, NLUA_MAP_TILE_METATABLE );
+	lua_pushvalue( L, -1 );
+	lua_setfield( L, -2, "__index" );
+	nlua_register( L, nlua_map_tile_f, -1 );
+
+	lua_createtable( L, 0, 0 );
+	nlua_register( L, "map_tile", nlua_map_tile_sf );
+}
