#include common_scripts\utility;

main( painter_spmp )
{

	painter_setup_array = getentarray( "painter_setup", "targetname" );

	if ( !painter_setup_array.size )
		return;

	if ( !getdvarint( "painter" ) )
	{
		array_thread( painter_setup_array, ::painter_clean_me );
		return;
	}

	painter_initvars( painter_spmp );

	painter_groups = [];

	groups = get_painter_groups( painter_setup_array );

	foreach ( group in groups )
		setup_painter_group( group );

	thread painter_init();

	array_thread( level._spam_model_group, ::default_undefined );
	level._stop_load = true;
	level waittill( "forever" );
}

painter_clean_me()
{
	if( isdefined( self.target ) )
	{
		ent = getent(self.target,"targetname");
		ent delete();
	}
	self delete();
}

default_undefined()
{
	if ( !isdefined( self.bPosedstyle ) )
		self.bPosedstyle = false;
	if ( !isdefined( self.bOrienttoplayeryrot ) )
		self.bOrienttoplayeryrot = false;
	if ( !isdefined( self.bTreeOrient ) )
		self.bTreeOrient = false;
	if ( !isdefined( self.bFacade ) )
		self.bFacade = false;
	if ( !isdefined( self.density ) )
		self.density = 32;
	if ( !isdefined( self.radius ) )
		self.radius = 84;
	if ( !isdefined( self.maxdist ) )
		self.maxdist = 1000;
	if ( !isdefined( self.angleoffset ) )
		self.angleoffset = [];

}

setup_painter_group( group )
{
	density = 100000001;
	group_copy = group;
	// figure out default radius and density for the group
	bTreeOrient = undefined;
	bFacade = undefined;
	radius = undefined;
	maxdist = undefined;
	offsetheight = undefined;
	bPosedstyle = undefined;
	bOrienttoplayeryrot = undefined;
	angleoffset = undefined;


	foreach ( obj in group )
	{
		angleoffset = get_angle_offset( obj );
		offsetheight = get_height_offset( obj );
		modeluseprefab = ( isdefined( obj.script_parameters ) && obj.script_parameters == "use_prefab_model" );
			
			
		if ( isdefined( obj.radius ) )
			radius = obj.radius;
		if ( isdefined( obj.script_painter_treeorient ) && obj.script_painter_treeorient )
			bTreeOrient = true;
		if ( isdefined( obj.script_painter_maxdist ) && obj.script_painter_maxdist )
			maxdist = obj.script_painter_maxdist;
		if ( isdefined( obj.script_painter_facade ) && obj.script_painter_facade )
			bFacade = true;
		foreach ( other_obj in group_copy )
		{
			if ( obj == other_obj )
				continue;
			dist = distance( obj.origin, other_obj.origin );
			assert( dist > 0 );
			if ( dist < density )
				density = dist;
		}
		if ( density == 100000001 )
			density = undefined;
		add_spammodel( obj.script_paintergroup, obj.model, bTreeOrient, bFacade, density, radius, maxdist, offsetheight, bPosedstyle, bOrienttoplayeryrot, angleoffset, modeluseprefab );
	}
}

get_angle_offset( obj )
{
	if( !isdefined( obj.target ) ) 
		return undefined;
	
	targent = getent( obj.target, "targetname" );
	assert( isdefined ( targent ) );
	return targent.angles - obj.angles;
}

get_height_offset( obj )
{
	if( !isdefined( obj.target ) ) 
		return undefined;
	
	targent = getent( obj.target, "targetname" );
	assert( isdefined ( targent ) );
	origin = targent.origin[2] - obj.origin[2];
	targent delete();
	return origin;
}




get_painter_groups( painter_setup_array )
{
	groups = [];
	script_paintergroup = "";
	foreach ( paint_obj in painter_setup_array )
	{
		if ( !isdefined( paint_obj.script_paintergroup ) )
		{
			paint_obj.script_paintergroup = paint_obj.model;
		}
		script_paintergroup = paint_obj.script_paintergroup;

		level._painter_startgroup = script_paintergroup;

		if ( !isdefined( groups[ script_paintergroup ] ) || ! groups[ script_paintergroup ].size )
			groups[ script_paintergroup ] = [];
		groups[ script_paintergroup ][ groups[ script_paintergroup ].size ] = paint_obj;
	}
	return groups;
}

