// Copyright (C) 2012-2014 ChaosForge Ltd // 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 #include "nv/lua/lua_map_area.hh" #include "nv/stl/flags.hh" #include "nv/core/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::uchar8* ascii; nv::uint16 size_x; nv::uint16 size_y; }; // static bool nlua_is_map_tile( lua_State* L, int index ) // { // return luaL_testudata( L, index, NLUA_MAP_TILE_METATABLE ) != 0; // } // // static map_tile nlua_to_map_tile( lua_State* L, int index ) // { // return *(map_tile*)luaL_checkudata( L, index, NLUA_MAP_TILE_METATABLE ); // } static map_tile* nlua_to_pmap_tile( lua_State* L, int index ) { return (map_tile*)luaL_checkudata( L, index, NLUA_MAP_TILE_METATABLE ); } static 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::uchar8[ 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[ (nv::uint8)( 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 ( nv::uint16 line = 0; line < tile.size_x; line++ ) for ( nv::uint16 row = 0; row < tile.size_y; row++ ) { nv::uchar8 gylph = (nv::uchar8)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::uchar8[ 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 ), c ); } 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 ); break; case 2 : nlua_map_tile_flip_y( L ); break; case 3 : nlua_map_tile_flip_xy( L ); break; 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 ); }