#include maps\_utility;
#include maps\_vehicle;
#include common_scripts\utility;
#include maps\_anim;
#include maps\favela_code;

PLAYER_SPRINT_SCALE = 1.4;

main()
{
	// ammo crates are for SO only so remove them
	array_call( getentarray( "ammo_crate_part", "targetname" ), ::delete );
	array_call( getentarray( "ammo_crate_clip", "targetname" ), ::delete );
	array_call( getentarray( "ammo_cache", "targetname" ), ::delete );
	
	setDvarIfUninitialized( "favela_trailer", "0" );
	
	setsaveddvar( "r_lightGridEnableTweaks", 1 );
	setsaveddvar( "r_lightGridIntensity", 1.5 );
	setsaveddvar( "r_lightGridContrast", 0 );
	
	default_start( ::startStreet );
	add_start( "street", ::startStreet );
	add_start( "chase", ::startChase );
	add_start( "favela", ::startFavela );
	add_start( "torture", ::startTortureTrailer );
	add_start( "soccer", ::startSoccer );
	add_start( "hilltop", ::startHilltop );
	add_start( "trailer1", ::trailer_talkers_1 );
	add_start( "trailer2", ::trailer_talkers_2 );
	add_start( "trailer3", ::trailer_talkers_3 );
	add_start( "end", ::startEnd );
	
	maps\createart\favela_fog::main();
	maps\createart\favela_art::main();
	maps\createfx\favela_audio::main();
	maps\favela_precache::main();
	maps\favela_fx::main();
	maps\_drone_ai::init();
	animscripts\dog\dog_init::initDogAnimations();
	maps\_hiding_door_anims::main();
	maps\_load::set_player_viewhand_model( "viewhands_player_tf141_favela" );
	maps\_load::main();
	maps\favela_anim::main();
	maps\_compass::setupMiniMap( "compass_map_favela" );
	thread maps\favela_amb::main();
	
	level.ai_friendlyFireBlockDuration = getdvarfloat( "ai_friendlyFireBlockDuration" );
	
	level.advanceToEnemyInterval = 30000;	// how often AI will try to run directly to their enemy if the enemy is not visible
	level.advanceToEnemyGroupMax = 1;		// group size for AI running to their enemy
	level.lastWallaTime = getTime();
	
	level.physics_drop_models[ 0 ] = "trash_container_big2";
	level.physics_drop_models[ 1 ] = "com_milk_carton";
	level.physics_drop_models[ 2 ] = "com_bottle4";
	level.physics_drop_models[ 3 ] = "trash_cup_tall3";
	level.physics_drop_models[ 4 ] = "trash_plate1";
	level.physics_drop_models[ 5 ] = "trash_can1";
	level.physics_drop_models[ 6 ] = "trash_can2";
	level.physics_drop_models[ 7 ] = "trash_can3";
	level.physics_drop_models[ 8 ] = "trash_can4";
	foreach( model in level.physics_drop_models )
		precacheModel( model );
	
	precacheString( &"FAVELA_OBJ_CATCH_RUNNER" );
	precacheString( &"FAVELA_OBJ_REACH_TOP" );
	precacheString( &"FAVELA_OBJ_CAPTURE" );
	precacheString( &"FAVELA_KILLED_RUNNER" );
	precacheString( &"FAVELA_RUNNER_GOT_AWAY" );
	precacheString( &"FAVELA_ROJAS_KILLED" );
	
	precacheShader( "black" );
	
	precacheItem( "rpg_straight" );
	precacheItem( "flash_grenade" );
	
	precacheModel( "com_bike_animated" );
	precacheModel( "curtain_torn01_animated" );
	precacheModel( "machinery_car_battery" );
	precacheModel( "vehicle_hummer_hula_girl" );
	
	array_spawn_function_noteworthy( "delete_at_path_end", ::delete_ai_at_path_end );
	array_spawn_function_noteworthy( "delete_at_path_end_no_choke", ::delete_ai_at_path_end_no_choke );
	array_spawn_function_noteworthy( "seek_player", ::seek_player );
	array_spawn_function_noteworthy( "dog_seek_player", ::dog_seek_player );
	array_spawn_function_noteworthy( "delete_at_goal", ::delete_ai_at_goal );
	array_spawn_function_noteworthy( "dont_see_player_no_choke", ::dont_see_player );
	array_spawn_function_noteworthy( "ignore_and_delete_on_goal", ::ignore_and_delete_on_goal );
	array_spawn_function_noteworthy( "ignore_and_delete_on_goal_nosight", ::ignore_and_delete_on_goal, true );
	array_spawn_function_noteworthy( "window_smasher", ::window_smasher );
	array_spawn_function_noteworthy( "ignored_until_goal", ::ignored_until_goal );
	array_spawn_function_noteworthy( "desert_eagle_guy", ::desert_eagle_guy );
	array_spawn_function_noteworthy( "faust", ::faust_spawn_func );
	array_spawn_function_noteworthy( "civilian_driver", ::civilian_driver );
	
	// spawners with script_parameters get processed on spawn
	all_axis_spawners = getspawnerteamarray( "axis" );
	script_parameter_spawners = [];
	foreach( spawner in all_axis_spawners )
	{
		if ( !isdefined( spawner.script_parameters ) )
			continue;
		script_parameter_spawners[ script_parameter_spawners.size ] = spawner;
	}
	array_spawn_function( script_parameter_spawners, ::process_ai_script_parameters );
	array_spawn_function( getSpawnerTeamArray( "neutral" ), ::civilian_flee_walla );
	
	array_thread( getentarray( "play_sound", "targetname" ), ::play_sound_trigger );
	array_thread( getentarray( "trigger_chance", "targetname" ), ::trigger_spawn_chance );
	array_thread( getentarray( "physics_drop", "targetname" ), ::physics_drop );
	array_thread( getentarray( "trigger_cleanup", "targetname" ), ::trigger_cleanup );
	array_thread( getentarray( "potted_plant", "targetname" ), ::potted_plant );
	array_thread( getentarray( "play_fx_trig", "targetname" ), ::play_fx_trig );
	array_thread( getentarray( "retreat_trigger", "targetname" ), ::retreat_trigger );
	array_thread( getentarray( "curtain_pulldown", "script_noteworthy" ), ::curtain_pulldown );
	array_thread( getentarray( "curtain_pulldown_playerwait", "script_noteworthy" ), ::curtain_pulldown, true );
	array_thread( getvehiclenodearray( "car_screech_node", "script_noteworthy" ), ::car_screech_node );
	add_global_spawn_function( "axis", ::adjustAccuracy );
	
	//flag_init( "all_friendlies_at_runner" );
	flag_init( "favela_gate_dialog_done" );
	flag_init( "car_getting_shot" );
	flag_init( "driver_dead" );
	flag_init( "player_is_ducking" );
	flag_init( "favela_music" );
	flag_init( "faust_music" );
	flag_init( "opening_scene_started" );
	flag_init( "favela_enemies_spawned" );
	flag_init( "start_chase" );
	flag_init( "block_alley" );
	flag_init( "favela_move_friendlies" );
	flag_init( "favela_civilians_fleeing" );
	flag_init( "allow_meat_death" );
	flag_init( "allow_royce_death" );
	flag_init( "civilians_walla" );
	flag_init( "torture_sequence_done" );
	flag_init( "van_skid" );
	flag_init( "makarov_alley_wounded" );
	flag_init( "favela_civilians_alerted" );
	flag_init( "favela_civilians_spawned" );
	flag_init( "ending_sequence_ready" );
	flag_init( "ending_sequence_started" );
	flag_init( "ending_sequence_dialog" );
	flag_init( "start_final_dialog" );
	flag_init( "visionset_chase" );
	flag_init( "visionset_torture" );
	flag_init( "soap_exits_car" );
	
	trigger_off( "favela_opening_civilians_spawn", "targetname" );
	
	thread vision_chase();
	thread vision_torture();
	thread gag_fence_dog();
	thread gag_civilian_window_1();
	thread favela_opening_civilians();
	thread favela_music();
	thread upper_village_triggered_dialog();
	thread upper_village_music();
	thread random_favela_background_runners();
	thread faust_appearance_1();
	thread faust_appearance_2();
	thread faust_appearance_3();
	thread faust_appearance_4();
	thread street_scenes();
	thread chase();
	thread forklift_blocker();
	thread final_bend_dialog();
	thread final_staircase_dialog();
	thread ending_sequence();
	
	skill = getDifficulty();
	if ( skill != "hard" && skill != "fu" )
	{
		setsaveddvar( "ai_accuracy_attackerCountMax", 4 );
		setsaveddvar( "ai_accuracy_attackerCountDecrease", 0.6 );
	}
}

