Index: trunk/src/lua/lua_nova.cc
===================================================================
--- trunk/src/lua/lua_nova.cc	(revision 217)
+++ trunk/src/lua/lua_nova.cc	(revision 217)
@@ -0,0 +1,806 @@
+// 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_nova.hh"
+
+#include "nv/lua/lua_raw.hh"
+
+const char* NV_STATE      = "NV_STATE";
+const char* NV_BLUEPRINTS = "NV_BLUEPRINTS";
+
+// static nv::lua::state* nova_get_state( lua_State * L )
+// {
+// 	int stack = lua_gettop( L );
+// 	nv::lua::state* result = nullptr;
+// 	lua_getfield( L, LUA_REGISTRYINDEX, NV_STATE );
+// 	result = static_cast< nv::lua::state* >( lua_touserdata( L, -1 ) );
+// 	lua_settop( L, stack );
+// 	return result;
+// }
+
+// BLUEPRINTS
+
+static bool nova_blueprint_exists( lua_State * L, int index )
+{
+	lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS );
+	lua_pushvalue( L, index );
+	lua_rawget( L, -2 );
+	bool result = lua_istable( L, -1 );
+	lua_pop( L, 2 );
+	return result;
+}
+
+// static void nova_blueprint_push( lua_State * L, int index )
+// {
+// 	lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS );
+// 	lua_pushvalue( L, index );
+// 	lua_rawget( L, -2 );
+// 	if ( !lua_istable( L, -1 ) )
+// 	{
+// 		luaL_error( L, "lua.nova - blueprint \"%s\" doesn't exist!", lua_tolstring( L, index, 0 ) );
+// 	}
+// 	lua_replace( L, -2 );
+// 	lua_pop( L, 1 );
+// }
+
+static int nova_apply_blueprint( lua_State * L ); // forward
+
+// returns true if value modified
+static bool nova_check_type_raw( lua_State * L, int iid, int ifield, int ivalue, int itype )
+{
+	iid    = lua_absindex( L, iid );
+	ifield = lua_absindex( L, ifield );
+	ivalue = lua_absindex( L, ivalue );
+	itype  = lua_absindex( L, itype );
+
+	switch ( lua_type( L, itype ) )
+	{
+	case LUA_TFUNCTION :
+		lua_pushvalue( L, itype );  // type function
+		lua_pushvalue( L, iid );    // identifier
+		lua_pushvalue( L, ifield ); // field name
+		lua_pushvalue( L, ivalue ); // field value
+		lua_call( L, 3, 1 );
+		if ( lua_isnoneornil( L, -1 ) )
+		{
+			lua_pop( L, 1 );
+		}
+		else
+		{
+			return true;
+		}
+	case LUA_TSTRING :
+		if ( lua_type( L, ivalue ) != LUA_TTABLE )
+		{
+			luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, table of blueprint \"%s\" expected, %s found!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, ifield, 0 ), lua_tolstring( L, itype, 0 ), lua_typename( L, lua_type( L, ivalue ) ) );
+		}
+		lua_pushcfunction( L, nova_apply_blueprint );
+		lua_pushvalue( L, ivalue );
+		lua_pushvalue( L, itype );
+		lua_pushvalue( L, iid );
+		lua_pushstring( L, "." );
+		lua_pushvalue( L, ifield );
+		lua_concat( L, 3 );
+		lua_call( L, 3, 0 );
+	case LUA_TNUMBER :
+		if (lua_tointeger( L, itype ) != lua_type( L, ivalue ) && lua_tointeger( L, itype ) > 0)
+		{
+			luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, %s expected, %s found!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, ifield, 0 ), lua_typename( L, lua_tointeger( L, itype ) ), lua_typename( L, lua_type( L, ivalue ) ) );
+		}
+
+	}
+	return false;
+}
+
+static void nova_apply_blueprint_values_raw( lua_State * L, int ibase, int iproto, int iset, int iid )
+{
+	ibase  = lua_absindex( L, ibase );
+	iproto = lua_absindex( L, iproto );
+	iset   = lua_absindex( L, iset );
+	iid    = lua_absindex( L, iid );
+
+	lua_pushnil( L );
+	while ( lua_next( L, iproto ) != 0 )
+	{
+		// Key -2, Value -1
+		int ikey   = lua_absindex( L, -2 );
+		int ivalue = lua_absindex( L, -1 );
+
+		// Base[Key]
+		lua_pushvalue( L, ikey );
+		lua_rawget( L, ibase );
+		bool present = !lua_isnil( L, -1 );
+		lua_pop( L, 1 );
+
+		if (lua_type( L, ivalue ) == LUA_TTABLE)
+		{
+			// Value[1]
+			lua_rawgeti( L, ivalue, 1 );
+			bool mandatory = lua_toboolean( L, -1 ) != 0;
+			bool nested    = lua_type( L, -1 ) == LUA_TTABLE;
+			lua_pop( L, 1 );
+
+			if (!present)
+			{
+				if (mandatory)
+				{
+					luaL_error( L, "lua.nova - %s has no required field \"%s\"!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, -2, 0 ) );
+				}
+				else
+				{
+					lua_pushvalue( L, ikey ); // push Key
+					lua_rawgeti( L, ivalue, 3 ); // Value[3]
+					if ( lua_type( L, -1 ) == LUA_TTABLE )
+					{
+						nlua_shallowcopy( L, -1 );
+						lua_replace( L, -2 );
+					}
+					lua_rawset( L, ibase ); // Base[Key] =
+				}
+			}
+
+			if (nested)
+			{
+				lua_rawgeti( L, ivalue, 1 ); // v[1]
+				lua_pushvalue( L, ikey );  // Key
+				lua_rawget( L, ibase );  // Base[Key]
+				lua_rawget( L, -2 );     // v[1][Base[Key]]
+				lua_replace( L, -2 );    // down to 1 stack
+				if (!lua_isnil( L, -1 ))
+				{
+					nova_apply_blueprint_values_raw( L, ibase, -1, iset, iid );
+				}
+				lua_pop( L, 1 );
+			}
+			else
+			if (present)
+			{
+				lua_rawgeti( L, ivalue, 2 ); // Value[2]
+				lua_pushvalue( L, ikey );  // Key
+				lua_rawget( L, ibase );  // Base[Key]
+				if ( nova_check_type_raw( L, iid, ikey, -1, -2 ) )
+				{
+					lua_pushvalue( L, ikey );  // Key
+					lua_insert( L, -2 );
+					lua_rawset( L, ibase );
+				}
+				lua_pop( L, 2 );
+			}
+		}
+		else
+		{
+			if (present) luaL_error( L, "lua.nova - %s - field \"%s\" cannot be redefined!", lua_tolstring( L, iid, 0 ), lua_tolstring( L, ikey, 0 ) );
+			// non-table entries get forced copied
+			lua_pushvalue( L, ikey ); // push Key
+			lua_pushvalue( L, ivalue ); // push Value
+			lua_rawset( L, ibase ); // Base[Key] =
+		}
+		lua_pushvalue( L, ikey ); // Key
+		lua_pushnil( L );
+		lua_rawset( L, iset );  // Set[Key] = nil
+		lua_pop( L, 1 );
+	}
+}
+
+static int nova_apply_blueprint_values( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TTABLE );  // base
+	luaL_checktype( L, 2, LUA_TTABLE );  // prototype
+	luaL_checktype( L, 3, LUA_TTABLE );  // set
+	luaL_checktype( L, 4, LUA_TSTRING ); // ident
+	lua_settop( L, 4 );
+	nova_apply_blueprint_values_raw( L, 1, 2, 3, 4 );
+	return 0;
+}
+
+static void nova_apply_blueprint_raw( lua_State * L, int ibase, int iproto, int iid )
+{
+	ibase    = lua_absindex( L, ibase );
+	iproto   = lua_absindex( L, iproto );
+	iid      = lua_absindex( L, iid );
+	nlua_tokeyset( L, ibase );
+	int iset = lua_absindex( L, -1 );
+
+	nova_apply_blueprint_values_raw( L, ibase, iproto, iset, iid );
+
+	lua_pushnil( L );
+	while ( lua_next( L, iset ) != 0 )
+	{
+		luaL_error( L, "lua.nova - %s has unknown field \"%s\"!", lua_tolstring( L, 3, 0 ), lua_tolstring( L, -2, 0 ) );
+		lua_pop(L, 1);
+	}
+	lua_pop(L, 1);
+}
+
+static int nova_apply_blueprint( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TTABLE );  // base
+	if ( lua_type( L, 2 ) == LUA_TSTRING )
+	{
+		lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS );
+		lua_pushvalue( L, 2 );
+		lua_rawget( L, -2 );
+		lua_replace( L, 2 );
+	}
+	luaL_checktype( L, 2, LUA_TTABLE );  // prototype
+	luaL_checktype( L, 3, LUA_TSTRING ); // ident
+	lua_settop( L, 3 );
+	nova_apply_blueprint_raw( L, 1, 2, 3 );
+	return 1;
+}
+
+// stack[1] - table
+// up[1]    - blueprint_id
+// up[2]    - inherit_id
+static int nova_register_blueprint_closure( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TTABLE );
+	lua_getfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS );
+
+	if ( lua_isnil( L, lua_upvalueindex(2) ) )
+	{
+		lua_pushvalue( L, lua_upvalueindex(1) );
+		lua_pushvalue( L, 1 );
+		lua_rawset( L, -3 );
+	}
+	else
+	{
+		lua_pushvalue( L, lua_upvalueindex(1) );
+		lua_pushvalue( L, lua_upvalueindex(2) );
+		lua_rawget( L, -3 );
+		nlua_shallowcopy( L, -1 );
+		lua_replace( L, -2 );
+		nlua_shallowmerge( L, 1 );
+		lua_rawset( L, -3 );
+	}
+
+	lua_pop( L, 1 );
+	return 0;
+}
+
+static int nova_register_blueprint( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TSTRING );
+	if ( nova_blueprint_exists( L, 1 ) )
+	{
+		luaL_error( L, "lua.nova - blueprint \"%s\" already registered!", lua_tolstring( L, 1, 0 ) );
+	}
+	if ( lua_gettop( L ) > 1 )
+	{
+		luaL_checktype( L, 2, LUA_TSTRING );
+		if ( !nova_blueprint_exists( L, 2 ) )
+		{ 
+			luaL_error( L, "lua.nova - blueprint \"%s\" doesn't exist!", lua_tolstring( L, 2, 0 ) );
+		}
+	}
+	else
+	{
+		lua_pushnil( L );
+	}
+	lua_pushcclosure( L, nova_register_blueprint_closure, 2 );
+	return 1;
+}
+
+
+static int nova_array_register( lua_State * L )
+{
+	// storage.__counter++
+	lua_pushstring( L, "__counter" );
+	lua_pushvalue( L, -1 );
+	lua_rawget( L, 1 );
+	int count = 0;
+	if ( !lua_isnil( L, -1 ) ) count = lua_tointeger( L, -1 );
+	lua_pop( L, 1 );
+	count++;
+	lua_pushinteger( L, count );
+	lua_rawset( L, 1 );
+
+	// element.nid = __counter
+	lua_pushstring( L, "nid" );
+	lua_pushinteger( L, count );
+	lua_rawset( L, 2 );
+
+	// storage[ __counter ] = element
+	lua_pushinteger( L, count );
+	lua_pushvalue( L, 2 );
+	lua_rawset( L, 1 );
+
+	// return nid
+	lua_pushinteger( L, count );
+	return 1;
+}
+
+static int nova_register( lua_State * L )
+{
+	nova_array_register(L);
+
+	// element.id = element.id 
+	lua_pushstring( L, "id" );
+	lua_rawget( L, 2 );
+	if ( lua_isnil( L, -1 ) )
+		luaL_error( L, "lua.nova - element without id!" );
+	
+	// storage[element.id] = element
+	lua_pushvalue( L, -1 );
+	lua_pushvalue( L, 2 );
+	lua_rawset( L, 1 );
+
+	// core.define( element.id, element.nid )
+	//LuaSystem.FDefines[ iID ] := lua_tointeger( L, 3 );
+
+	// return id
+	return 1;
+}
+
+static int nova_declare( lua_State * L )
+{
+	if ( lua_gettop( L ) <  1 ) return 0;
+	if ( lua_gettop( L ) == 1 ) lua_pushboolean( L, false );
+	lua_settop( L, 2 );
+	lua_pushglobaltable( L );
+	lua_insert( L, 1 );
+	lua_rawset( L, -3 );
+	return 0;
+}
+
+static int nova_iif( lua_State * L )
+{
+	lua_settop( L, lua_toboolean( L, 1 ) ? 2 : 3 );
+	return 1;
+}
+
+static int nova_create_seq_function_closure( lua_State * L )
+{
+	int fc   = lua_tointeger( L, lua_upvalueindex( 1 ) );
+	int args = lua_gettop( L );
+	for ( int fi = 1; fi <= fc; fi++ )
+	{
+		lua_pushvalue( L, lua_upvalueindex( fi + 1 ) );
+		for ( int i = 1; i <= args; i++ )
+			lua_pushvalue( L, i );
+		lua_call( L, args, 0 );
+	}
+	return 0;
+}
+
+static int nova_create_seq_function( lua_State * L )
+{
+	int count    = lua_gettop( L );
+	int upvalues = count;
+	for ( int i = 1; i <= count; i++ )
+		if ( lua_isnoneornil( L, i ) )
+			upvalues--;
+		else
+			luaL_checktype( L, i, LUA_TFUNCTION );
+
+	if ( upvalues == 0 ) return 0;
+
+	lua_pushinteger( L, upvalues );
+	for ( int i = 1; i <= count; i++ )
+		if ( !lua_isnoneornil( L, i ) )
+			lua_pushvalue( L, i );
+
+	if ( upvalues == 1 ) return 1;
+	lua_pushcclosure( L, nova_create_seq_function_closure, upvalues + 1 );
+	return 1;
+}
+
+static int nova_type_FLAGS( lua_State * L )
+{
+	if ( lua_type( L, 3 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, flags expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) );
+	nlua_toset( L, 3 );
+	return 1;
+}
+
+static int nova_type_BLUEPRINT( lua_State * L )
+{
+	if ( lua_type( L, 3 ) != LUA_TSTRING ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, blueprint id expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) );
+	if ( !nova_blueprint_exists( L, 3 ) )  luaL_error( L, "lua.nova - \"%s.%s\" - blueprint \"%s\" isn't valid!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_tolstring( L, 3, 0 ) );
+	return 0;
+}
+
+static int nova_type_ARRAY_closure( lua_State * L )
+{
+	if ( lua_type( L, 3 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, ARRAY expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) );
+	lua_settop( L, 3 );
+	lua_pushvalue( L, lua_upvalueindex(1) ); // push type index 4
+
+	lua_pushvalue( L, 1 );
+	lua_pushstring( L, "." );
+	lua_pushvalue( L, 2 );
+	lua_concat( L, 3 ); // new ident index 5
+
+	lua_pushnil(L);
+	while ( lua_next( L, 3 ) != 0 )
+	{
+		// key (index -2), value (index -1)
+		if ( lua_type( L, -2 ) != LUA_TNUMBER ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, ARRAY expected, field found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ) );
+		if ( nova_check_type_raw( L, 5, -2, -1, 4 ) ) 
+		{
+			lua_pushvalue( L, -3 );  // Key
+			lua_insert( L, -2 );
+			lua_rawset( L, -3 ); // update Value
+		}
+		lua_pop( L, 1 );
+	}
+	return 0;
+}
+
+static int nova_type_ARRAY( lua_State * L )
+{
+	if ( lua_gettop( L ) != 1 ) luaL_error( L, "lua.nova - Misuse of nova.TARRAY type - usage is core.TARRAY( type )" );
+	lua_pushcclosure( L, nova_type_ARRAY_closure, 1 );
+	return 1;
+}
+
+static int nova_type_MAP_closure( lua_State * L )
+{
+	if ( lua_type( L, 3 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, MAP expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) );
+
+	lua_settop( L, 3 );
+	lua_pushvalue( L, lua_upvalueindex(1) ); // push key type index 4
+	lua_pushvalue( L, lua_upvalueindex(2) ); // push value type index 5
+
+	lua_pushvalue( L, 1 );
+	lua_pushstring( L, "." );
+	lua_pushvalue( L, 2 );
+	lua_concat( L, 3 ); // new ident index 6
+
+	lua_pushnil(L);
+	while ( lua_next( L, 3 ) != 0 )
+	{
+		// key (index -2), value (index -1)
+		if ( nova_check_type_raw( L, 6, -2, -2, 4 ) ) luaL_error( L, "lua.nova - \"%s.%s\" - KEY type can't be mutable!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ) );
+		if ( nova_check_type_raw( L, 6, -2, -1, 5 ) )
+		{
+			lua_pushvalue( L, -3 );  // Key
+			lua_insert( L, -2 );
+			lua_rawset( L, -3 ); // update Value
+		}
+		lua_pop( L, 1 );
+	}
+	return 0;
+}
+
+static int nova_type_MAP( lua_State * L )
+{
+	if ( lua_gettop( L ) != 2 ) luaL_error( L, "lua.nova - Misuse of nova.TMAP type - usage is core.TMAP( keytype, valuetype )" );
+	lua_pushcclosure( L, nova_type_MAP_closure, 2 );
+	return 1;
+}
+
+static int nova_type_ID_closure( lua_State * L )
+{
+	if ( lua_type( L, 3 ) != LUA_TSTRING ) luaL_error( L, "lua.nova - \"%s.%s\" - type mismatch, ID expected, %s found!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ), lua_typename( L, lua_type( L, 3 ) ) );
+	lua_settop( L, 3 );
+	lua_pushvalue( L, lua_upvalueindex(1) ); // push type index 4
+	if ( lua_type( L, -1 ) == LUA_TSTRING )
+	{
+		lua_pushglobaltable( L );
+		lua_pushvalue( L, -2 );
+		lua_rawget( L, -2 );
+		lua_replace( L, -2 );
+	}
+	if ( lua_type( L, -1 ) != LUA_TTABLE ) luaL_error( L, "lua.nova - not a valid storage table in TID!" );
+	lua_pushvalue( L, 3 );
+	lua_rawget( L, -2 );
+	if ( lua_isnoneornil( L, -1 ) )	luaL_error( L, "lua.nova - \"%s.%s\" - valid ID expected!", lua_tolstring( L, 1, 0 ), lua_tolstring( L, 2, 0 ) );
+	return 0;
+}
+
+static int nova_type_ID( lua_State * L )
+{
+	if ( lua_gettop( L ) != 1 ) luaL_error( L, "lua.nova - Misuse of nova.TID type - usage is core.TID( storage )" );
+	lua_pushcclosure( L, nova_type_ID_closure, 1 );
+	return 1;
+}
+
+static int nova_create_constructor_impl( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TTABLE );
+	lua_pushvalue( L, lua_upvalueindex( 1 ) ); // id
+	//ident = vlua_tostring( L, -1 );
+	//if LuaSystem.Defines.Exists( ident ) then
+	//luaL_error( L, 'Redefinition of id "%s"!', lua_tolstring( L, -1, nil ) );
+	lua_setfield( L, 1, "id" );
+	lua_getfield( L, 1, "blueprint" );
+
+	if ( lua_isnil( L, -1 ) && (!lua_isnoneornil( L, lua_upvalueindex( 3 ) ) ) )  // blueprint
+	{
+		lua_pushvalue( L, lua_upvalueindex( 3 ) );
+		lua_replace( L, -2 );
+	}
+	if ( lua_isnil( L, -1 ) ) // storage.__blueprint
+	{
+		lua_getfield( L, lua_upvalueindex( 2 ), "__blueprint" );
+		lua_replace( L, -2 );
+	}
+	if ( !lua_isnil( L, -1 ) )
+	{
+		lua_getfield( L, lua_upvalueindex( 2 ), "__name" ); // storage.__name
+		lua_pushfstring( L, "%s[%s]", lua_tostring( L, -1 ), lua_tostring( L, lua_upvalueindex( 1 ) ) );
+		lua_replace( L, -2 );
+
+		lua_pushcfunction( L, nova_apply_blueprint );
+		lua_pushvalue( L, 1 );
+		lua_pushvalue( L, -4 );
+		lua_pushvalue( L, -4 );
+		lua_call( L, 3, 0 );
+		lua_pop( L, 2 );
+	}
+
+	lua_pushcfunction( L, nova_register );
+	lua_pushvalue( L, lua_upvalueindex( 2 ) );
+	lua_pushvalue( L, 1 );
+	lua_call( L, 2, 0 );
+
+	if ( !lua_isnoneornil( L, lua_upvalueindex( 4 ) ) ) // constructor
+	{
+		lua_pushvalue( L, lua_upvalueindex( 4 ) ); // constructor
+		lua_pushvalue( L, 1 );
+		lua_call( L, 1, 0 );
+	}
+
+	lua_pushvalue( L, lua_upvalueindex( 1 ) ); // id
+	return 1;
+}
+
+static int nova_create_constructor_closure( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TSTRING );
+	if ( lua_gettop( L ) > 1 )
+	{
+		lua_settop( L, 2 );
+		luaL_checktype( L, 2, LUA_TSTRING );
+		lua_pushvalue( L, lua_upvalueindex( 1 ) );
+		lua_insert( L, -2 );
+	}
+	else
+	{
+		lua_settop( L, 1 );
+		lua_pushvalue( L, lua_upvalueindex( 1 ) );
+		lua_pushvalue( L, lua_upvalueindex( 2 ) );
+	}
+	lua_pushvalue( L, lua_upvalueindex( 3 ) );
+	lua_settop( L, 4 );
+
+	lua_pushcclosure(L, nova_create_constructor_impl, 4);
+	return 1;
+}
+
+static int nova_create_constructor( lua_State * L )
+{
+	if ( lua_type( L, 1 ) == LUA_TSTRING )
+	{
+		lua_getglobal( L, lua_tostring( L, 1 ) );
+		lua_replace( L, 1 );
+	}
+	luaL_checktype( L, 1, LUA_TTABLE );
+	if ( !lua_isnoneornil( L, 2 ) ) luaL_checktype( L, 2, LUA_TSTRING );
+	if ( !lua_isnoneornil( L, 3 ) ) luaL_checktype( L, 3, LUA_TFUNCTION );
+	lua_settop( L, 3 );
+	lua_pushcclosure(L, nova_create_constructor_closure, 3);
+	return 1;
+}
+
+static int nova_create_array_constructor_closure( lua_State * L )
+{
+	bool blueprint = true;
+	luaL_checktype( L, 1, LUA_TTABLE );
+
+	lua_getfield( L, lua_upvalueindex( 1 ), "__blueprint" );
+	if ( lua_type( L, -1 ) == LUA_TBOOLEAN ) blueprint = lua_toboolean( L, -1 ) != 0;
+	lua_pop( L, 1 );
+
+	if ( blueprint )
+	{
+		lua_getfield( L, 1, "blueprint" );
+		if ( lua_isnil( L, -1 ) && (!lua_isnoneornil( L, lua_upvalueindex( 2 ) )) ) // blueprint
+		{
+			lua_pushvalue( L, lua_upvalueindex( 2 ) );
+			lua_replace( L, -2 );
+		}
+
+		if ( lua_isnil( L, -1 ) ) // storage.__blueprint
+		{
+			lua_getfield( L, lua_upvalueindex( 1 ), "__blueprint" );
+			lua_replace( L, -2 );
+		}
+
+		if ( !lua_isnil( L, -1 ) && ( lua_type( L, -1 ) != LUA_TBOOLEAN ) )
+		{
+			lua_getfield( L, lua_upvalueindex( 1 ), "__name" ); // storage.__name
+			lua_pushfstring( L, "%s[%d]", lua_tostring( L, -1 ), lua_objlen(L,lua_upvalueindex( 1 ))+1 );
+			lua_replace( L, -2 );
+
+			lua_pushcfunction( L, nova_apply_blueprint );
+			lua_pushvalue( L, 1 );
+			lua_pushvalue( L, -4 );
+			lua_pushvalue( L, -4 );
+			lua_call( L, 3, 0 );
+			lua_pop( L, 2 );
+		}
+	}
+
+	lua_pushcfunction( L, nova_array_register );
+	lua_pushvalue( L, lua_upvalueindex( 1 ) );
+	lua_pushvalue( L, 1 );
+	lua_call( L, 2, 1 );
+
+	if (!lua_isnoneornil( L, lua_upvalueindex( 3 ) )) // constructor
+	{
+		lua_pushvalue( L, lua_upvalueindex( 3 ) ); // constructor
+		lua_pushvalue( L, 1 );
+		lua_call( L, 1, 0 );
+	}
+
+	return 1;
+}
+
+static int nova_create_array_constructor( lua_State * L )
+{
+	if ( lua_type( L, 1 ) == LUA_TSTRING )
+	{
+		lua_getglobal( L, lua_tostring( L, 1 ) );
+		lua_replace( L, 1 );
+	}
+	luaL_checktype( L, 1, LUA_TTABLE );
+	if ( lua_isnoneornil( L, 2 ) )
+	{
+		if ( lua_type( L, 2 ) != LUA_TBOOLEAN )	luaL_checktype( L, 2, LUA_TSTRING );
+	}
+	if ( !lua_isnoneornil( L, 3 ) ) luaL_checktype( L, 3, LUA_TFUNCTION );
+	lua_settop( L, 3 );
+	lua_pushcclosure(L, nova_create_array_constructor_closure, 3);
+	return 1;
+}
+
+static int nova_register_storage( lua_State * L )
+{
+	bool blueprint = false;
+	bool constr    = false;
+	luaL_checktype( L, 1, LUA_TSTRING );
+	if ( !lua_isnoneornil( L, 2 ) )
+	{
+		luaL_checktype( L, 2, LUA_TSTRING );
+		blueprint = true;
+	}
+	if ( !lua_isnoneornil( L, 3 ) )
+	{
+		luaL_checktype( L, 3, LUA_TFUNCTION );
+		constr = true;
+	}
+	lua_settop( L, 3 );
+
+	lua_pushglobaltable( L );
+	lua_pushvalue( L, 1 );
+	lua_rawget( L, -2 );
+
+	if ( !lua_isnil( L, -1 ) ) luaL_error( L, "lua.nova - storage \"%s\" already registered!", lua_tolstring( L, lua_upvalueindex(1), 0 ) );
+
+	lua_newtable( L ); // g t
+	lua_pushvalue( L, 1 ); // g t name
+	lua_pushvalue( L, -2 ); // g t name, duplicate table
+	lua_pushvalue( L, 1 ); // g t name, dt, name
+
+	lua_setfield( L, -2, "__name" );
+	if ( blueprint )
+	{
+		lua_pushvalue( L, 2 );
+		lua_setfield( L, -2, "__blueprint" );
+	}
+	lua_rawset( L, 4 );
+
+	lua_pushcfunction( L, nova_create_constructor );
+	lua_pushvalue( L, -2 ); // storage
+
+	if ( blueprint )
+		lua_pushvalue( L, 2 );
+	else 
+		lua_pushnil( L );
+
+	if ( constr )
+		lua_pushvalue( L, 3 );
+	else 
+		lua_pushnil( L );
+	lua_call( L, 3, 1 );
+	return 1;
+}
+
+static int nova_register_array_storage( lua_State * L )
+{
+	bool blueprint = false;
+	bool constr    = false;
+	luaL_checktype( L, 1, LUA_TSTRING );
+	if ( !lua_isnoneornil( L, 2 ) )
+	{
+		if ( lua_type( L, 2 ) != LUA_TBOOLEAN ) luaL_checktype( L, 2, LUA_TSTRING );
+		blueprint = true;
+	}
+	if ( !lua_isnoneornil( L, 3 ) )
+	{
+		luaL_checktype( L, 3, LUA_TFUNCTION );
+		constr = true;
+	}
+	lua_settop( L, 3 );
+	lua_pushglobaltable( L );
+
+	lua_pushvalue( L, 1 );
+	lua_rawget( L, -2 );
+	if ( !lua_isnil( L, -1 ) ) luaL_error( L, "storage \"%s\" already registered!", lua_tolstring( L, lua_upvalueindex(1), 0 ) );
+
+	lua_newtable( L );
+	lua_pushvalue( L, 1 ); // name
+	lua_pushvalue( L, -2 ); // duplicate table
+	lua_pushvalue( L, 1 ); // name
+	lua_setfield( L, -2, "__name" );
+	if ( blueprint )
+	{
+		lua_pushvalue( L, 2 );
+		lua_setfield( L, -2, "__blueprint" );
+	}
+	lua_rawset( L, 4 );
+
+	lua_pushcfunction( L, nova_create_array_constructor );
+	lua_pushvalue( L, -2 ); // storage
+
+	if ( blueprint )
+		lua_pushvalue( L, 2 );
+	else 
+		lua_pushnil( L );
+
+	if ( constr )
+		lua_pushvalue( L, 3 );
+	else
+		lua_pushnil( L );
+
+	lua_call( L, 3, 1 );
+	return 1;
+}
+
+static const luaL_Reg nova_f[] = {
+	{ "TFLAGS",                    nova_type_FLAGS },
+	{ "TBLUEPRINT",                nova_type_BLUEPRINT },
+	{ "TARRAY",                    nova_type_ARRAY },
+	{ "TMAP",                      nova_type_MAP },
+	{ "TID",                       nova_type_ID },
+	{ "iif",                       nova_iif },
+	{ "register",                  nova_register },
+	{ "array_register",            nova_array_register },
+	{ "declare",                   nova_declare },
+	{ "create_seq_function",       nova_create_seq_function },
+	{ "apply_blueprint",           nova_apply_blueprint },
+	{ "apply_blueprint_values",    nova_apply_blueprint_values },
+	{ "register_blueprint",        nova_register_blueprint },
+	{ "create_constructor",        nova_create_constructor },
+	{ "register_storage",          nova_register_storage },
+	{ "register_array_storage",    nova_register_array_storage },
+	{ "create_array_constructor",  nova_create_array_constructor },
+	{NULL, NULL}
+};
+
+
+int luaopen_nova( lua_State * L )
+{
+	lua_createtable( L, 0, 0 );
+	nlua_register( L, nova_f, -1 );
+	return 1;
+}
+
+void nv::lua::register_nova( state* a_state )
+{ 
+	lua_State* L = a_state->get_raw();
+	int stack = lua_gettop( L );
+
+	lua_pushlightuserdata( L, a_state );
+	lua_setfield( L, LUA_REGISTRYINDEX, NV_STATE );
+
+	lua_newtable( L );
+	lua_setfield( L, LUA_REGISTRYINDEX, NV_BLUEPRINTS );
+
+	luaL_requiref( L, "nova", luaopen_nova, 1 );
+	lua_settop( L, stack );
+}
Index: trunk/src/lua/lua_state.cc
===================================================================
--- trunk/src/lua/lua_state.cc	(revision 216)
+++ trunk/src/lua/lua_state.cc	(revision 217)
@@ -8,4 +8,5 @@
 
 #include "nv/lua/lua_raw.hh"
