int custom_object_callable::get_key_slot(const std::string& key)
{
	std::map<std::string, int>::const_iterator itor = keys_to_slots().find(key);
	if(itor == keys_to_slots().end()) {
		return -1;
	}

	return itor->second;
}
custom_object_callable::custom_object_callable(bool is_singleton)
{

    static const std::string CustomObjectProperties[] = {
        "consts", "type", "active",
        "time_in_animation", "time_in_animation_delta", "frame_in_animation", "level",
        "animation", "available_animations",
        "hitpoints", "max_hitpoints", "mass", "label", "x", "y", "xy", "z",
        "relative_x", "relative_y", "spawned_by", "spawned_children",
        "parent", "pivot", "zorder", "zsub_order",
        "previous_y", "x1", "x2", "y1", "y2", "w", "h", "mid_x", "mid_y", "mid_xy", "midpoint_x", "midpoint_y", "midpoint_xy",
        "solid_rect", "solid_mid_x", "solid_mid_y", "solid_mid_xy",
        "img_mid_x", "img_mid_y", "img_mid_xy", "img_w", "img_h", "img_wh", "front", "back", "cycle", "facing",
        "upside_down", "up", "down", "velocity_x", "velocity_y", "velocity_xy",
        "velocity_magnitude", "velocity_angle",
        "accel_x", "accel_y", "accel_xy", "gravity_shift", "platform_motion_x",
        "registry", "globals", "vars", "tmp", "group", "rotate",
        "me", "self",
        "red", "green", "blue", "alpha", "text_alpha", "damage", "hit_by",
        "distortion", "is_standing", "standing_info",
        "near_cliff_edge", "distance_to_cliff",
        "slope_standing_on", "underwater",
        "previous_water_bounds", "water_bounds", "water_object",
        "driver", "is_human", "invincible",
        "sound_volume", "destroyed", "is_standing_on_platform", "standing_on",
        "shader", "effects", "variations",
        "attached_objects", "call_stack", "lights",
        "solid_dimensions_in", "solid_dimensions_not_in",
        "collide_dimensions_in", "collide_dimensions_not_in",
        "brightness", "current_generator", "tags", "draw_area", "scale",
        "activation_area", "clip_area",
        "always_active", "activation_border", "fall_through_platforms", "has_feet",
        "x_schedule", "y_schedule", "rotation_schedule", "schedule_speed",
        "schedule_expires",
        "platform_area", "platform_offsets", "custom_draw",
        "draw_primitives", "event_handlers",
        "use_absolute_screen_coordinates",
        "widgets", "textv", "body", "mouseover_delay", "mouseover_area",
        "ctrl_up", "ctrl_down", "ctrl_left", "ctrl_right",
        "ctrl_attack", "ctrl_jump", "ctrl_tongue",
    };
    ASSERT_EQ(NUM_CUSTOM_OBJECT_PROPERTIES, sizeof(CustomObjectProperties)/sizeof(*CustomObjectProperties));

    if(global_entries().empty()) {
        for(int n = 0; n != sizeof(CustomObjectProperties)/sizeof(*CustomObjectProperties); ++n) {
            global_entries().push_back(entry(CustomObjectProperties[n]));
        }

        for(int n = 0; n != global_entries().size(); ++n) {
            keys_to_slots()[global_entries()[n].id] = n;
        }
    }

//	global_entries()[CUSTOM_OBJECT_LEVEL].type_definition = &level::get_formula_definition();
    global_entries()[CUSTOM_OBJECT_PARENT].type_definition = is_singleton ? this : &instance();

    entries_ = global_entries();
}
custom_object_callable::custom_object_callable()
{

	static const std::string CustomObjectProperties[] = {
	"consts", "type", "active",
	"time_in_animation", "time_in_animation_delta", "level",
	"animation", "available_animations",
	"hitpoints", "max_hitpoints", "mass", "label", "x", "y", "xy", "z",
	"relative_x", "relative_y", "parent", "pivot", "zorder", "zsub_order",
	"previous_y", "x1", "x2", "y1", "y2", "w", "h", "midpoint_x", "midpoint_y",
	"solid_rect", "img_w", "img_h", "front", "back", "cycle", "facing",
	"upside_down", "up", "down", "velocity_x", "velocity_y",
	"accel_x", "accel_y", "gravity_shift", "platform_motion_x",
	"registry", "globals", "vars", "tmp", "group", "rotate",
	"me", "self",
	"red", "green", "blue", "alpha", "text_alpha", "damage", "hit_by",
	"distortion", "is_standing", "near_cliff_edge", "distance_to_cliff",
	"slope_standing_on", "underwater", "water_bounds", "water_object",
	"driver", "is_human", "invincible",
	"sound_volume", "destroyed", "is_standing_on_platform", "standing_on",
	"fragment_shaders", "vertex_shaders", "shader", "variations",
	"attached_objects", "call_stack", "lights",
	"solid_dimensions_in", "solid_dimensions_not_in",
	"collide_dimensions_in", "collide_dimensions_not_in",
	"brightness", "current_generator", "tags", "draw_area", "scale",
	"activation_area", "clip_area",
	"always_active", "activation_border", "fall_through_platforms", "has_feet",
	"x_schedule", "y_schedule", "rotation_schedule", "schedule_speed",
	"platform_area",
	"ctrl_up", "ctrl_down", "ctrl_left", "ctrl_right",
	"ctrl_attack", "ctrl_jump", "ctrl_tongue",
};
	ASSERT_EQ(NUM_CUSTOM_OBJECT_PROPERTIES, sizeof(CustomObjectProperties)/sizeof(*CustomObjectProperties));

	if(global_entries().empty()) {
		for(int n = 0; n != sizeof(CustomObjectProperties)/sizeof(*CustomObjectProperties); ++n) {
			global_entries().push_back(entry(CustomObjectProperties[n]));
		}

		for(int n = 0; n != global_entries().size(); ++n) {
			keys_to_slots()[global_entries()[n].id] = n;
		}
	}

	entries_ = global_entries();
}
custom_object_callable::custom_object_callable(bool is_singleton)
{
	if(is_singleton) {
		instance_ptr = this;
		set_type_name("custom_obj");
	}

	//make sure 'library' is initialized as a valid type.
	game_logic::get_library_definition();

	static const Property CustomObjectProperties[] = {
	{ "value", "any" },
	{ "_data", "any" },
	{ "arg", "object" },
	{ "consts", "any" },
	{ "type", "string" },
	{ "active", "any" },
	{ "lib", "library" },

	{ "time_in_animation", "int" },
	{ "time_in_animation_delta", "int" },
	{ "frame_in_animation", "int" },
	{ "level", "any" },

	{ "animation", "string/string|map" },
	{ "available_animations", "[string]" },

	{ "hitpoints", "int" },
	{ "max_hitpoints", "int" },
	{ "mass", "int" },
	{ "label", "string" },
	{ "x", "int/int|decimal" },
	{ "y", "int/int|decimal" },
	{ "xy", "[int]" },
	{ "z", "int" },

	{ "relative_x", "int/int|decimal" },
	{ "relative_y", "int/int|decimal" },
	{ "spawned_by", "null|custom_obj" },
	{ "spawned_children", "[custom_obj]" },

	{ "parent", "null|custom_obj" },
	{ "pivot", "string" },
	{ "zorder", "int" },
	{ "zsub_order", "int" },

	{ "previous_y", "int" },
	{ "x1", "int/int|decimal" },
	{ "x2", "int/int|decimal" },
	{ "y1", "int/int|decimal" },
	{ "y2", "int/int|decimal" },
	{ "w", "int" },
	{ "h", "int" },
	{ "mid_x", "int/int|decimal" },
	{ "mid_y", "int/int|decimal" },
	{ "mid_xy", "[int]" },
	{ "midpoint_x", "int/int|decimal" },
	{ "midpoint_y", "int/int|decimal" },
	{ "midpoint_xy", "[int]" },

    { "is_solid", "bool" },
	{ "solid_rect", "rect_obj" },
	{ "solid_mid_x", "int" },
	{ "solid_mid_y", "int" },
	{ "solid_mid_xy", "[int]" }, 

	{ "img_mid_x", "int" },
	{ "img_mid_y", "int" },
	{ "img_mid_xy", "int" },
	{ "img_w", "int" },
	{ "img_h", "int" },
	{ "img_wh", "int" },
	{ "front", "int" },
	{ "back", "int" },
	{ "cycle", "int" },
	{ "facing", "int" },
	
	{ "upside_down", "int" },
	{ "up", "int" },
	{ "down", "int" },
	{ "velocity_x", "int/int|decimal" },
	{ "velocity_y", "int/int|decimal" },
	{ "velocity_xy", "[int]" }, 

	{ "velocity_magnitude", "decimal" },
	{ "velocity_angle", "decimal" },

	{ "accel_x", "int/int|decimal" },
	{ "accel_y", "int/int|decimal" },
	{ "accel_xy", "[int]" },
	{ "gravity_shift", "int" },
	{ "platform_motion_x", "int" },

	{ "registry", "object" },
	{ "globals", "object" },
	{ "vars", "object" },
	{ "tmp", "object" },
	{ "group", "int" },

	{ "rotate", "decimal" },
	{ "rotate_x", "decimal" },
	{ "rotate_y", "decimal" },
	{ "rotate_z", "decimal" },

	{ "me", "any" },
	{ "self", "any" },

	{ "red", "int" },
	{ "green", "int" },
	{ "blue", "int" },
	{ "alpha", "int" },
	{ "text_alpha", "int" },
	{ "damage", "int" },
	{ "hit_by", "null|custom_obj" },

	{ "distortion", "null|object" },
	{ "is_standing", "bool" },
	{ "standing_info", "null|object" },
	
	{ "near_cliff_edge", "bool" },
	{ "distance_to_cliff", "int" },
	
	{ "slope_standing_on", "int" },
	{ "underwater", "bool" },
	
	{ "previous_water_bounds", "[int]" },
	{ "water_bounds", "null|[int]" },
	{ "water_object", "null|custom_obj" },
	
	{ "driver", "null|custom_obj" },
	{ "is_human", "bool" },
	{ "invincible", "int" },
	
	{ "sound_volume", "int" },
	{ "destroyed", "bool" },
	{ "is_standing_on_platform", "null|bool|custom_obj" },
	{ "standing_on", "null|custom_obj" },
	
	{ "shader", "null|shader_program" },
	{ "effects", "[shader_program]" },
	{ "variations", "[string]" },
	
	{ "attached_objects", "[custom_obj]" },
	{ "call_stack", "[string]" },
	{ "lights", "[object]" },
	
	{ "solid_dimensions_in", "[string]" },
	{ "solid_dimensions_not_in", "[string]" },
	
	{ "collide_dimensions_in", "[string]" },
	{ "collide_dimensions_not_in", "[string]" },
	
	{ "brightness", "int" },
	{ "current_generator", "object" },
	{ "tags", "object" },
	{ "draw_area", "any" },
	{ "scale", "decimal" },
	
	{ "activation_area", "null|[int|decimal]" },
	{ "clip_area", "null|[int]" },

	{ "always_active", "bool" },
	{ "activation_border", "int/int|decimal" },
	{ "fall_through_platforms", "any" },
	{ "has_feet", "bool" },
	
	{ "x_schedule", "any" },
	{ "y_schedule", "any" },
	{ "rotation_schedule", "any" },
	{ "schedule_speed", "any" },
	
	{ "schedule_expires", "any" },
	
	{ "platform_area", "null|[int]" },
	{ "platform_offsets", "[int]" },
	{ "custom_draw", "list" },
	
	{ "uv_array", "[decimal]" },
	{ "xy_array", "[decimal]" },
	{ "uv_segments", "[int]" },
	
	{ "draw_primitives", "[object]/[object|map]|map" },
	{ "event_handlers", "object" },
	
	{ "use_absolute_screen_coordinates", "bool" },
	
	{ "widgets", "object/[object|map]|object|map" },
	{ "widget_list", "[widget]" },
	{ "textv", "any" },
	{ "body", "any" },
	{ "paused", "bool" },
	{ "mouseover_delay", "int" },
	{ "mouseover_area", "[int]" },
	{ "particle_systems", "{string -> object}" },

	{ "truez", "bool" },
	{ "tx", "decimal" },
	{ "ty", "decimal" },
	{ "tz", "decimal" },

	{ "animated_movements", "[string]" },

	{ "ctrl_user_output", "any" },
	
	{ "ctrl_up", "bool" },
	{ "ctrl_down", "bool" },
	{ "ctrl_left", "bool" },
	{ "ctrl_right", "bool" },
	
	{ "ctrl_attack", "bool" },
	{ "ctrl_jump", "bool" },
	{ "ctrl_tongue", "bool" },
	{ "ctrl_user", "any" },

	//player-specific
	{ "difficulty", "int" },
	{ "can_interact", "bool" },
	{ "underwater_controls", "bool" },
	{ "ctrl_mod_key", "int" },
	{ "ctrl_keys", "[string]" },
	{ "ctrl_mice", "[[int|string]]" },
	{ "ctrl_tilt", "int" },
	{ "ctrl_x", "int" },
	{ "ctrl_y", "int" },
	{ "ctrl_reverse_ab", "bool" },
	{ "control_scheme", "string" },
	{ "vertical_look", "int" },
	{ "control_lock", "null|[string]" },
};
	ASSERT_EQ(NUM_CUSTOM_OBJECT_PROPERTIES, sizeof(CustomObjectProperties)/sizeof(*CustomObjectProperties));

	if(global_entries().empty()) {
		for(int n = 0; n != sizeof(CustomObjectProperties)/sizeof(*CustomObjectProperties); ++n) {
			global_entries().push_back(entry(CustomObjectProperties[n].id));

			const std::string& type = CustomObjectProperties[n].type;
			std::string::const_iterator itor = std::find(type.begin(), type.end(), '/');
			std::string read_type(type.begin(), itor);
			global_entries().back().set_variant_type(parse_variant_type(variant(read_type)));

			if(itor != type.end()) {
				global_entries().back().write_type = parse_variant_type(variant(std::string(itor+1, type.end())));
			}
		}

		for(int n = 0; n != global_entries().size(); ++n) {
			keys_to_slots()[global_entries()[n].id] = n;
		}

		global_entries()[CUSTOM_OBJECT_ME].set_variant_type(variant_type::get_custom_object());
		global_entries()[CUSTOM_OBJECT_SELF].set_variant_type(variant_type::get_custom_object());

		const variant_type_ptr builtin = variant_type::get_builtin("level");
		global_entries()[CUSTOM_OBJECT_LEVEL].set_variant_type(builtin);
	}

	global_entries()[CUSTOM_OBJECT_PARENT].type_definition = is_singleton ? this : &instance();
	global_entries()[CUSTOM_OBJECT_LIB].type_definition = game_logic::get_library_definition().get();

	entries_ = global_entries();
}