startStreet()
{
	thread battlechatter_off( "allies" );
	thread battlechatter_off( "axis" );
	
	setSavedDvar( "compass", 0 );
	setSavedDvar( "hud_showStance", 0 );
	
	// start random city traffic
	thread start_traffic_group( 3.5, "traffic_car_groupA_1", "traffic_car_groupA_2");
	thread start_traffic_group( 5.0, "traffic_car_groupB_1", "traffic_car_groupB_2");
	
	// spawn street civilians
	array_thread( getentarray( "street_civilian", "targetname" ), ::spawn_ai, true );
	delayThread( 8.0, ::bike_rider, "bike_path_1" );
	delayThread( 13.0, ::bike_rider, "bike_path_2" );
	
	thread opening_scene();
	thread intro_music();
}

intro_music()
{
	// start intro music for ride and chase, and ends the music if the runner is shot before the alley
	thread music_play( "favela_intro" );
	
	level waittill( "runner_shot" );
	
	music_stop( 2.0 );
}

startChase()
{
	thread battlechatter_off( "allies" );
	thread battlechatter_off( "axis" );
	
	movePlayerToStartPoint( "playerstart_chase" );
	setSavedDvar( "player_sprintUnlimited", "1" );
	setSavedDvar( "player_sprintSpeedScale", PLAYER_SPRINT_SCALE );
	flag_set( "start_chase" );
	flag_set( "visionset_chase" );
}

startTortureTrailer()
{
	setDvar( "favela_trailer", 1 );
	thread startFavela();
}

startFavela()
{
	thread battlechatter_off( "allies" );
	thread battlechatter_off( "axis" );
	
	thread fade_to_black_alley( 0.2, 2.0, 2.5 );
	
	level waittill( "black_screen_start" );
	
	// Give flashbangs and grenades now that the chase is over
	level.player giveWeapon( "fraggrenade" );
	level.player setOffhandSecondaryClass( "flash" );
	level.player giveWeapon( "flash_grenade" );
	
	level notify( "stop_monitoring_makarov_damage" );
	
	// delete any AI ( dead or alive ) that might be left over from before
	thread delete_ai_during_blackscreen();
	array_call( getCorpseArray(), ::delete );
	if ( isdefined( level.runner ) )
		level.runner delete();
	
	flag_set( "visionset_torture" );
	
	level delayThread( 1.2, ::torture_sequence );
	
	movePlayerToStartPoint( "playerstart_favela" );
	activate_trigger( "favela_vision", "script_noteworthy" );
	
	flag_set( "civilians_walla" );
	enablePlayerWeapons( false );
	
	// Reenable destructibles to do bad places, was disabled for the chase
	level.disable_destructible_bad_places = undefined;
	
	level.player setStance( "stand" );
	
	setSavedDvar( "player_sprintUnlimited", "0" );
	setSavedDvar( "player_sprintSpeedScale", 1.5 );
	assert( isdefined( level.ai_friendlyFireBlockDuration ) );
	setSavedDvar( "ai_friendlyFireBlockDuration", level.ai_friendlyFireBlockDuration );
	
	flag_clear( "player_near_stairs" );
	
	// Block the alley with the forklift so player wont backtrack
	flag_set( "block_alley" );
	
	level waittill( "black_screen_finish" );
	
	thread autosave_now();
	thread timed_favela_autosaves();
	
	favela_friendlies();
	
	flag_clear( "give_favela_warning" );
	trigger_on( "favela_opening_civilians_spawn", "targetname" );
	getent( "favela_enter_player_clip", "targetname" ) delete();	// remove player clip brush that prevented the player from entering the favela prematurely.
	
	wait 0.05;
	
	// Make enemies talk more by modding battlechatter times
	thread modify_battlechatter_times();
	
	// Wait for torture sequence to end before going into favela
	flag_wait( "favela_move_friendlies" );
	
	flag_set( "favela_music" );
	
	enablePlayerWeapons( true );
	
	thread add_top_of_hill_objective();
	thread favela_gate();
	thread favela_warning();
	thread favela_dialog();
	thread soccer_dialog();
	thread meat_dies();
	thread royce_dies();
}

startSoccer()
{
	add_top_of_hill_objective();
	array_call( getentarray( "delete_for_start_soccer", "script_noteworthy" ), ::delete );
	movePlayerToStartPoint( "playerstart_soccer" );
	activate_trigger( "vision_shanty", "script_noteworthy" );
	flag_set( "civilians_walla" );
}

startHilltop()
{
	add_top_of_hill_objective();
	movePlayerToStartPoint( "playerstart_hilltop" );
	activate_trigger( "favela_hill", "script_noteworthy" );
	flag_set( "civilians_walla" );
}

trailer_talkers_1()
{
	spawn_talkers( "trailer_talkers_1" );
	level.player set_ignoreme( true );
	movePlayerToStartPoint( "trailer_talkers_1_player" );
}

trailer_talkers_2()
{
	spawn_talkers( "trailer_talkers_2" );
	level.player set_ignoreme( true );
	movePlayerToStartPoint( "trailer_talkers_2_player" );
}

trailer_talkers_3()
{
	spawn_talkers( "trailer_talkers_3" );
	level.player set_ignoreme( true );
	movePlayerToStartPoint( "trailer_talkers_3_player" );
}

spawn_talkers( targetname )
{
	spawners = getentarray( targetname, "targetname" );
	foreach( spawner in spawners )
	{
		guy = spawner spawn_ai( true );
		guy set_ignoreall( true );
		guy.goalradius = 16;
		guy setGoalPos( guy.origin );
		if ( !isdefined( spawner.animation ) )
			continue;
		guy.animname = "trailer";
		guy thread anim_loop_solo( guy, spawner.animation );
	}
}

startEnd()
{
	add_top_of_hill_objective();
	movePlayerToStartPoint( "playerstart_end" );
	activate_trigger( "favela_vision", "script_noteworthy" );
}

favela_gate()
{
	// Gate sequence gone, just some dialog now
	thread favela_gate_dialog();
}

favela_gate_dialog()
{
	// Wait for torture sequence to finish so we don't step on dialog
	flag_wait( "torture_sequence_done" );
	
	// Let's go.
	level.royce anim_single_solo( level.royce, "favela_ryc_letsgo" );
	
	// Wait for player to be somewhere nearby
	flag_wait( "player_near_stairs" );
	
	// Remember - there are civilians in the favela. Take clean shots, watch your background.
	level.royce anim_single_solo( level.royce, "favela_ryc_watchyourbg" );
	
	flag_set( "favela_gate_dialog_done" );
}

torture_sequence()
{
	node = getent( "torture_node", "targetname" );
	door = getent( "torture_door", "targetname" );
	
	torture_enemy_spawner_targetname = "torture_enemy_spawner";
	if ( getdvarint( "favela_trailer" ) > 0 )
		torture_enemy_spawner_targetname = "torture_enemy_spawner_trailer";
	
	guys[ 0 ] = spawn_targetname( torture_enemy_spawner_targetname );
	guys[ 0 ].animname = "torture_enemy";
	
	guys[ 1 ] = spawn_targetname( "torture_friendly_1_spawner" );
	guys[ 1 ].animname = "torture_friend1";
	
	guys[ 2 ] = spawn_targetname( "torture_friendly_2_spawner" );
	guys[ 2 ].animname = "torture_friend2";
	
	foreach( guy in guys )
	{
		guy set_ignoreme( true );
		guy set_ignoreall( true );
	}
	
	jumper_cables = spawn_anim_model( "torture_cables", node.origin );
	guys[ 3 ] = jumper_cables;
	
	//chad - comment this out once the map has been recompiled
	battery_origin = ( -527, -1043.5, 725 );
	battery_angles = ( 0, 289.5, 0 );
	car_battery = spawn( "script_model", battery_origin );
	car_battery.angles = battery_angles;
	car_battery setModel( "machinery_car_battery" );
	//----
	
	node anim_first_frame( guys, "torture" );
	
	guys[ 2 ] thread torture_sequence_door( door );
	
	guys[ 2 ] setLookAtEntity( level.player );

	
	guys[ 0 ] thread play_sound_on_entity( "scn_favela_captive_in_chair" );
	garagePos = guys[ 0 ].origin;
	
	node anim_single( guys, "torture" );
	
	array_call( guys, ::delete );
	
	flag_set( "torture_sequence_done" );
	flag_set( "favela_move_friendlies" );
	
	thread play_sound_in_space( "scn_favela_garage_interior", garagePos );
}

torture_sequence_door( door )
{
	finalDoorPos = door.origin;
	startingDoorPos = door.origin + ( 0, 0, 45 );
	door.origin = startingDoorPos;
	
	flag_wait( "drop_door" );
	
	door playsound( "scn_favela_garage_door" );
	door moveTo( finalDoorPos, 1.3, 0.1, 0.0 );
}