painter_initvars( painter_spmp )
{
	level._clearalltextafterhudelem = false;
	level._bPosedstyle = false;
	level._bOrienttoplayeryrot = false;
	level._spam_density_scale = 16;
	level._spaming_models = false;
	level._spam_model_group = [];
	level._spamed_models = [];
	level._spam_models_flowrate = .1;
	level._spam_model_radius = 31;
	level._spam_maxdist = 1000;
	level._previewmodels = [];
	level._spam_models_isCustomrotation = false;
	level._spam_models_isCustomheight = false;
	level._spam_models_customheight = 0;
	level._spam_model_circlescale_lasttime = 0;
	level._spam_model_circlescale_accumtime = 0;
	level._paintadd = ::add_spammodel;
//	level.geteyeoffset = (0,0,24);
	level._timeLimitOverride = true;
	thread hack_start( painter_spmp );
	thread hud_init();
}

hack_start( painter_spmp )
{
	if ( !isdefined( painter_spmp ) )
		painter_spmp = "painter";

	precachemenu( painter_spmp );

	//who knows what the mp scripts are doing I took a dive deep into them and discovered many hud elements being controled through code and not through a menu that can be easily disabled
	// here I simply automate some things to get the user up and running.
	// get the player going.  I don't handle people dieing in this tool since they are in ufo mode anyway.

	flag_init( "user_alive" );
	while ( !isdefined( get_player() ) )
		wait .05;
	level._painter_player = get_player();
	wait .05;
	menu = "team_marinesopfor";
	
	if( level._multiTeamBased )
	{
		menu = "team_mtdm_options";
	}
	
	response = "autoassign";
	level._painter_player notify( "menuresponse", menu, response );
	wait .05;
	menu = "changeclass_offline";
	response = "offline_class1_mp, 0";
	level._painter_player notify( "menuresponse", menu, response );
	level._painter_player openpopupmenu( painter_spmp );// painter.menu execs some console commands( ufo mode ).. sneaky hacks.
	wait .05;
	level._painter_player closepopupmenu();
	flag_set( "user_alive" );
}

painter_init()
{
	array_call( getentarray( "script_model", "classname" ), ::delete );
	setcurrentgroup( level._painter_startgroup );
	level._painter_startgroup = undefined;
	playerInit();
}

hud_update_placed_model_count()
{
	level._hud_controler[ "helppm" ].description setvalue( level._spamed_models.size );
	whitecap = 256;
	if ( level._spamed_models.size < whitecap )
	{
		level._hud_controler[ "helppm" ].description.color = ( 1, 1, 1 );
		return;
	}

	r = 1;
	g = 1 - ( ( level._spamed_models.size - whitecap ) / whitecap );
	b = g;

	level._hud_controler[ "helppm" ].description.color = ( r, g, b );

}

hud_init()
{
	flag_init( "user_hud_active" );
	flag_wait( "user_alive" );

	//shorter list for mp cause it's got too many g_configstring somesuch. There is probably better check than substr on the mapname. I don't think this will bite me though, knock on wood.
	listsize = 7;
	if ( is_mp() )
		listsize = 7;

	hudelems = [];
	spacer = 15;
	div = int( listsize / 2 );
	org = 240 + div * spacer;
	alphainc = .5 / div;
	alpha = alphainc;

	for ( i = 0;i < listsize;i++ )
	{
		hudelems[ i ] = _newhudelem();
		hudelems[ i ].location = 0;
		hudelems[ i ].alignX = "left";
		hudelems[ i ].alignY = "middle";
		hudelems[ i ].foreground = 1;
		hudelems[ i ].fontScale = 2;
		hudelems[ i ].sort = 20;
		if ( i == div )
			hudelems[ i ].alpha = 1;
		else
			hudelems[ i ].alpha = alpha;

		hudelems[ i ].x = 20;
		hudelems[ i ].y = org;
		hudelems[ i ] _settext( "." );

		if ( i == div )
			alphainc *= -1;

		alpha += alphainc;

		org -= spacer;
	}

	level._spam_group_hudelems = hudelems;

	crossHair = _newhudelem();
	crossHair.location = 0;
	crossHair.alignX = "center";
	crossHair.alignY = "bottom";
	crossHair.foreground = 1;
	crossHair.fontScale = 2;
	crossHair.sort = 20;
	crossHair.alpha = 1;
	crossHair.x = 320;
	crossHair.y = 244;
	crossHair _settext( "." );
	level._crosshair = crossHair;

			 // setup "crosshair"
	crossHair = _newhudelem();
	crossHair.location = 0;
	crossHair.alignX = "center";
	crossHair.alignY = "bottom";
	crossHair.foreground = 1;
	crossHair.fontScale = 2;
	crossHair.sort = 20;
	crossHair.alpha = 0;
	crossHair.x = 320;
	crossHair.y = 244;
	crossHair setvalue( 0 );
	level._crosshair_value = crossHair;

	controler_hud_add( "helppm", 1, "^5Placed Models: ", undefined, level._spamed_models.size );
	controler_hud_add( "helpdensity", 2, "^5Spacing: ", undefined, level._spam_density_scale );
	controler_hud_add( "helpradius", 3, "^5Radius: ", undefined, level._spam_model_radius );
	controler_hud_add( "helpxy", 6, "^4X / ^3Y: ", undefined, level._spam_model_radius );
	controler_hud_add( "helpab", 7, "^2A / ^1B^7: ", " - " );
	controler_hud_add( "helplsrs", 8, "^8L^7 / R Stick: ", " - " );
	controler_hud_add( "helplbrb", 9, "^8L^7 / R Shoulder: ", " - " );
	controler_hud_add( "helpdpu", 10, "^8DPad U / ^7D: ", " - " );
	controler_hud_add( "helpdpl", 11, "^8DPad L / ^7R: ", " - " );
	controler_hud_add( "helpF", 17, "^8F: ^7( dump ) ^3map_source/" + level._script + "_modeldump.map", "" );

	hint_buttons_main();

	flag_set( "user_hud_active" );
}