+#include "nv/lua/lua_nova.hh"
 #include "nv/logging.hh"
 #include "nv/string.hh"
@@ -235,4 +236,5 @@
 			lib->func( m_state );
 		}
+		register_nova( this );
 	}
 
@@ -337,4 +339,20 @@
 	}
 	deep_pointer_copy( -1, o );
+	return luaL_ref( m_state, LUA_REGISTRYINDEX );
+}
+
+lua::reference lua::state::register_proto( object * o, const char* storage )
+{
+	stack_guard guard( this );
+	lua_getglobal( m_state, storage );
+	if ( lua_isnil( m_state, -1 ) )
+	{
+		NV_THROW( runtime_error, std::string( storage ) + " storage not registered!" );
+	}
+	lua_getfield( m_state, -1, o->get_id().c_str() );
+	if ( lua_isnil( m_state, -1 ) )
+	{
+		NV_THROW( runtime_error, std::string( o->get_id() ) + " not found in " + std::string( storage ) + " storage!" );
+	}
 	return luaL_ref( m_state, LUA_REGISTRYINDEX );
 }
Index: trunk/src/object.cc
===================================================================
--- trunk/src/object.cc	(revision 216)
+++ trunk/src/object.cc	(revision 217)
@@ -16,10 +16,26 @@
 
 object::object()