opening_scene()
{
	// open/close hotel doors for the sequence
	thread hotel_doors();
	
	// spawn player vehicle and put player inside
	player_vehicle = spawn_vehicle_from_targetname_and_drive( "player_vehicle" );
	assert( isdefined( player_vehicle ) );
	player_vehicle hidepart( "TAG_GLASS_FRONT_D" );
	player_vehicle hidepart( "TAG_BLOOD" );
	player_vehicle thread car_anims();
	player_vehicle thread car_driver_anims();
	player_vehicle thread play_sound_on_entity( "scn_favela_driveup" );
	thread player_rides_vehicle( player_vehicle );
	
	// spawn van
	van = spawn_vehicle_from_targetname_and_drive( "van" );
	assert( isdefined( van ) );
	van godon();
	van thread van_skid_sound();
	van.animname = "van";
	van SetAnimTree();
	
	// slow for intro screen - dont want to go too far while screen is black
	player_vehicle Vehicle_SetSpeed( 3, 5.0, 5.0 );
	van Vehicle_SetSpeed( 3, 5.0, 5.0 );
	
	wait 4;
	
	thread opening_scene_dialog();
	
	player_vehicle resumeSpeed( 2.5 );
	van resumeSpeed( 2.5 );
	
	// wait for van to get into position
	vehNode = getVehicleNode( "van_last_node", "script_noteworthy" );
	vehNode waittill( "trigger" );
	
	// get animation node
	animNode = getent( "opening_scene_node", "targetname" );
	
	// spawn actors ( actually just spawns drones )
	
	makarov_spawner = getent( "makarov_spawner", "targetname" );
	driver_spawner = getent( "opening_driver_spawner", "targetname" );
	gunner1_spawner = getent( "opening_gunner1_spawner", "targetname" );
	gunner2_spawner = getent( "opening_gunner2_spawner", "targetname" );
	
	wait 1;
	
	makarov = makarov_spawner spawn_ai( true );
	makarov.animname = "makarov";
	makarov thread magic_bullet_shield();
	makarov.ignorerandombulletdamage = true;
	makarov.grenadeawareness = 0;
	
	driver = driver_spawner spawn_ai( true );
	driver.animname = "driver";
	driver thread opening_death();
	driver thread blood_driver();
	
	gunner1 = gunner1_spawner spawn_ai( true );
	gunner1.animname = "gunner1";
	gunner1 thread opening_death();
	gunner1 thread blood_gunner1();
	
	gunner2 = gunner2_spawner spawn_ai( true );
	gunner2.animname = "gunner2";
	gunner2 thread opening_death();
	gunner2 thread blood_gunner2();
	
	guys[ 0 ] = makarov;
	guys[ 1 ] = driver;
	guys[ 2 ] = gunner1;
	guys[ 3 ] = gunner2;
	
	street_clip = getent( "street_civilian_clip", "targetname" );
	street_clip connectPaths();
	street_clip delete();
	
	flag_set( "opening_scene_started" );
	van thread van_door_sounds();
	van thread anim_single_solo( van, "door_open" );
	animNode anim_single( guys, "opening_scene" );
	
	level notify( "end_scene" );
	thread stop_traffic();
	
	// makarov shoots at player for a bit then flees
	animNode thread anim_loop_solo( makarov, "opening_scene_shoot", "stop_shooting" );
	thread break_windshield( player_vehicle );
	makarov makarov_shoot_player( player_vehicle );
	animNode notify( "stop_shooting" );
	makarov anim_stopanimscripted();
	
	// makarov runs around the corner and gets deleted
	runner_first_node = getnode( "runner_first_node", "targetname" );
	makarov.goalradius = 32;
	makarov thread delete_ai_at_goal( true );
	makarov set_ignoreall( true );
	makarov setGoalNode( runner_first_node );
	
	thread delayThread( 1.0, ::flag_set, "start_chase" );
	
	player_vehicle thread play_sound_on_entity( "scn_favela_npc_door_open" );
	
	wait 2;
	
	// get player out of the vehicle
	thread player_exits_vehicle( player_vehicle );
	flag_set( "visionset_chase" );
}

player_rides_vehicle( vehicle )
{
	level endon( "exiting_vehicle" );
	
	enablePlayerWeapons( false );
	
	MAX_ROTATE_ANG = 66;
	
	// attach player to viewmodel rig for crouch movement
	player_rig = spawn_anim_model( "player_rig" );
	player_rig.origin = vehicle getTagOrigin( "tag_passenger" );
	player_rig.angles = vehicle getTagAngles( "tag_passenger" );
	player_rig linkTo( vehicle, "tag_passenger" );
	player_rig hide();
	
	player_rig thread player_dies_in_vehicle();
	
	level.player playerLinkToDelta( player_rig, "tag_player", 1.0, MAX_ROTATE_ANG, MAX_ROTATE_ANG, 45, 20 );
	level.player allowProne( false );
	level.player allowCrouch( false );
	level.player allowStand( true );
	flag_clear( "player_is_ducking" );
	
	level.player endon( "death" );
	
	level.player setStance( "stand" );
	player_rig thread anim_first_frame_solo( player_rig, "duck_down" );
	
	notifyOnCommand( "go_crouch", "+movedown" );
	notifyOnCommand( "go_crouch", "+prone" );
	notifyOnCommand( "go_crouch", "+stance" );
	notifyOnCommand( "go_crouch", "lowerstance" );
	notifyOnCommand( "go_crouch", "togglecrouch" );
	notifyOnCommand( "go_crouch", "toggleprone" );
	notifyOnCommand( "go_crouch", "goprone" );
	notifyOnCommand( "go_crouch", "gocrouch" );
	
	notifyOnCommand( "go_stand", "+stance" );
	notifyOnCommand( "go_stand", "raisestance" );
	notifyOnCommand( "go_stand", "togglecrouch" );
	notifyOnCommand( "go_stand", "toggleprone" );
	notifyOnCommand( "go_stand", "+moveup" );
	notifyOnCommand( "go_stand", "+gostand" );
	
	for(;;)
	{
		level.player waittill( "go_crouch" );
		flag_set( "player_is_ducking" );
		level.player enableInvulnerability();
		level.player lerpViewAngleClamp( 0.3, 0, 0, 25, 25, 25, 0 );
		player_rig anim_single_solo( player_rig, "duck_down" );
		player_rig thread anim_loop_solo( player_rig, "duck_down_idle", "stop_down_idle" );
		
		level.player waittill( "go_stand" );
		level.player lerpViewAngleClamp( 0.3, 0, 0, MAX_ROTATE_ANG, MAX_ROTATE_ANG, 45, 20 );
		player_rig notify( "stop_down_idle" );
		player_rig anim_single_solo( player_rig, "duck_up" );
		flag_clear( "player_is_ducking" );
		level.player disableInvulnerability();
	}
}

player_dies_in_vehicle()
{
	level endon( "exiting_vehicle" );
	
	level.player waittill( "shot_next_frame" );
	
	level.player lerpViewAngleClamp( 0, 0, 0, 0, 0, 0, 0 );
	self anim_single_solo( self, "die" );
}

player_exits_vehicle( vehicle )
{
	wait 1.2;
	
	level notify( "exiting_vehicle" );
	
	vehicle notify( "door_open" );
	
	exit_point = getent( "player_vehicle_exit_point", "targetname" );
	
	dummy = spawn( "script_model", level.player.origin );
	dummy.angles = level.player.angles;
	dummy setmodel( "tag_origin" );
	level.player playerLinkTo( dummy, "tag_player", 1.0, 45, 45, 45, 20 );
	
	MOVETIME = 1.5;
	ACCEL = 0.3;
	DECEL = 0.3;
	dummy moveTo( exit_point.origin, MOVETIME, ACCEL, DECEL );
	dummy rotateTo( exit_point.angles, MOVETIME, ACCEL, DECEL );
	
	wait MOVETIME;
	
	level.player unlink();
	level.player disableInvulnerability();
	enablePlayerWeapons( true );
	
	level.player allowProne( true );
	level.player allowCrouch( true );
	level.player allowStand( true );
	level.player setStance( "stand" );
	
	setSavedDvar( "player_sprintUnlimited", "1" );
	setSavedDvar( "player_sprintSpeedScale", PLAYER_SPRINT_SCALE );
}

van_skid_sound()
{
	flag_wait( "van_skid" );
	self thread play_sound_on_entity( "scn_favela_van_skid2stop" );
}

van_door_sounds()
{
	self thread delayThread( 0.0, ::play_sound_on_entity, "scn_favela_van_door_open" );
	self thread delayThread( 2.3, ::play_sound_on_entity, "scn_favela_van_driverdoor_open" );
	self thread delayThread( 2.8, ::play_sound_on_entity, "scn_favela_van_door_close" );
	self thread delayThread( 4.0, ::play_sound_on_entity, "scn_favela_van_driverdoor_close" );
}

opening_scene_dialog()
{
	wait 2.0;
	
	// Ghost, the plates are a match.
	radio_dialogue( "favela_cmt_ready2move" );
	
	// Copy. Any sign of Rojas's right hand man?
	radio_dialogue( "favela_gst_good2go" );
	
	// Negative. They've stopped twice already - no sign of him.
	radio_dialogue( "favela_cmt_rogerthat" );
	
	flag_wait( "van_in_position" );
	thread autosave_now_silent();
	
	wait 0.5;
	
	// Wait, they've stopped again. Standby.
	radio_dialogue( "favela_cmt_inposition" );
	
	wait 2.5;
	
	// Got a positive ID! Whoever these guys are, they're not happy to see him…
	radio_dialogue( "favela_cmt_insight" );
	
	wait 0.9;
	
	// Ghost we have a situation here!
	radio_dialogue( "favela_cmt_needhimalive" );
	
	wait 0.2;
	
	// Get down get down!!!
	radio_dialogue( "favela_cmt_getdown" );
}