hint_buttons_main()
{
	controler_hud_update_text( "helpxy", "^4Select Set Up ^7 / ^3Down" );
	controler_hud_update_text( "helpab", "^2Spacing Down ^7 / ^1up " );
	controler_hud_update_text( "helplsrs", "^8Radius Down ^7 / Up" );
	controler_hud_update_text( "helplbrb", "^8Remove ^7 / Place" );
	controler_hud_update_text( "helpdpl", "^8zOffset Clear ^7 / Set" );
	controler_hud_update_text( "helpdpu", "^8Rotation Clear ^7 / Set" );
//	controler_hud_update_text( "helpF", text );

}

hint_buttons_zoffset()
{
	controler_hud_update_text( "helpxy", "^4 - ^7 / ^3 - " );
	controler_hud_update_text( "helpab", "^2Height Down ^7 / ^1Up " );
	controler_hud_update_text( "helplsrs", "^8 - ^7 / - " );
	controler_hud_update_text( "helplbrb", "^8 - ^7 / - " );
	controler_hud_update_text( "helpdpl", "^8 - ^7 / Set" );
	controler_hud_update_text( "helpdpu", "^8 - ^7 / - " );
	controler_hud_update_text( "helpF", " - " );
}

hint_buttons_rotation()
{
	controler_hud_update_text( "helpxy", "^4 - ^7 / ^3 - " );
	controler_hud_update_text( "helpab", "^2RotateOther Up ^7 / ^1Down " );
	controler_hud_update_text( "helplsrs", "^8 - ^7 / - " );
	controler_hud_update_text( "helplbrb", "^8 - ^7 / - " );
	controler_hud_update_text( "helpdpl", "^8 - ^7 / - " );
	controler_hud_update_text( "helpdpu", "^8Set ^7 / - " );
	controler_hud_update_text( "helpF", " - " );
}

setcurrentgroup( group )
{
	flag_wait( "user_hud_active" );
	level._spam_model_current_group = group;
	keys = getarraykeys( level._spam_model_group );
	index = 0;
	div = int( level._spam_group_hudelems.size / 2 );
	for ( i = 0;i < keys.size;i++ )
		if ( keys[ i ] == group )
		{
			index = i;
			break;
		}

	level._spam_group_hudelems[ div ] _settext( keys[ index ] );

	for ( i = 1;i < level._spam_group_hudelems.size - div;i++ )
	{
			if ( index - i < 0 )
			{
				level._spam_group_hudelems[ div + i ] _settext( "." );
				continue;
			}
			level._spam_group_hudelems[ div + i ] _settext( keys[ index - i ] );
	}

	for ( i = 1;i < level._spam_group_hudelems.size - div;i++ )
	{
			if ( index + i > keys.size - 1 )
			{
				//  -- -- 
				level._spam_group_hudelems[ div - i ] _settext( "." );
				continue;
			}
			level._spam_group_hudelems[ div - i ] _settext( keys[ index + i ] );
	}

	group = getcurrent_groupstruct();

	level._bOrienttoplayeryrot = group.bOrienttoplayeryrot;
	level._bPosedstyle = group.bPosedstyle;
	level._spam_maxdist = group.maxdist;
	level._spam_model_radius = group.radius;
	level._hud_controler[ "helpradius" ].description setvalue( level._spam_model_radius );

	level._spam_density_scale = group.density;
	level._hud_controler[ "helpdensity" ].description setvalue( level._spam_density_scale );
}