-	: m_root( nullptr ), m_id(), m_name(), m_uid(0), m_lua_index(lua::ref_none), m_parent( nullptr ), m_children(), m_child_count(0)
+	: m_root( nullptr )
+	, m_id()
+	, m_name()
+	, m_uid(0)
+	, m_lua_index(lua::ref_none)
+	, m_lua_proto_index(lua::ref_none)
+	, m_parent( nullptr )
+	, m_children()
+	, m_child_count(0)
 {
 }
 
 object::object( root* aroot, const string& aid )
-	: m_root( aroot ), m_id(aid), m_name(), m_uid( 0 ), m_lua_index(lua::ref_none), m_parent( nullptr ), m_children(), m_child_count(0)
+	: m_root( aroot )
+	, m_id( aid )
+	, m_name()
+	, m_uid(0)
+	, m_lua_index(lua::ref_none)
+	, m_lua_proto_index(lua::ref_none)
+	, m_parent( nullptr )
+	, m_children()
+	, m_child_count(0)
 {
 	if ( m_root )
@@ -191,5 +207,5 @@
 }
 
-void nv::object::register_with_lua( const char* lua_name /*= nullptr*/ )
+void nv::object::register_with_lua( const char* lua_name, const char* storage )
 {
 	lua::state* state = get_root()->get_lua_state();
@@ -198,10 +214,10 @@
 		if ( lua_name != nullptr )
 		{
-			m_lua_index = state->register_object( this, lua_name );
-		}
-		else
-		{
-			m_lua_index = state->register_object( this );
-		}
-	}
-}
+			m_lua_index       = state->register_object( this, lua_name );
+		}
+		if ( storage != nullptr )
+		{
+			m_lua_proto_index = state->register_proto( this, storage );
+		}
+	}
+}