chase()
{
	flag_wait( "start_chase" );
	flag_clear( "runner_gets_away" );
	
	setSavedDvar( "compass", 1 );
	setSavedDvar( "hud_showStance", 1 );
	setSavedDvar( "ai_friendlyFireBlockDuration", 0 );
	
	thread autosave_now();
	
	// disable badplaces on destructibles so that soap can run through the city without getting stuck, then set it back
	level.disable_destructible_bad_places = true;
	
	// spawn soap
	level.soap = spawn_targetname( "soap_spawner", true );
	level.soap set_ignoreall( true );
	level.soap.usechokepoints = false;
	level.soap.fixednode = false;
	level.soap.disableBulletWhizbyReaction = true;
	level.soap.a.disablePain = true;
	level.soap.maxsightdistsqrd = 0;
	level.soap.ignoresuppression = true;
	level.soap.animname = "mactavish";
	level.soap.pathRandomPercent = 0;
	level.soap thread magic_bullet_shield();
	
	chase_objective_location = getent( "chase_objective_location", "targetname" );
	objective_add( 1, "current", &"FAVELA_OBJ_CATCH_RUNNER", chase_objective_location.origin );
	
	waveNode = getnode( "soap_start_node", "targetname" );
	waveNode anim_teleport_solo( level.soap, "run_and_wave" );
	flag_set( "soap_exits_car" );
	waveNode anim_single_solo( level.soap, "run_and_wave" );
	
	thread chase_dialog();
	level.soap thread control_run_speed();
	
	// Wait for makarov street trigger and then spawn makarov
	// He will run his path when he spawns
	//trigger_wait( "makarov_street_spawn_trig", "targetname" );
	
	makarov = spawn_targetname( "makarov_street_spawner", true );
	makarov set_ignoreall( true );
	makarov set_ignoreme( true );
	makarov.disableBulletWhizbyReaction = true;
	makarov.a.disablePain = true;
	makarov.maxsightdistsqrd = 0;
	makarov.ignoresuppression = true;
	makarov.usechokepoints = false;
	makarov.skipBloodPool = true;
	makarov thread magic_bullet_shield();
	makarov thread faust_assistant_kill_player_monitor();
	makarov thread teleport_runner_for_takedown_1();
	makarov thread teleport_runner_for_takedown_2();
	makarov thread makarov_alley_fall();
	makarov thread makarov_gets_away();
	makarov thread player_doesnt_chase();
	makarov thread makarov_check_player_distance();
	
	objective_onentity( 1, level.soap, ( 0, 0, 70 ) );
}

makarov_check_player_distance()
{
	// Player fails if you don't keep up with the runner
	// Once you're told to take the shot you can't fail for getting too far away anymore
	// He will get away and fail soon anyways if you don't shoot him so give the player some freedom to take him down here
	
	level endon( "take_the_shot" );
	self endon( "death" );
	self endon( "runner_shot" );
	
	MIN_DIST = 500;
	MAX_DIST = 2500;
	
	SPEED_MIN = 1.0;
	SPEED_MAX = 1.6;
	
	for(;;)
	{
		d = distance( level.player.origin, self.origin );
		
		if ( d > MAX_DIST )
		{
			setdvar( "ui_deadquote", "@FAVELA_RUNNER_GOT_AWAY" );
			maps\_utility::missionFailedWrapper();
			return;
		}
		
		runSpeed = self.moveplaybackrate;
		if ( d < MIN_DIST )
		{
			// player too close - run faster
			runSpeed += 0.1;
		}
		else
		{
			// player farther away - run normal speed again
			runSpeed -= 0.1;
		}
		self.moveplaybackrate = cap_value( runSpeed, SPEED_MIN, SPEED_MAX );
		
		wait 0.1;
	}
}

chase_dialog()
{
	level endon( "runner_shot" );
	
	// Ghost, our driver's dead! We're on foot! Meet us at the Hotel Rio and cut him off if you can!
	level.soap dialogue_queue( "favela_cmt_driversdead" );
	
	// Roger, I'm on my way!
	radio_dialogue( "favela_gst_onmyway" );
	
	flag_wait( "runner_in_alley" );
	
	// He went into the alley - bloody hell he's fast!
	radio_dialogue( "favela_gst_hesfast" );
	
	// Non-lethal takedowns only! We need him alive!
	level.soap dialogue_queue( "favela_cmt_nonlethal" );
	
	flag_wait( "take_the_shot" );
	
	thread legshot_pre_stinger();
	
	// Roach - take the shot!! Go for his leg!!
	level.soap dialogue_queue( "favela_cmt_takeshot" );
}

legshot_pre_stinger()
{
	level endon( "runner_shot" );
	
	wait 0.75;
	
	thread music_stop( 0.25 );
	level.player thread play_sound_on_entity( "favela_legshot_pre_stinger" );
}

teleport_runner_for_takedown_1()
{
	// This makes sure he hasn't gotten too far down the alley when the player gets there.
	// In the case that the player is really far behind they would have failed already for
	// not keeping up so I dont really have to worry about that.
	
	self endon( "death" );
	
	trig = getent( "teleport_runner_1", "targetname" );
	teleportLoc = getent( trig.target, "targetname" );
	
	trig waittill( "trigger" );
	
	// Only teleport him if he's gone around the corner out of sight
	if ( !flag( "runner_in_alley" ) )
		return;
	
	self forceTeleport( teleportLoc.origin, teleportLoc.angles );
}

teleport_runner_for_takedown_2()
{
	self endon( "death" );
	
	trig = getent( "teleport_runner_2", "targetname" );
	teleportLoc = getent( trig.target, "targetname" );
	
	trig waittill( "trigger" );
	
	// Only teleport him if he's gone around the corner out of sight
	if ( !flag( "runner_in_alley2" ) )
		return;
	
	self forceTeleport( teleportLoc.origin, teleportLoc.angles );
}

makarov_alley_fall()
{
	//level endon( "makarov_wounded_successfully" );
	level endon( "stop_monitoring_makarov_damage" );
	
	self set_generic_deathanim( "alley_death_fall" );
	
	for(;;)
	{
		self waittill( "damage", damage, attacker, direction_vec, point, type );
		
		// Only Soap and the player can damage him
		if ( !isdefined( attacker ) )
			continue;
		if ( attacker != level.player && attacker != level.soap )
			continue;
		
		// If damage is only 1 ignore it. This happens when flashbangs and other things bounce off him
		if ( damage <= 1 )
			continue;
		
		// If player uses grenades you fail
		if ( !isdefined( type ) )
			continue;
		if ( issubstr( type, "GRENADE" ) )
		{
			self thread makarov_alley_killed( point );
			return;
		}
		
		// Only non-lethal shots are allowed
		if ( self was_shot_in_lethal_area() )
		{
			self thread makarov_alley_killed( point );
			return;
		}
		
		self thread makarov_alley_wounded();
	}
}

was_shot_in_lethal_area()
{
	assert( isdefined( self ) );
	
	if ( isdefined( self.disableLethalCheck ) )
		return false;
	
	if ( !isdefined( self.damagelocation ) )
		return true;
	
	switch( self.damagelocation )
	{
		case "none":
			if ( flag( "makarov_alley_wounded" ) )
				return false;
			return true;
		case "helmet":
		case "head":
		case "neck":
		case "torso_upper":
		case "torso_lower":
			return true;
		default:
			return false;
	}
}

makarov_alley_wounded()
{
	level notify( "runner_shot" );
	
	if ( flag( "makarov_alley_wounded" ) )
		return;
	flag_set( "makarov_alley_wounded" );
	
	self thread makarov_alley_wounded_and_crawl();
	
	wait 1.0;
	
	level.soap thread anim_single_solo( level.soap, "favela_cmt_hesdown" );
	wait 1.0;
	
	level notify( "makarov_wounded_successfully" );
	objective_State( 1, "done" );
	thread startFavela();
}

makarov_alley_wounded_and_crawl()
{
	self endon( "deleted" );
	self endon( "death" );
	
	if ( flag( "runner_disable_crawl" ) )
		self clear_deathanim();
	
	self stop_magic_bullet_shield();
	level.runner = self;
	level.runner thread play_sound_in_space( "scn_favela_death_crawl", level.runner.origin );
	self.noDrop = true;
	self kill();
}

makarov_alley_killed( point )
{	
	level notify( "runner_shot" );
	
	if ( !flag( "makarov_alley_wounded" ) )
	{
		self clear_deathanim();
		self stop_magic_bullet_shield();
		self kill( point, level.player );
	}
	
	setdvar( "ui_deadquote", "@FAVELA_KILLED_RUNNER" );
	maps\_utility::missionFailedWrapper();
}