setgroup_up()
{
	index = undefined;
	keys = getarraykeys( level._spam_model_group );
	for ( i = 0;i < keys.size;i++ )
		if ( keys[ i ] == level._spam_model_current_group )
		{
			index = i + 1;
			break;
		}
	if ( index == keys.size )
		return;
	setcurrentgroup( keys[ index ] );
	while ( level._painter_player buttonpressed( "BUTTON_Y" ) )
		wait .05;
}

setgroup_down()
{
	index = undefined;
	keys = getarraykeys( level._spam_model_group );
	for ( i = 0;i < keys.size;i++ )
		if ( keys[ i ] == level._spam_model_current_group )
		{
			index = i - 1;
			break;
		}
	if ( index < 0 )
		return;
	setcurrentgroup( keys[ index ] );
	while ( level._painter_player buttonpressed( "BUTTON_X" ) )
		wait .05;
}

Add_Spammodel( group, model, bTreeOrient, bFacade, density, radius, maxdist, offsetheight, bPosedstyle, bOrienttoplayeryrot, angleoffset, modelusesprefab )
{
	if ( !isdefined( level._spam_model_group[ group ] ) )
	{
		struct = spawnstruct();
		level._spam_model_group[ group ] = struct;
		level._spam_model_group[ group ].models = [];
	}
	
	if( !isdefined( angleoffset ) )
		angleoffset = (0,0,0);
		
	level._spam_model_group[ group ].bFacade =  bFacade;
	level._spam_model_group[ group ].bTreeOrient =  bTreeOrient;
	level._spam_model_group[ group ].density =  density;
	level._spam_model_group[ group ].radius =  radius;
	level._spam_model_group[ group ].maxdist =  maxdist;
	level._spam_model_group[ group ].bPosedstyle = bPosedstyle;
	level._spam_model_group[ group ].bOrienttoplayeryrot = bOrienttoplayeryrot;
	
	if( !isdefined( level._spam_model_group[ group ].angleoffset ) )
		level._spam_model_group[ group ].angleoffset = [];
	level._spam_model_group[ group ].angleoffset[ model ] = angleoffset;

	if( !isdefined( level._spam_model_group[ group ].heightoffset ) )
		level._spam_model_group[ group ].heightoffset = [];
	level._spam_model_group[ group ].heightoffset[ model ] = offsetheight;
		
	if( !isdefined( level._spam_model_group[ group ].modelusesprefab ) )
		level._spam_model_group[ group ].modelusesprefab = [];
	level._spam_model_group[ group ].modelusesprefab[ model ] = modelusesprefab;
		
	level._spam_model_group[ group ].models[ level._spam_model_group[ group ].models.size ] = model;
}


playerInit()
{
	level._painter_max = 700;
 	level._painter_player takeAllWeapons();

	flag_wait( "user_hud_active" );
	while ( 1 )
	{
		trace = player_view_trace();
		draw_placement_circle( trace );
		if ( level._painter_player buttonpressed( "f" ) )
			dump_models();
		if ( level._painter_player buttonpressed( "DPAD_UP" ) )
			customrotation_mode( trace, "DPAD_UP" );
		else if ( level._painter_player buttonpressed( "DPAD_DOWN" ) )
			customrotation_mode_off();
		else if ( level._painter_player buttonpressed( "DPAD_RIGHT" ) )
			customheight_mode( trace, "DPAD_RIGHT" );
		else if ( level._painter_player buttonpressed( "DPAD_LEFT" ) )
			customheight_mode_off();
		else if ( level._painter_player buttonpressed( "BUTTON_X" ) )
			setgroup_down();
		else if ( level._painter_player buttonpressed( "BUTTON_Y" ) )
			setgroup_up();
		else if ( level._painter_player buttonpressed( "BUTTON_LSTICK" ) )
			spam_model_circlescale( trace, -1 );
		else if ( level._painter_player buttonpressed( "BUTTON_RSTICK" ) )
			spam_model_circlescale( trace, 1 );
		else if ( level._painter_player buttonpressed( "BUTTON_A" ) )
			spam_model_densityscale( trace, -1 );
		else if ( level._painter_player buttonpressed( "BUTTON_B" ) )
			spam_model_densityscale( trace, 1 );
		else
		{
			if ( level._painter_player buttonpressed( "BUTTON_LSHLDR" ) )
				spam_model_erase( trace );
			if ( level._painter_player buttonpressed( "BUTTON_RSHLDR" ) )
				thread spam_model_place( trace );// threaded for delay
		}
		level notify( "clear_previews" );
		wait .05;
		hud_update_placed_model_count();
	}
}