makarov_gets_away()
{
	self endon( "runner_shot" );
	
	self waittill( "reached_path_end" );
	
	setdvar( "ui_deadquote", "@FAVELA_RUNNER_GOT_AWAY" );
	maps\_utility::missionFailedWrapper();
}

player_doesnt_chase()
{
	self endon( "runner_shot" );
	
	flag_wait( "runner_gets_away" );
	
	setdvar( "ui_deadquote", "@FAVELA_RUNNER_GOT_AWAY" );
	maps\_utility::missionFailedWrapper();
}

stop_sounds_during_black()
{
	// Total hack to get sounds to stop playing. No good way to do this
	
	X_LINE = -50;
	ents = getentarray();
	foreach( ent in ents )
	{
		if ( ent.origin[ 0 ] < X_LINE )
			continue;
		
		if ( !isdefined( ent.code_classname ) )
			continue;
		switch( ent.classname )
		{
			case "script_vehicle":
			case "script_origin":
			case "script_model":
			case "script_struct":
				ent notify( "stop_car_alarm" );
				ent stopSounds();
				break;
			default:
				break;
		}
	}
}

fade_to_black_alley( fadeInTime, fadeOutTime, duration )
{
	setSavedDvar( "compass", 0 );
	setSavedDvar( "hud_showStance", 0 );
	
	stop_sounds_during_black();
	level.player playLocalSound( "scn_favela_legshot_stinger" );
	
	overlay = newHudElem();
	overlay.x = 0;
	overlay.y = 0;
	overlay setshader( "black", 640, 480 );
	overlay.alignX = "left";
	overlay.alignY = "top";
	overlay.horzAlign = "fullscreen";
	overlay.vertAlign = "fullscreen";
	
	overlay.alpha = 0;
	if ( fadeInTime > 0 )
		overlay fadeOverTime( fadeInTime );
	overlay.alpha = 1;
	
	wait fadeInTime;
	wait 0.05;
	
	level.player freezeControls( true );
	level notify( "black_screen_start" );
	
	wait duration;
	
	level.player freezeControls( false );
	level notify( "black_screen_finish" );
	
	if ( fadeOutTime > 0 )
		overlay fadeOverTime( fadeOutTime );
	overlay.alpha = 0;
	
	wait fadeOutTime;
	
	setSavedDvar( "compass", 1 );
	setSavedDvar( "hud_showStance", 1 );
	
	overlay destroy();
}

hotel_doors()
{
	rotationAmount = 110;
	left = getent( "hotel_door_left", "targetname" );
	right = getent( "hotel_door_right", "targetname" );
	knobs = getentarray( "hotel_knob", "targetname" );
	
	// Doors open
	left rotateYaw( rotationAmount * -1, 0.05 );
	right rotateYaw( rotationAmount, 0.05 );
	array_call( knobs, ::hide );
	
	flag_wait( "car_getting_shot" );
	flag_wait( "player_is_ducking" );
	
	// Doors close
	left rotateYaw( rotationAmount, 0.05 );
	right rotateYaw( rotationAmount * -1, 0.05 );
	array_call( knobs, ::show );
}

break_windshield( vehicle )
{
	flag_wait( "driver_dead" );
	
	// break windshield on player vehicle
	vehicle thread play_sound_on_tag( "scn_favela_car_glass_shatter", "TAG_GLASS_FRONT_D" );
	playfxontag( getfx( "car_glass_interior" ), vehicle, "TAG_GLASS_FRONT_FX" );
	vehicle showpart( "TAG_GLASS_FRONT_D" );
	vehicle showpart( "TAG_BLOOD" );
	vehicle hidepart( "TAG_GLASS_FRONT" );
	
	if ( !isdefined( level.hula_girl ) )
		return;
	
	level.hula_girl unlink();
	force = 150;
	level.hula_girl physicsLaunchClient( level.hula_girl.origin + ( 0, 0, 5 ), ( 0, force, 0 ) );
}

driver_shot_in_head( driver )
{
	
	//blood yessss!!!
	playfxontag( getfx( "blood" ), driver, "J_Head" );
	playfxontag( getfx( "blood_dashboard_splatter" ), driver, "J_Head" );
	
	wait 0.1;
	
	flag_set( "driver_dead" );
}

driver_falls_on_horn( driver )
{
	alias = "scn_favela_merc_horn_loop";
	driver thread play_loop_sound_on_entity( alias, ( 0, 0, 20 ) );
	wait 60;
	driver notify( "stop sound" + alias );
}

blood_gunner1()
{
	wait 6.6;
	playfxontag( getfx( "blood" ), self, "J_Hip_RI" );
	wait 0.1;
	playfxontag( getfx( "blood" ), self, "J_SpineLower" );
}

blood_gunner2()
{
	wait 7.1;
	playfxontag( getfx( "blood" ), self, "J_Hip_RI" );
	wait 0.2;
	playfxontag( getfx( "blood" ), self, "J_SpineLower" );
	wait 1.2;
	playfxontag( getfx( "blood" ), self, "J_Head" );
}

blood_driver()
{
	wait 9.5;
	playfxontag( getfx( "blood" ), self, "J_Shoulder_RI" );
}

makarov_shoot_player( vehicle )
{
	self endon( "death" );
	
	shots = 15;
	shot_kill_start = 10;
	shot_kill_stop = 15;
	
	width = 29;
	height = 8;
	interval_min = 0.1;
	interval_max = 0.3;
	fireAnim = level.scr_anim[ self.animname ][ "stand_fire" ];
	
	center = vehicle getTagOrigin( "tag_glass_front_fx" );
	windowAng = vehicle getTagAngles( "tag_glass_front_fx" );
	right = anglesToRight( windowAng );
	up = anglesToUp( windowAng );
	
	farLeft = center - ( right * width );
	farRight = center + ( right * width );
	top = center + ( up * height );
	bottom = center - ( up * height );
	
	//thread draw_line_for_time( self.origin, farLeft, 1, 0, 0, 10.0 );
	//thread draw_line_for_time( self.origin, farRight, 1, 0, 0, 10.0 );
	//thread draw_line_for_time( self.origin, top, 1, 0, 0, 10.0 );
	//thread draw_line_for_time( self.origin, bottom, 1, 0, 0, 10.0 );
	
	flag_set( "car_getting_shot" );
	
	shootPoints = [];
	for( i = 0 ; i < shots ; i++ )
	{
		rand_horizontal_offset = randomfloatrange( width * -1, width );
		rand_vertical_offset = randomfloatrange( height * -1, height );
		point = center;
		point += right * rand_horizontal_offset;
		point += up * rand_vertical_offset;
		
		//thread draw_line_for_time( self.origin, point, 1, 1, 1, 10.0 );
		
		shootPoints[ shootPoints.size ] = point;
	}
	
	for( i = 0 ; i < shots ; i++ )
	{	
		self shoot( 100.0, shootPoints[ i ] );
		
		fxOrigin = shootPoints[ i ];
		forward = vectorNormalize( fxOrigin - self getTagOrigin( "tag_flash" ) );
		playFX( getfx( "glass_exit" ), fxOrigin, forward );		
		
		if ( i >= shot_kill_start && i <= shot_kill_stop )
		{
			// this is a deadly bullet! Check that the player is crouching
			if ( !flag( "player_is_ducking" ) )
				self thread kill_player();
		}
		
		self setAnimKnobRestart( fireAnim, 1, .2, 1.0 );
		wait randomfloatrange( interval_min, interval_max );
	}
	
	self clearAnim( fireAnim, 0 );
}

kill_player()
{
	level.player notify( "shot_next_frame" );
	wait 0.05;
	level.player enableDeathShield( false );
	level.player enableHealthShield( false );
	level.player disableInvulnerability();
	level.player doDamage( level.player.health + 50000, level.player getEye(), self );
	wait 0.05;
	if ( isalive( level.player ) )
		level.player kill();
	assert( !isAlive( level.player ) );
}

opening_death()
{
	level waittill( "end_scene" );
	
	self.allowdeath = true;
	self.a.nodeath = true;
	self.noragdoll = true;
	self setContents( 0 );
	self kill();
}

add_top_of_hill_objective()
{
	originEnt = getent( "mission_objective_location", "targetname" );
	objective_add( 2, "current", &"FAVELA_OBJ_REACH_TOP", originEnt.origin );
}

favela_friendlies()
{
	level.meat = spawn_targetname( "meat_spawner", true );
	level.meat thread magic_bullet_shield();
	level.meat.animname = "meat";
	
	level.royce = spawn_targetname( "royce_spawner", true );
	level.royce thread magic_bullet_shield();
	level.royce.animname = "royce";
}

favela_opening_civilians()
{
	trigger_wait( "favela_opening_civilians_spawn", "targetname" );
	spawners = getentarray( "favela_opening_civilian_spawner", "targetname" );
	
	array_thread( spawners, ::favela_opening_civilians_think );
	thread favela_civilians_scream();
	
	flag_set( "favela_civilians_spawned" );
}