customheight_mode_off()
{
	level._spam_models_isCustomheight = false;
	hint_buttons_main();
}

customheight_mode( trace, button )
{
	if ( trace[ "fraction" ] == 1 )
		return;

	while ( level._painter_player buttonpressed( button ) )
		wait .05;

	level._spam_models_isCustomheight = true;
	hint_buttons_zoffset();
	models = [];
	models = spam_models_atcircle( trace, false, true );

	inc = 2;
	dir = 1;

	origin = trace[ "position" ];
	while ( !level._painter_player buttonpressed( button ) )
	{
		height = level._spam_models_customheight;
		if ( level._painter_player buttonpressed( "BUTTON_A" ) )
			dir = -1;
		else if ( level._painter_player buttonpressed( "BUTTON_B" ) )
			dir = 1;
		else
			dir = 0;
		height += dir * inc;
		if ( height == 0 )
			height += dir * inc;
		level._spam_models_customheight = height;

		array_thread( models, ::customheight_mode_offsetmodels, trace );
		draw_placement_circle( trace, ( 1, 1, 1 ) );

		wait .05;
	}
	array_thread( models, ::deleteme );
	hint_buttons_main();
	while ( level._painter_player buttonpressed( button ) )
		wait .05;
}

customheight_mode_offsetmodels( trace )
{
	self.origin = self.orgorg + ( trace[ "normal" ] * level._spam_models_customheight );
}

customrotation_mode_off()
{
	level._spam_models_isCustomrotation = false;
	hint_buttons_main();
}

customrotation_mode( trace, button )
{
	if ( trace[ "fraction" ] == 1 )
		return;

	while ( level._painter_player buttonpressed( button ) )
		wait .05;

	hint_buttons_rotation();

	level._spam_models_isCustomrotation = true;
	level._spam_models_customrotation = level._painter_player getplayerangles();
	models = [];
	models = spam_models_atcircle( trace, true, true );

	otherangle = 0;
	otherangleinc = 1;
	dir = 0;

	while ( !level._painter_player buttonpressed( button ) )
	{
		dir = 0;
		if ( level._painter_player buttonpressed( "BUTTON_A" ) )
			dir = -1;
		else if ( level._painter_player buttonpressed( "BUTTON_B" ) )
			dir = 1;
		otherangle += dir * otherangleinc;
		if ( otherangle > 360 )
			otherangle = 1;
		if ( otherangle < 0 )
			otherangle = 359;
		draw_placement_circle( trace, ( 0, 0, 1 ) );
		level._spam_models_customrotation = level._painter_player getplayerangles();
		level._spam_models_customrotation += ( 0, 0, otherangle );
		for ( i = 0;i < models.size;i++ )
			models[ i ].angles = level._spam_models_customrotation;
		wait .05;
	}

	hint_buttons_main();

	while ( level._painter_player buttonpressed( button ) )
		wait .05;

	for ( i = 0;i < models.size;i++ )
		models[ i ] thread deleteme();

}

deleteme()
{
	self delete();
}

spam_model_clearcondition()
{
	self endon( "death" );
	level waittill( "clear_previews" );
	level._previewmodels = array_remove( level._previewmodels, self );
	self delete();
}

crosshair_fadetopoint()
{
	level notify( "crosshair_fadetopoint" );
	level endon( "crosshair_fadetopoint" );
	wait 2;
	level._crosshair_value.alpha = 0;
	level._crosshair.alpha = 1;
}

spam_model_circlescale( trace, dir )
{
	if ( gettime() - level._spam_model_circlescale_lasttime > 60 )
		level._spam_model_circlescale_accumtime = 0;

	level._spam_model_circlescale_accumtime += .05;

	if ( level._spam_model_circlescale_accumtime < .5 )
		inc = 2;
	else
		inc = level._spam_model_circlescale_accumtime / .3;

	radius = level._spam_model_radius;
	radius += dir * inc;
	if ( radius > 0 )
		level._spam_model_radius = radius;

	level._hud_controler[ "helpradius" ].description setvalue( level._spam_model_radius );

	level._spam_model_circlescale_lasttime = gettime();
}

spam_model_densityscale( trace, dir )
{
		 // ghetto hack here.  density scale used for distance on floating model types
		inc = 2;
		scale = level._spam_density_scale;
		scale += dir * inc;
		if ( scale > 0 )
			level._spam_density_scale = scale;


		level._crosshair_value.alpha = 1;
		level._crosshair.alpha = 0;

		level._crosshair_value setvalue( level._spam_density_scale );
		level._hud_controler[ "helpdensity" ].description setvalue( level._spam_density_scale );

		thread crosshair_fadetopoint();
}

draw_placement_circle( trace, coloroverride )
{
	if ( !isdefined( coloroverride ) )
		coloroverride = ( 0, 1, 0 );
	if ( trace[ "fraction" ] == 1 )
		return;
 // 	angles = vectortoangles( anglestoup( vectortoangles( trace[ "normal" ] ) ) );
	angles = vectortoangles( trace[ "normal" ] );
	origin = trace[ "position" ];
	radius = level._spam_model_radius;
 // 	plot_circle( origin, radius, angles, color, circleres );
	plot_circle( origin, radius, angles, coloroverride, 40, level._spam_model_radius );

	if ( level._spam_models_isCustomrotation )
		draw_axis( origin, level._spam_models_customrotation );
	if ( level._spam_models_isCustomheight )
		draw_arrow( origin, origin + ( trace[ "normal" ] * level._spam_models_customheight ), ( 1, 1, 1 ) );
}

player_view_trace()
{
	maxdist = level._spam_maxdist;
	traceorg = level._painter_player geteye();
	return bullettrace( traceorg, traceorg + ( anglestoforward( level._painter_player getplayerangles() ) * maxdist ), 0, self );
}

Orienttoplayeryrot()
{
	self addyaw( level._painter_player getplayerangles()[ 1 ] - flat_angle( self.angles )[ 1 ] );
 // 	self.angles = ( x, y, z );
}

getcurrent_groupstruct()
{
	return level._spam_model_group[ level._spam_model_current_group ];
}

orient_model()
{
	group = getcurrent_groupstruct();

	if ( level._spam_models_isCustomrotation )
	{
		self.angles = level._spam_models_customrotation;
		return;
	}

	if ( level._bPosedstyle )
		self.angles = level._painter_player getplayerangles();

	if ( level._bOrienttoplayeryrot )
		self Orienttoplayeryrot();

	if ( group.bTreeOrient )
		self.angles = flat_angle( self.angles );

	if ( ! level._bOrienttoplayeryrot && !level._bPosedstyle )
		self addyaw( randomint( 360 ) );

	if ( group.bFacade )
	{
		self.angles = flat_angle( vectortoangles( self.origin - level._painter_player geteye() ) );
		self addyaw( 90 );

	}

	 assert( isdefined( group.angleoffset ) && isdefined( group.angleoffset[ self.model] ) );

	 self addroll( group.angleoffset[self.model][ 0 ] );
	 self addpitch( group.angleoffset[self.model][ 1 ] );
	 self addyaw( group.angleoffset[self.model][ 2 ] );
	 
	 	
	 
}

spam_model_place( trace )
{
	if ( 	level._spaming_models )
		return;
	if ( trace[ "fraction" ] == 1  && !level._bPosedstyle )
		return;
	level._spaming_models = true;
	models = spam_models_atcircle( trace, true );
	level._spamed_models = array_combine( level._spamed_models, models );
	level._spaming_models = false;
}

getrandom_spammodel()
{
	models = level._spam_model_group[ level._spam_model_current_group ].models;
	return models[ randomint( models.size ) ];
}

spam_models_atcircle( trace, bRandomrotation, bForcedSpam )
{
	if ( !isdefined( bForcedSpam ) )
		bForcedSpam = false;
	models = [];
	incdistance = level._spam_density_scale;
	radius = level._spam_model_radius;
	incs = int( radius / incdistance ) * 2;
	startpoint = 0;
	traceorg = trace[ "position" ];
	angles = vectortoangles( trace[ "normal" ] );
	if ( bRandomrotation )
		angles += ( 0, randomfloat( 360 ), 0 );
	xvect = vectornormalize( anglestoright( angles ) );
	yvect = vectornormalize( anglestoup( angles ) );
	startpos = traceorg;
	startpos -= ( xvect * radius );
	startpos -= ( yvect * radius );
	startpos += ( xvect * incdistance );
	startpos += ( yvect * incdistance );

	modelpos = startpos;
	 // special for when circle is too small for current density to place anything.  Just place one in the center..
	if ( incs == 0 || level._bPosedstyle )
	{
		if ( !bForcedSpam )
		if ( 	is_too_dense( traceorg ) )
			return models;
		if ( !bForcedSpam )
		if ( level._spamed_models.size + models.size > level._painter_max )
			return models;

		getmodel = getrandom_spammodel();
		models[ 0 ] = spam_modelattrace( trace, getmodel );
		models[ 0 ] orient_model();

		return models;
	}

	countourtrace = [];
	for ( x = startpoint;x < incs;x++ )
	for ( y = startpoint;y < incs;y++ )
	{
		if ( !bForcedSpam )
		if ( level._spamed_models.size + models.size > level._painter_max )
			return models;;
		modelpos = startpos;
		modelpos += ( xvect * x * incdistance );
		modelpos += ( yvect * y * incdistance );
		if ( distance( modelpos, traceorg ) > radius )
			continue;
//		if ( !bForcedSpam )
			countourtrace = contour_point( modelpos, angles, level._spam_model_radius );

		if ( countourtrace[ "fraction" ] == 1 )
			continue;
		if ( is_too_dense( countourtrace[ "position" ] ) )
			continue;
		getmodel = getrandom_spammodel();

		model = spam_modelattrace( countourtrace, getmodel );
		model orient_model();
		models[ models.size ] = model;

	}
	return models;
}