favela_opening_civilians_think()
{
	// spawn the civilian
	civilian = self spawn_ai( true );
	civilian endon( "death" );
	
	if ( !isdefined( level.favelaCivilians ) )
		level.favelaCivilians = [];
	level.favelaCivilians[ level.favelaCivilians.size ] = civilian;
	
	wait 0.05;
	civilian.alertlevel = "noncombat";
	
	// Make walkers walk instead of do idle animations
	if ( !isdefined( self.animation ) )
	{
		civilian thread delete_ai_at_path_end();
		civilian.useChokePoints	= false;
		return;
	}
	
	// make a node
	eNode = undefined;
	if ( isdefined( self.script_linkto ) )
	{
		eNode = get_linked_ent();
	}
	else
	{
		eNode = spawn( "script_origin", self.origin );
		eNode.angles = self.angles;
	}
	assert( isdefined( eNode ) );
	
	// do idle as specified in editor
	animScene = self.animation;
	eNode thread anim_generic_loop( civilian, animScene, "stop_idle_anim" );
	
	// wait for firefight to break out
	civilian waittill( "combat" );
	
	flag_set( "favela_civilians_fleeing" );
	
	wait randomfloat( 1.5 );
	
	// civilian stops looping idle
	eNode notify( "stop_idle_anim" );
	civilian notify( "stop_idle_anim" );
	civilian stopAnimScripted();
	
	// civilian runs away and gets deleted
	civilian.useChokePoints	= true;
	
	node = getnode( "favela_civ_flee_node_opening", "targetname" );
	civilian thread follow_path( node );
	civilian thread delete_ai_at_path_end();
}

favela_civilians_scream()
{
	soundEnt = getent( "favela_civilians_scream_ent", "targetname" );
	soundEntEndPos = getent( soundEnt.target, "targetname" );
	
	flag_wait( "favela_civilians_fleeing" );
	
	soundEnt playSound( "scn_favela_civ_outofhere_screams" );
	soundEnt moveTo( soundEntEndPos.origin, 6.0, 4.0, 2.0 );
}

favela_music()
{
	flag_wait( "favela_music" );
	level endon( "favela_music" );
	
	thread favela_music_stop();
	alias = "favela_tension";
	time = musicLength( alias );
	
	
	
	while( flag( "favela_music" ) )
	{
		thread music_play( alias );
		wait time;
	}
}

favela_music_stop()
{
	flag_waitopen( "favela_music" );
	music_stop( 3 );
}

upper_village_music()
{
	flag_wait( "upper_village_music" );
	level endon( "faust_appearance_1" );
	
	// Roach - this is their territory and they know it well! Keep an eye open for ambush positions and check your corners!
	radio_dialogue( "favela_cmt_theirterritory" );
	
	alias = "favela_uppervillage_start";
	time = musicLength( alias );
	
	while( !flag( "faust_appearance_1" ) )
	{	
		thread music_play( alias, 2.0 );
		wait time;
	}
}

faust_music()
{
	level endon( "faust_music" );
	
	thread faust_music_stop();
	
	alias = "favela_moneyrun";
	time = musicLength( alias );
	
	while( flag( "faust_music" ) )
	{
		thread music_play( alias );
		wait time;
	}
}

faust_music_stop()
{
	flag_waitopen( "faust_music" );
	music_stop( 3 );
}

random_favela_background_runners()
{
	level endon( "cleared_favela" );
	spawners = getentarray( "random_favela_background_runner", "targetname" );
	
	flag_wait( "favela_enemies_spawned" );
	
	for(;;)
	{
		spawners = array_randomize( spawners );
		foreach ( spawner in spawners )
		{
			if ( flag( "cleared_favela" ) )
				return;
			
			spawner.count = 1;
			spawner thread spawn_ai( true );
			
			wait randomintrange( 4, 8 );
		}
	}
}

faust_appearance_1()
{
	flag_wait( "faust_appearance_1" );
	
	thread autosave_by_name( "faust_appearance_1" );
	
	thread faust_appearance_1_dialog();
	
	faust = spawn_targetname( "faust_spawner_1", true );
	faust thread objective_on_faust();

	thread music_stop( 3.0 );
	wait 3.2;
	flag_set( "faust_music" );
	thread faust_music();
}

objective_on_faust()
{
	assert( isalive( self ) );
	
	level notify( "objective_on_faust" );
	level endon( "objective_on_faust" );
	
	// put the objective marker on Faust
	objective_onentity( 2, self, ( 0, 0, 70 ) );
	objective_setpointertextoverride( 2, &"FAVELA_OBJ_CAPTURE" );
	setsaveddvar( "objectiveFadeTooFar", 0.1 );
	
	// wait until we delete Faust
	self waittill( "death" );
	
	// Marker back to top of hill
	originEnt = getent( "mission_objective_location", "targetname" );
	objective_position( 2, originEnt.origin );
	setsaveddvar( "objectiveFadeTooFar", 25 );
}

faust_appearance_1_dialog()
{
	// Roach! I've spotted Faust, he's making a run for it! He's headed your way!
	radio_dialogue( "favela_cmt_spottedfaust" );
	
	// And don't shoot him! We need him alive and unharmed! 
	radio_dialogue( "favela_cmt_unharmed" );
	
	wait 5.0;
	
	// Roach, we're going to cut him off at the summit, keep pushing him that way! Go! Go!
	radio_dialogue( "favela_cmt_cutoff" );
}

faust_appearance_2()
{
	flag_wait( "faust_appearance_2" );
	
	faust = spawn_targetname( "faust_spawner_2", true );
	faust thread objective_on_faust();
}

faust_appearance_3()
{
	flag_wait( "faust_appearance_3" );
	
	faust = spawn_targetname( "faust_spawner_3", true );
	faust thread objective_on_faust();
	
	thread faust_appearance_3_dialog();
}

faust_appearance_3_dialog()
{
	level endon( "ending_sequence_dialog" );
	
	// We've got eyes on Faust - wait! Shite! he's headed back towards you! 
	radio_dialogue( "favela_cmt_backtowards" );
	
	if ( flag( "ending_sequence_dialog" ) )
		return;
	
	// Roach, keep pushing him up the hill! Don't let him double back!
	radio_dialogue( "favela_cmt_doubleback" );
}

faust_appearance_4()
{
	flag_wait( "faust_appearance_4" );
	
	faust = spawn_targetname( "faust_spawner_4", true );
	faust thread objective_on_faust();
}

street_scenes()
{
	flag_wait( "start_street_sequences_1" );
	
	thread street_scene_civilian_180_runaway();
	thread street_scene_civilian_wounded_1();
	
	thread street_scene_destruction();
}

street_scene_civilian_180_runaway()
{
	spawner = getent( "civilian_180_runaway", "targetname" );
	animNode = spawn( "script_origin", spawner.origin );
	animNode.angles = spawner.angles;
	animScene = spawner.animation;
	runtoNode = getnode( spawner.target, "targetname" );
	
	guy = spawner spawn_ai( true );
	guy endon( "death" );
	guy.allowdeath = true;
	animNode anim_generic( guy, animScene );
	
	guy.goalradius = 32;
	guy thread delete_ai_at_goal();
	guy setGoalNode( runtoNode );
}

street_scene_civilian_wounded_1()
{
	wounded_spawner = getent( "wounded_guy_1", "targetname" );
	helper_spawner = getent( "wounded_guy_helper_1", "targetname" );
	woundedAnimScene = wounded_spawner.animation;
	helperAnimScene = helper_spawner.animation;
	animNode = wounded_spawner get_linked_ent();
	runtoNode = getnode( helper_spawner.target, "targetname" );
	
	wounded = wounded_spawner spawn_ai( true );
	helper = helper_spawner spawn_ai( true );
	
	wounded.a.nodeath = true;
	
	animNode thread anim_generic_first_frame( wounded , woundedAnimScene );
	animNode thread anim_generic_first_frame( helper , helperAnimScene );
	
	helper endon( "death" );
	
	// wait for player to hit the proximity trigger
	trigger_wait( "wounded_guy_1_proximity", "targetname" );
	
	animNode thread anim_generic( wounded , woundedAnimScene );
	animNode anim_generic( helper , helperAnimScene );
	
	// scene is over, put wounded guy back into first frame idle
	animNode thread anim_generic_first_frame( wounded , woundedAnimScene );
	
	// helper is done, he runs away now and gets deleted
	helper.goalradius = 32;
	helper thread delete_ai_at_goal();
	helper setGoalNode( runtoNode );
}

street_scene_destruction()
{
	//chad - disabled this because nobody really likes it? Try without it for now.
	
	car1 = getent( "force_explosion_car_1", "script_noteworthy" );
	car2 = getent( "force_explosion_car_2", "script_noteworthy" );
	car3 = getent( "force_explosion_car_3", "script_noteworthy" );
	
	thread delayThread( 0.0, ::street_scene_gunshots, car1.origin );
	car1 thread delayThread( 1.8, ::destructible_force_explosion );
	
	thread delayThread( 2.5, ::street_scene_gunshots, car2.origin );
	car2 thread delayThread( 3.2, ::destructible_force_explosion );
	
	thread delayThread( 4.0, ::street_scene_gunshots, car3.origin );
	thread delayThread( 5.0, ::street_scene_gunshots, car3.origin );
	car3 thread delayThread( 6.2, ::destructible_force_explosion );
}