is_too_dense( testorg )
{
	 // going backwards will be faster
	for ( i = level._spamed_models.size - 1; i >= 0; i -- )
		if ( distance( level._spamed_models[ i ].orgorg, testorg ) < ( level._spam_density_scale - 1 ) )
			return true;
	return false;
}

get_player()
{
	return getentarray( "player", "classname" )[ 0 ];
}

spam_modelattrace( trace, getmodel )
{
	model = spawn( "script_model", level._painter_player.origin );
	model setmodel( getmodel );
	model notsolid();
	model.origin = trace[ "position" ];
	model.angles = vectortoangles( trace[ "normal" ] );
	model addpitch( 90 );
	model.orgorg = model.origin;
	group = getcurrent_groupstruct();
	if ( level._spam_models_isCustomheight )
		model.origin += ( trace[ "normal" ] * level._spam_models_Customheight );

	group = getcurrent_groupstruct();
	if( isdefined( group.heightoffset[ getmodel ] ) )
			model.origin += ( trace[ "normal" ] * group.heightoffset[ getmodel ]  );
	if( isdefined( group.modelusesprefab[ getmodel ] ) )
			model.modelusesprefab = group.modelusesprefab[ getmodel ];
		
	return model;
}

contour_point( origin, angles, height )
{
	offset = height;
	vect = anglestoforward( angles );
	destorg = origin + ( vect * offset );
	targetorg = origin + ( vect * - 1 * offset );
	return bullettrace( destorg, targetorg, 0, level._painter_player );
}

plot_circle( origin, radius, angles, color, circleres, contourdepth )
{
	if ( !isdefined( color ) )
		color = ( 0, 1, 0 );
	if ( !isdefined( circleres ) )
		circleres = 16;
	hemires = circleres / 2;
	circleinc = 360 / circleres;
	circleres++ ;
	plotpoints = [];
	rad = 0;
	plotpoints = [];
	rad = 0.000;
	for ( i = 0;i < circleres;i++ )
	{
		baseorg =  origin + ( anglestoup( ( angles + ( 0, 0, rad ) ) ) * radius );
		point = contour_point( baseorg, angles, level._spam_model_radius );
		if ( point[ "fraction" ] != 1 )
			plotpoints[ plotpoints.size ] =  point[ "position" ];
		rad += circleinc;
	}
	plot_points( plotpoints, color[ 0 ], color[ 1 ], color[ 2 ] );
	plotpoints = [];
}

spam_model_erase( trace )
{
	traceorg = trace[ "position" ];
	keepmodels = [];
	deletemodels = [];
	for ( i = 0;i < level._spamed_models.size;i++ )
	{
		if ( distance( level._spamed_models[ i ].orgorg, traceorg ) > level._spam_model_radius )
			keepmodels[ keepmodels.size ] = level._spamed_models[ i ];
		else
			deletemodels[ deletemodels.size ] = level._spamed_models[ i ];
	}
	level._spamed_models = keepmodels;

	for ( i = 0;i < deletemodels.size;i++ )
		deletemodels[ i ] delete();
}