street_scene_gunshots( org )
{
	shots = randomintrange( 5, 10 );
	for( i = 0 ; i < shots ; i++ )
	{
		thread play_sound_in_space( "weap_deserteagle_fire_npc", org );
		wait randomfloatrange( 0.1, 0.3 );
	}
}

favela_warning()
{
	// Friendlies take up positions above the favela and wait for the player to approach
	level.meat.goalradius = 32;
	level.royce.goalradius = 32;
	level.meat set_goal_node_targetname( "meat_first_node" );
	level.royce set_goal_node_targetname( "royce_first_node" );
	
	thread player_gives_favela_warning();
	
	// Wait for other dialog to be done
	flag_wait( "favela_gate_dialog_done" );
		
	// Wait for player to approach
	flag_wait( "give_favela_warning" );
	
	animNode = getnode( "favela_warning_node", "targetname" );
	if ( !flag( "favela_civilians_alerted" ) )
	{
		// Royce tells meat to give the warning
		level.royce anim_single_solo( level.royce, "favela_ryc_warning" );			// Meat, give the civvies a fair warning.
		level.meat thread anim_single_solo( level.meat, "favela_met_rogerthat" );	// Roger that.
		
		// meat gives warning
		animNode anim_reach_solo( level.meat, "favela_warning_jump" );
	}
	
	// make friendlies go to nodes at the bottom of the favela
	level.meat thread set_goal_node_targetname( "favela_warning_guy_first_node" );
	level.royce thread set_goal_node_targetname( "favela_other_guy_first_node" );
	
	// friendlies are now on color node system
	level.meat enable_ai_color();
	level.royce enable_ai_color();
	
	if ( !flag( "favela_civilians_alerted" ) )
	{
		animNode anim_single_solo( level.meat, "favela_warning_jump" );
		animNode anim_single_solo( level.meat, "favela_warning_landing" );
	}
	
	// spawn favela enemies
	flag_set( "favela_enemies_spawned" );
	thread activate_trigger( "favela_spawn_trigger", "script_noteworthy", level.player );
	
	thread battlechatter_on( "allies" );
	thread battlechatter_on( "axis" );
}

player_gives_favela_warning()
{
	flag_wait( "favela_civilians_spawned" );
	
	thread player_gives_favela_warning_weapons();
	thread player_gives_favela_warning_trigger();
}

player_gives_favela_warning_weapons()
{
	level endon( "favela_civilians_alerted" );
	
	level.player waittill_any( "grenade_fire", "weapon_fired" );
	
	thread alert_favela_civilians();
}
player_gives_favela_warning_trigger()
{
	level endon( "favela_civilians_alerted" );
	
	flag_wait( "player_entered_favela" );
	
	thread alert_favela_civilians();
}

alert_favela_civilians()
{
	flag_set( "favela_civilians_alerted" );
	
	wait 1.5;
	
	foreach( civilian in level.favelaCivilians )
	{
		if ( isdefined( civilian ) )
			civilian.alertlevel = "alert";
	}
}

favela_dialog()
{
	flag_wait( "favela_enemies_spawned" );
	
	wait 7.0;
	
	radio_dialogue( "favela_cmt_fullbattalion" );	// Bravo Six, be advised - we've engaged enemy militia at the lower village!
	radio_dialogue( "favela_ryc_withyou" );			// Roach! I'm with you! Watch the rooftops! Go!
	
	// wait for player to fight in a bit
	flag_wait( "player_in_lower_favela_shanty" );
	
	radio_dialogue( "favela_cmt_doingok" );			// Royce, gimme a sitrep, over!
	radio_dialogue( "favela_ryc_nosign" );			// Lots of militia but no sign of Faust over here, over!
	radio_dialogue( "favela_cmt_keepsearching" );	// Copy that! Keep searching! Let me know if you see him! Out!
	
	wait 2.5;
	
	radio_dialogue( "favela_ryc_moveup" );			// Roach! Move up! Let's go!
	
	// allow meat to die now that dialog with him is over
	flag_set( "allow_meat_death" );
}

soccer_dialog()
{
	flag_wait( "cleared_favela" );
	
	radio_dialogue( "favela_cmt_cuthimoff" );	// Roach - we've got Faust's location! He's headed west along the upper levels of the favela.
	radio_dialogue( "favela_cmt_keepgoing" );	// We'll keep him from doubling back on our side - keep going and cut him off up top!
	wait 1.0;
	radio_dialogue( "favela_cmt_notime" );		// There's no time to wait for backup. You're gonna have to do this on your own. Good luck. Out.
}

meat_dies()
{
	// wait until all dialog is done before killing him because he needs to say some lines before he dies
	flag_wait( "allow_meat_death" );
	flag_wait( "player_midway_through_lower_favela" );
	
	// meat becomes vulnerable to die now. If he doens't die by the time we hit the next flag trigger then force kill him.
	level.meat thread meat_dies_dialog();
	level.meat stop_magic_bullet_shield();
	wait 0.05;
	level.meat.health = 1;
	level.meat thread meat_force_death();
}

meat_force_death()
{
	self endon( "death" );
	
	flag_wait( "force_meat_death" );
	
	magicBullet( "dragunov", ( -5427, -77, 1790 ), self getEye() );
	wait 0.1;
	self kill();
}

meat_dies_dialog()
{
	self waittill( "death" );
	
	// Meat is down! I repeat, Meat is down!
	radio_dialogue( "favela_ryc_meatisdown" );
	
	flag_set( "allow_royce_death" );
}

royce_dies()
{
	// wait until meat is dead, and dialog is done about him dying, and player is most of the way through the lower favela
	flag_wait( "force_meat_death" );
	flag_wait( "allow_royce_death" );
	
	// royce becomes vulnerable now. If he doesn't die by the time we hit the next flag trigger then force kill him.
	level.royce thread royce_dies_dialog();
	level.royce stop_magic_bullet_shield();
	wait 0.05;
	level.royce.health = 1;
	level.royce thread royce_force_death();
}

royce_force_death()
{
	self endon( "death" );
	
	flag_wait( "force_kill_royce" );
	
	magicBullet( "dragunov", ( -5427, -77, 1790 ), self getEye() );
	wait 0.1;
	self kill();
}

royce_dies_dialog()
{
	self waittill( "death" );
	
	withinView = false;
	if ( isdefined( self ) && isdefined( self.origin ) )
		withinView = level.player player_looking_at( self.origin, cos( 45 ) );
	
	if ( !withinView )
		radio_dialogue( "favela_ryc_imhit" );			// Roach! I'm down! Meat's dead! They're all over - (gunfire, angry shouting in Portuguese)
}

upper_village_triggered_dialog()
{
	level endon( "faust_appearance_1" );
	
	// Trigger
	flag_wait( "dialog_watch_rooftops" );
	
	radio_dialogue( "favela_cmt_watchrooftops" );	// Roach, watch the rooftops! We've had a few close calls with RPGs and machine guns positioned up high!
	
	wait 4.0;
	
	radio_dialogue( "favela_cmt_stilltracking" );	// Roach, we're taking heavy fire from militia here but I'm still tracking Faust! He's gone into a building to get something! Ghost, you see him?
	radio_dialogue( "favela_gst_duffelbag" );		// Roger that, subject is now carrying a black duffel bag full of cash! Greedy bastard!
	radio_dialogue( "favela_cmt_intercept" );		// Well that ought to slow him down! Roach, we're keeping him from doubling back! Keep moving to intercept! Go! Go!
	
	wait 12;
	
	radio_dialogue( "favela_cmt_yourside" );		// Keep going! Faust is still headed towards your side of the favela!
	radio_dialogue( "favela_gst_pinyoudown" );		// Roach! Don't let the militia pin you down for too long! Use your flashbangs on them!
	radio_dialogue( "favela_cmt_lostsightagain" );	// I've lost sight of him again! Ghost, talk to me!
	radio_dialogue( "favela_gst_alleysbelow" );		// I'm onto him! He's trying to double back through the alleys below!
	radio_dialogue( "favela_cmt_stayonhim" );		// Roger that! Stay on him!
	
	wait 6;
	
	// Trigger
	flag_wait( "dialog_faust_through_market" );
	
	radio_dialogue( "favela_gst_cuttingthru" );		// I've got a visual on Faust! He's cutting through the market!
	radio_dialogue( "favela_cmt_headforrooftops" );	// Roger that! I'll head for the rooftops and try to cut him off on the right! He's going to have no choice but to head west!
	wait 3.0;
	radio_dialogue( "favela_gst_wayaround" );		// Bloody hell, I'm taking a lot of fire from the militia, I don't think I can track him through the market! I'm going to have to find another way around!
	
	wait 6;
	
	// Trigger
	flag_wait( "dialog_faust_in_sights" );
	
	radio_dialogue( "favela_gst_halfklick" );		// Be advised, I'm about half a klick east of the market, I can see Faust running across the rooftops on my right side!
	radio_dialogue( "favela_cmt_eyeopen" );			// Roger that! Roach! We're corraling him closer to your side of the hill! Keep an eye open for Faust! He's still moving across the rooftops!
	
	wait 8;
	
	radio_dialogue( "favela_gst_legshot" );			// Sir, I've got Faust in my sights! I can go for a clean leg shot! We can end it here!
	radio_dialogue( "favela_cmt_donotengage" );		// Negative! We can't risk it! Do not engage Faust!
	radio_dialogue( "favela_gst_rogerthat2" );		// Bollocks! Roger that!
	
	wait 12;
	
	radio_dialogue( "favela_cmt_nowheretogo" );		// Roach! Keep moving uphill! I've cut him off! He's got nowhere to go but west over the rooftops into your area!
	radio_dialogue( "favela_cmt_traphimuphere" );	// Roach! He knows the area well but we can trap him up here! Don't stop! Go! Go!
	
	wait 12;
	
	radio_dialogue( "favela_gst_jumpedfence" );		// He jumped the fence! I'm after him!!!
	radio_dialogue( "favela_cmt_goingleft" );		// Roger that! I'm going around to the left!
	
	wait 12;
	
	radio_dialogue( "favela_cmt_closertoyourpart" );// Roach! He's getting closer to your part of the favela!! Keep moving! Go! Go!
}