dump_models()
{
 /#
	if ( ! level._spamed_models.size )
		return;
	fileprint_launcher_start_file();
	fileprint_map_start();
	for ( i = 0;i < level._spamed_models.size;i++ )
	{
		origin = fileprint_radiant_vec( level._spamed_models[ i ].origin );// convert these vectors to mapfile keypair format
		angles = fileprint_radiant_vec( level._spamed_models[ i ].angles );

		fileprint_map_entity_start();
			if( isdefined ( level._spamed_models[ i ].modelusesprefab ) && level._spamed_models[ i ].modelusesprefab )
			{
				fileprint_map_keypairprint( "classname", "misc_prefab" );
				fileprint_map_keypairprint( "model", "prefabs/misc_models/" + level._spamed_models[ i ].model + ".map" );
			}
			else
			{
				fileprint_map_keypairprint( "classname", "misc_model" );
				fileprint_map_keypairprint( "model", level._spamed_models[ i ].model );
			}
			fileprint_map_keypairprint( "origin", origin );
			fileprint_map_keypairprint( "angles", angles );
			fileprint_map_keypairprint( "spammed_model", level._spam_model_current_group );
		fileprint_map_entity_end();
	}
	map_path = level._script+"_modeldump.map";
	if( !fileprint_launcher_end_file( "/map_source/"+map_path,false ) )
		return;
	launcher_write_clipboard( map_path );
	array_thread( level._spamed_models, ::deleteme );
	level._spamed_models = [];
#/
}

draw_axis( org, angles )
{
	range = 32;
	forward = range * anglestoforward( angles );
	right = range * anglestoright( angles );
	up = range * anglestoup( angles );
	line( org, org + forward, ( 1, 0, 0 ), 1 );
	line( org, org + up, ( 0, 1, 0 ), 1 );
	line( org, org + right, ( 0, 0, 1 ), 1 );
}

_newhudelem()
{
	if ( !isdefined( level._scripted_elems ) )
	 	level._scripted_elems = [];
	elem = newhudelem();
	level._scripted_elems[ level._scripted_elems.size ] = elem;
	return elem;
}

_settext( text )
{
	self.realtext = text;
	self settext( "_" );
	self thread _clearalltextafterhudelem();
	sizeofelems = 0;
	foreach ( elem in level._scripted_elems )
	{
		if ( isdefined( elem.realtext ) )
		{
			sizeofelems += elem.realtext.size;
			elem settext( elem.realtext );
		}
	}
	println( "SIze of elems: " + sizeofelems );
}

controler_hud_add( identifier, inc, initial_text, initial_description_text, initial_value )
{
	startx = 520;
	if ( is_mp() )
		startx = 630;
	starty = 120;
	space = 18;
	basealpha = .8;
	denradoffset = 20;
	descriptionscale = 1.4;
	if ( !isdefined( initial_text ) )
		initial_text = "";

	if ( !isdefined( level._hud_controler ) || !isdefined( level._hud_controler[ identifier ] ) )
	{
		level._hud_controler[ identifier ] = _newhudelem();
		description = _newhudelem();
	}
	else
		description = level._hud_controler[ identifier ].description;

	level._hud_controler[ identifier ].location = 0;
	level._hud_controler[ identifier ].alignX = "right";
	level._hud_controler[ identifier ].alignY = "middle";
	level._hud_controler[ identifier ].foreground = 1;
	level._hud_controler[ identifier ].fontscale = 1.5;
	level._hud_controler[ identifier ].sort = 20;
	level._hud_controler[ identifier ].alpha = basealpha;
	level._hud_controler[ identifier ].x = startx + denradoffset;
	level._hud_controler[ identifier ].y = starty + ( inc * space );
	level._hud_controler[ identifier ] _settext( initial_text );
	level._hud_controler[ identifier ].base_button_text = initial_text;

	description.location = 0;
	description.alignX = "left";
	description.alignY = "middle";
	description.foreground = 1;
	description.fontscale = descriptionscale;
	description.sort = 20;
	description.alpha = basealpha;
	description.x = startx + denradoffset;
	description.y = starty + ( inc * space );
	if ( isdefined( initial_value ) )
		description setvalue( initial_value );
	if ( isdefined( initial_description_text ) )
		description _settext( initial_description_text );
	level._hud_controler[ identifier ].description = description;
}

controler_hud_update_text( hudid, text )
{
	if ( is_mp() )
	{
		level._hud_controler[ hudid ] _settext( level._hud_controler[ hudid ].base_button_text + text );
		level._hud_controler[ hudid ].description _settext( "" );
	}
	else
		level._hud_controler[ hudid ].description _settext( text );



}

controler_hud_update_button( hudid, text )
{
	level._hud_controler[ hudid ] _settext( text );
}


_clearalltextafterhudelem()
{
	if ( level._clearalltextafterhudelem )
		return;
	level._clearalltextafterhudelem = true;
	self clearalltextafterhudelem();
	wait .05;
	level._clearalltextafterhudelem = false;

}

is_mp()
{
	return issubstr( level._script, "mp_" );
}