final_bend_dialog()
{
	level endon( "ending_sequence_dialog" );
	level endon( "player_approaching_final_stairs" );
	level endon( "stop_all_misc_dialog" );
	
	flag_wait( "player_at_final_bend" );
	
	radio_dialogue( "favela_cmt_motorcycle" );		// Ghost he's going for that motorcycle!
	
	soundEnt = getent( "nohesnot_location", "targetname" );
	soundEnt play_sound_in_space( "favela_gst_nohesnot" );	// (gunshots, explosion) No he's not.
	
	radio_dialogue( "favela_cmt_dontshoothim" );	// Nice! He's breaking to the right again! Roach, if you see him, don't shoot him! I need him unharmed!
	
	wait 0.3;
	
	radio_dialogue( "favela_cmt_onthemove" );		// Roach! He's on the move and headed your way! Go! Go!
	
	wait 4.0;
	
	radio_dialogue( "favela_cmt_anotherfence" );	// Roach! He's jumped another fence and he's still headed towards your end of the favela! Keep moving up! Go! Go!
	
	wait 4.0;
	
	radio_dialogue( "favela_cmt_corraling" );		// Keep corraling him up the hill! We'll cut him off at the top!
	
	wait 10;
	
	radio_dialogue( "favela_gst_whereishe" );		// Where is he where is he?
	wait 0.2;
	radio_dialogue( "favela_cmt_slidingrooftops" );	// Got a visual! He's over there, sliding down the tin rooftops!
	wait 0.2;
	radio_dialogue( "favela_gst_anotherlegshot" );	// I've got another clear leg shot!
	wait 0.2;
	radio_dialogue( "favela_cmt_carryhimback" );	// Negative! Not unless you want to carry him back out with all this militia breathing down your neck! I need him unharmed!
}

final_staircase_dialog()
{
	flag_wait( "player_approaching_final_stairs" );
	
	if ( flag( "ending_sequence_dialog" ) )
		return;
	level endon( "ending_sequence_dialog" );
	
	// Ghost, I'm going far right!
	radio_dialogue( "favela_cmt_farright" );
	
	// ( Ghost - Radio ) Roger that.
	radio_dialogue( "favela_gst_rogerthat" );
}

ending_sequence()
{
	trigger_wait( "ending_sequence", "targetname" );
	
	thread ending_sequence_dialog();
	
	thread battlechatter_off( "allies" );
	thread battlechatter_off( "axis" );
	
	node = getent( "ending_node", "targetname" );
	
	soap = spawn_targetname( "ending_soap_spawner", true );
	soap.animname = "mactavish";
	soap set_ignoreme( true );
	soap set_ignoreall( true );
	soap thread magic_bullet_shield();
	
	faust = spawn_targetname( "ending_faust_spawner", true );
	faust.animname = "faust";
	faust set_ignoreme( true );
	faust set_ignoreall( true );
	faust thread magic_bullet_shield();
	faust thread faust_mission_fail();
	faust thread objective_on_faust();
	
	car = getent( "ending_car", "targetname" );
	car useanimtree( level.scr_animtree[ "car" ] );
	car.animname = "car";
	
	// do first frame animation to set it up
	guys[ 0 ] = soap;
	guys[ 1 ] = faust;
	guys[ 2 ] = car;
	node anim_first_frame( guys, "ending_takedown" );
	
	// wait for the player to be in the area and to look at the sequence, or timeout
	flag_wait( "player_in_ending_area" );
	faust waittill_player_lookat( 0.8, undefined, undefined, 7.0 );
	
	// don't slomo the "no he's not" dialog
	SoundSetTimeScaleFactor( "Music", 0 );
	SoundSetTimeScaleFactor( "Menu", 0 );
	SoundSetTimeScaleFactor( "Bulletimpact", 0 );
	SoundSetTimeScaleFactor( "Voice", 0 );
	SoundSetTimeScaleFactor( "effects2", 0 );
	SoundSetTimeScaleFactor( "Mission", 0 );
	SoundSetTimeScaleFactor( "Announcer", 0 );
	SoundSetTimeScaleFactor( "local", 0 );
	SoundSetTimeScaleFactor( "physics", 0 );
	SoundSetTimeScaleFactor( "ambient", 0 );
	SoundSetTimeScaleFactor( "auto", 0 );
	SoundSetTimeScaleFactor( "Shellshock", 0 );
	
	//flag_wait( "ending_sequence_ready" );
	flag_set( "ending_sequence_started" );
	
	thread ending_sequence_ghost();
	
	// ending sequence happens
	
	flag_clear( "faust_music" );
	flag_clear( "favela_music" );
	thread music_stop( 1.0 );
	level.player thread play_sound_on_entity( "favela_moneyrun_endfall" );
	
	node thread anim_single( guys, "ending_takedown" );
	delayThread( 13.0, ::fade_out_level );
	
	wait 5.2;
	
	assert( isdefined( level.ghost ) );
	level.ghost anim_single_solo( level.ghost, "favela_gst_sendchopper" );
	wait 0.8;
	level.ghost anim_single_solo( level.ghost, "favela_gst_skiesareclear" );
	wait 1.0;
	level.ghost thread anim_single_solo( level.ghost, "favela_gst_onourown" );
	wait 4;
	nextmission();
}

fade_out_level()
{
	fadeInTime = 1.0;
	
	setSavedDvar( "compass", 0 );
	setSavedDvar( "hud_showStance", 0 );
	
	overlay = newHudElem();
	overlay.x = 0;
	overlay.y = 0;
	overlay setshader( "black", 640, 480 );
	overlay.alignX = "left";
	overlay.alignY = "top";
	overlay.horzAlign = "fullscreen";
	overlay.vertAlign = "fullscreen";
	
	overlay.alpha = 0;
	overlay fadeOverTime( fadeInTime );
	overlay.alpha = 1;
	
	wait fadeInTime;
	
	level.player freezeControls( true );
	enablePlayerWeapons( false );
}

ending_sequence_ghost()
{
	level.ghost = spawn_targetname( "ending_ghost_spawner", true );
	level.ghost.animname = "ghost";
	level.ghost set_ignoreme( true );
	level.ghost set_ignoreall( true );
	level.ghost thread magic_bullet_shield();
}

ending_sequence_dialog()
{
	flag_set( "ending_sequence_dialog" );
	
	if ( !flag( "ending_sequence_started" ) )
	{
		// ( Ghost - Radio ) He's gonna get away!!
		radio_dialogue( "favela_gst_getaway" );
	}
	
	//flag_set( "ending_sequence_ready" );
	flag_wait( "ending_sequence_started" );
	
	// No he's not.
	radio_dialogue( "favela_cmt_nohesnot" );
	
	flag_set( "start_final_dialog" );
}

ending_sequence_slowmo()
{	
	wait 0.15;
	
	if ( !player_looking_at( self.origin, undefined, true ) )
		return;
	
	slomoLerpTime_in = 0.5;
	slomoLerpTime_out = 0.65;
	slomobreachplayerspeed = 0.1;
	slomoSpeed = 0.2;
	slomoDuration = 2.0;
	
	level.player thread play_sound_on_entity( "slomo_whoosh" );
	
	slowmo_start();
	slowmo_setspeed_slow( slomoSpeed );
	slowmo_setlerptime_in( slomoLerpTime_in );
	slowmo_lerp_in();
	level.player SetMoveSpeedScale( slomobreachplayerspeed );
	
	wait slomoDuration * slomoSpeed;
	
	level.player thread play_sound_on_entity( "slomo_whoosh" );
	
	slowmo_setlerptime_out( slomoLerpTime_out );
	slowmo_lerp_out();
	slowmo_end();
	level.player SetMoveSpeedScale( 1.0 );
	
	wait 1;
	
	objective_State( 2, "done" );
}