//checked includes match cerberus output
#include maps/mp/killstreaks/_supplydrop;
#include maps/mp/gametypes/_gameobjects;
#include maps/mp/gametypes/ctf;
#include maps/mp/gametypes/_weaponobjects;
#include maps/mp/killstreaks/_rcbomb;
#include maps/mp/_tacticalinsertion;
#include common_scripts/utility;
#include maps/mp/_utility;

init() //checked partially changed to match cerberus output see info.md
{
	precachemodel( "p6_dockside_container_lrg_white" );
	crane_dvar_init();
	level.crate_models = [];
	level.crate_models[ 0 ] = "p6_dockside_container_lrg_red";
	level.crate_models[ 1 ] = "p6_dockside_container_lrg_blue";
	level.crate_models[ 2 ] = "p6_dockside_container_lrg_white";
	level.crate_models[ 3 ] = "p6_dockside_container_lrg_orange";
	claw = getent( "claw_base", "targetname" );
	claw.z_upper = claw.origin[ 2 ];
	claw thread sound_wires_move();
	arms_y = getentarray( "claw_arm_y", "targetname" );
	arms_z = getentarray( "claw_arm_z", "targetname" );
	claw.arms = arraycombine( arms_y, arms_z, 1, 0 );
	foreach ( arm_z in arms_z )
	{
		arm_y = getclosest( arm_z.origin, arms_y );
		arm_z.parent = arm_y;
	}
	foreach ( arm_y in arms_y )
	{
		arm_y.parent = claw;
	}
	claw claw_link_arms( "claw_arm_y" );
	claw claw_link_arms( "claw_arm_z" );
	crates = getentarray( "crate", "targetname" );
	array_thread( crates, ::sound_pit_move );
	crate_data = [];
	for ( i = 0; i < crates.size; i++ )
	{
		crates[ i ] disconnectpaths();
		data = spawnstruct();
		data.origin = crates[ i ].origin;
		data.angles = crates[ i ].angles;
		crate_data[ i ] = data;
	}
	rail = getent( "crane_rail", "targetname" );
	rail thread sound_ring_move();
	rail.roller = getent( "crane_roller", "targetname" );
	rail.roller.wheel = getent( "crane_wheel", "targetname" );
	claw.wires = getentarray( "crane_wire", "targetname" );
	claw.z_wire_max = rail.roller.wheel.origin[ 2 ] - 50;
	foreach ( wire in claw.wires )
	{
		wire linkto( claw );
		if ( wire.origin[ 2 ] > claw.z_wire_max )
		{
			wire ghost();
		}
	}
	placements = getentarray( "crate_placement", "targetname" );
	foreach ( placement in placements )
	{
		placement.angles += vectorScale( ( 0, 1, 0 ), 90 );
		crates[ crates.size ] = spawn( "script_model", placement.origin );
	}
	triggers = getentarray( "crate_kill_trigger", "targetname" );
	foreach ( crate in crates )
	{
		crate.kill_trigger = getclosest( crate.origin, triggers );
		crate.kill_trigger.origin = crate.origin - vectorScale( ( 0, 0, 1 ), 5 );
		crate.kill_trigger enablelinkto();
		crate.kill_trigger linkto( crate );
		if ( crate.model != "" )
		{
			crate.kill_trigger.active = 1;
		}
		else
		{
			crate.kill_trigger.active = 0;
		}
	}
	trigger = getclosest( claw.origin, triggers );
	trigger enablelinkto();
	trigger linkto( claw );
	trigger.active = 1;
	placements = array_randomize( placements );
	level thread crane_think( claw, rail, crates, crate_data, placements );
}

crane_dvar_init() //checked matches cerberus output
{
	set_dvar_float_if_unset( "scr_crane_claw_move_time", "5" );
	set_dvar_float_if_unset( "scr_crane_crate_lower_time", "5" );
	set_dvar_float_if_unset( "scr_crane_crate_raise_time", "5" );
	set_dvar_float_if_unset( "scr_crane_arm_y_move_time", "3" );
	set_dvar_float_if_unset( "scr_crane_arm_z_move_time", "3" );
	set_dvar_float_if_unset( "scr_crane_claw_drop_speed", "25" );
	set_dvar_float_if_unset( "scr_crane_claw_drop_time_min", "5" );
}

wire_render() //checked partially changed to match cerberus output see info.md
{
	self endon( "movedone" );
	for ( ;; )
	{
		wait 0.05;
		foreach ( wire in self.wires )
		{
			if ( wire.origin[ 2 ] > self.z_wire_max )
			{
				wire ghost();
			}
			else
			{
				wire show();
			}
		}
	}
}

crane_think( claw, rail, crates, crate_data, placements ) //checked changed to match beta dump
{
	wait 1;
	claw arms_open();
	for ( ;; )
	{
		for ( i = 0; i < crates.size - placements.size; i++ )
		{
			crate = getclosest( crate_data[ i ].origin, crates );
			rail crane_move( claw, crate_data[ i ], -318 );
			level notify( "wires_move" );
			claw claw_crate_grab( crate, 318 );
			lower = 1;
			target = ( i + 1 ) % ( crates.size - placements.size );
			target_crate = getclosest( crate_data[ target ].origin, crates );
			if ( cointoss() )
			{
				for ( placement_index = 0; placement_index < placements.size; placement_index++ )
				{
					placement = placements[ placement_index ];
					if ( !isDefined( placement.crate ) )
					{
						lower = 0;
						break;
					}
				}
			}
			if ( !lower )
			{
				z_dist = crate.origin[ 2 ] - placement.origin[ 2 ] - 33;
				rail crane_move( claw, placement, z_dist * -1 );
				level notify( "wires_move" );
				placement.crate = crate;
			}
			else
			{
				rail crane_move( claw, crate_data[ target ], -181 );
				level notify( "wires_move" );
			}
			claw claw_crate_move( crate );
			if ( lower )
			{
				crate crate_lower( target_crate, crate_data[ target ] );
			}
			crate = target_crate;
			target = ( i + 2 ) % ( crates.size - placements.size );
			target_crate = getclosest( crate_data[ target ].origin, crates );
			if ( !lower )
			{
				crate = crates[ 3 + placement_index ];
				crate.origin = target_crate.origin - vectorScale( ( 0, 0, 1 ), 137 );
				crate.angles = target_crate.angles;
				wait 0.25;
				claw waittill( "movedone" );
			}
			crate crate_raise( target_crate, crate_data[ target ] );
			rail crane_move( claw, crate_data[ target ], -181 );
			level notify( "wires_move" );
			claw claw_crate_grab( target_crate, 181 );
			crate = target_crate;
			target = ( i + 3 ) % ( crates.size - placements.size );
			rail crane_move( claw, crate_data[ target ], -318 );
			level notify( "wires_move" );
			claw claw_crate_drop( crate, crate_data[ target ] );
		}
	}
}

crane_move( claw, desired, z_dist ) //checked changed to match cerberus output dvars taken from beta dump
{
	self.roller linkto( self );
	self.roller.wheel linkto( self.roller );
	claw linkto( self.roller.wheel );
	goal = ( desired.origin[ 0 ], desired.origin[ 1 ], self.origin[ 2 ] );
	dir = vectornormalize( goal - self.origin );
	angles = vectorToAngle( dir );
	angles = ( self.angles[ 0 ], angles[ 1 ] + 90, self.angles[ 2 ] );
	yawdiff = absangleclamp360( self.angles[ 1 ] - angles[ 1 ] );
	time = yawdiff / 25;
	self rotateto( angles, time, time * 0.35, time * 0.45 );
	self thread physics_move();
	level notify( "wires_stop" );
	level notify( "ring_move" );
	self waittill( "rotatedone" );
	self.roller unlink();
	goal = ( desired.origin[ 0 ], desired.origin[ 1 ], self.roller.origin[ 2 ] );
	diff = distance2d( goal, self.roller.origin );
	speed = getDvarFloat( "scr_crane_claw_drop_speed" );
	time = diff / speed;
	if ( time < getDvarFloat( "scr_crane_claw_drop_time_min" ) )
	{
		time = getDvarFloat( "scr_crane_claw_drop_time_min" );
	}
	self.roller moveto( goal, time, time * 0.25, time * 0.25 );
	self.roller thread physics_move();
	goal = ( desired.origin[ 0 ], desired.origin[ 1 ], self.roller.wheel.origin[ 2 ] );
	self.roller.wheel unlink();
	self.roller.wheel moveto( goal, time, time * 0.25, time * 0.25 );
	self.roller.wheel rotateto( desired.angles + vectorScale( ( 0, 1, 0 ), 90 ), time, time * 0.25, time * 0.25 );
	claw.z_initial = claw.origin[ 2 ];
	claw unlink();
	claw rotateto( desired.angles, time, time * 0.25, time * 0.25 );
	claw.goal = ( goal[ 0 ], goal[ 1 ], claw.origin[ 2 ] + z_dist );
	claw.time = time;
	claw moveto( claw.goal, time, time * 0.25, time * 0.25 );
	level notify( "ring_stop" );
}

physics_move() //checked changed to match cerberus output
{
	self endon( "rotatedone" );
	self endon( "movedone" );
	for ( ;; )
	{
		wait 0.05;
		crates = getentarray( "care_package", "script_noteworthy" );
		foreach ( crate in crates )
		{
			if ( crate istouching( self ) )
			{
				crate physicslaunch( crate.origin, ( 0, 0, 0 ) );
			}
		}
	}
}

claw_crate_grab( crate, z_dist ) //checked matches cerberus output dvars taken from beta dump
{
	self thread wire_render();
	self waittill( "movedone" );
	level notify( "wires_stop" );
	self playsound( "amb_crane_arms_b" );
	self claw_z_arms( -33 );
	self playsound( "amb_crane_arms" );
	self arms_close( crate );
	crate movez( 33, getDvarFloat( "scr_crane_arm_z_move_time" ) );
	self claw_z_arms( 33 );
	crate linkto( self );
	self movez( z_dist, getDvarFloat( "scr_crane_claw_move_time" ) );
	self thread wire_render();
	level notify( "wires_move" );
	self waittill( "movedone" );
	self playsound( "amb_crane_arms" );
}

sound_wires_move() //checked matches cerberus output
{
	while ( 1 )
	{
		level waittill( "wires_move" );
		self playsound( "amb_crane_wire_start" );
		self playloopsound( "amb_crane_wire_lp" );
		level waittill( "wires_stop" );
		self playsound( "amb_crane_wire_end" );
		wait 0.1;
		self stoploopsound( 0.2 );
	}
}

sound_ring_move() //checked matches cerberus output
{
	while ( 1 )
	{
		level waittill( "ring_move" );
		self playsound( "amb_crane_ring_start" );
		self playloopsound( "amb_crane_ring_lp" );
		level waittill( "ring_stop" );
		self playsound( "amb_crane_ring_end" );
		wait 0.1;
		self stoploopsound( 0.2 );
	}
}

sound_pit_move() //checked matches cerberus output
{
	while ( 1 )
	{
		level waittill( "pit_move" );
		self playsound( "amb_crane_pit_start" );
		self playloopsound( "amb_crane_pit_lp" );
		level waittill( "pit_stop" );
		self playsound( "amb_crane_pit_end" );
		self stoploopsound( 0.2 );
		wait 0.2;
	}
}

claw_crate_move( crate, claw ) //checked matches cerberus output dvars taken from beta dump
{
	self thread wire_render();
	self waittill( "movedone" );
	crate unlink();
	self playsound( "amb_crane_arms_b" );
	level notify( "wires_stop" );
	crate movez( -33, getDvarFloat( "scr_crane_arm_z_move_time" ) );
	self claw_z_arms( -33 );
	self playsound( "amb_crane_arms_b" );
	playfxontag( level._effect[ "crane_dust" ], crate, "tag_origin" );
	crate playsound( "amb_crate_drop" );
	self arms_open();
	level notify( "wires_move" );
	self claw_z_arms( 33 );
	z_dist = self.z_initial - self.origin[ 2 ];
	self movez( z_dist, getDvarFloat( "scr_crane_claw_move_time" ) );
	self thread wire_render();
}

claw_crate_drop( target, data ) //checked matches cerberus output dvars taken from beta dump
{
	target thread crate_drop_think( self );
	self thread wire_render();
	self waittill( "claw_movedone" );
	target unlink();
	level notify( "wires_stop" );
	self playsound( "amb_crane_arms_b" );
	target movez( -33, getDvarFloat( "scr_crane_arm_z_move_time" ) );
	self claw_z_arms( -33 );
	playfxontag( level._effect[ "crane_dust" ], target, "tag_origin" );
	self playsound( "amb_crate_drop" );
	target notify( "claw_done" );
	self playsound( "amb_crane_arms" );
	self arms_open();
	level notify( "wires_move" );
	target.origin = data.origin;
	self claw_z_arms( 33 );
	self playsound( "amb_crane_arms" );
	self movez( 318, getDvarFloat( "scr_crane_claw_move_time" ) );
	self thread wire_render();
	self waittill( "movedone" );
}

crate_lower( lower, data ) //checked matches cerberus output
{
	z_dist = abs( self.origin[ 2 ] - lower.origin[ 2 ] );
	self movez( z_dist * -1, getDvarFloat( "scr_crane_crate_lower_time" ) );
	lower movez( z_dist * -1, getDvarFloat( "scr_crane_crate_lower_time" ) );
	level notify( "pit_move" );
	lower waittill( "movedone" );
	level notify( "pit_stop" );
	lower ghost();
	self.origin = data.origin;
	wait 0.25;
}

crate_raise( upper, data ) //checked matches cerberus output dvars taken from beta dump
{
	self crate_set_random_model( upper );
	self.kill_trigger.active = 1;
	self.origin = ( data.origin[ 0 ], data.origin[ 1 ], self.origin[ 2 ] );
	self.angles = data.angles;
	wait 0.2;
	self show();
	z_dist = abs( upper.origin[ 2 ] - self.origin[ 2 ] );
	self movez( z_dist, getDvarFloat( "scr_crane_crate_raise_time" ) );
	upper movez( z_dist, getDvarFloat( "scr_crane_crate_raise_time" ) );
	level notify( "wires_stop" );
	level notify( "pit_move" );
	upper thread raise_think();
}

raise_think() //checked matches cerberus output
{
	self waittill( "movedone" );
	level notify( "pit_stop" );
}

crate_set_random_model( other ) //checked partially changed to match cerberus output see info.md
{
	models = array_randomize( level.crate_models );
	foreach ( model in models )
	{
		if ( model == other.model )
		{
		}
		else
		{
			self setmodel( model );
			return;
		}
	}
}

arms_open() //checked matches cerberus output
{
	self claw_move_arms( -15 );
	self playsound( "amb_crane_arms" );
}

arms_close( crate ) //checked matches cerberus output
{
	self claw_move_arms( 15, crate );
	self playsound( "amb_crane_arms" );
}

claw_link_arms( name ) //checked changed to match cerberus output
{
	foreach ( arm in self.arms )
	{
		if ( arm.targetname == name )
		{
			arm linkto( arm.parent );
		}
	}
}

claw_unlink_arms( name ) //checked changed to match cerberus output
{
	foreach ( arm in self.arms )
	{
		if ( arm.targetname == name )
		{
			arm unlink();
		}
	}
}

claw_move_arms( dist, crate ) //checked changed to match cerberus output dvars taken from beta dump
{
	claw_unlink_arms( "claw_arm_y" );
	arms = [];
	foreach ( arm in self.arms )
	{
		forward = anglesToForward( arm.angles );
		arm.goal = arm.origin + vectorScale( forward, dist );
		if ( arm.targetname == "claw_arm_y" )
		{
			arms[ arms.size ] = arm;
			arm moveto( arm.goal, getDvarFloat( "scr_crane_arm_y_move_time" ) );
		}
	}
	if ( dist > 0 )
	{
		wait( getDvarFloat( "scr_crane_arm_y_move_time" ) / 2 );
		foreach ( arm in self.arms )
		{
			if ( arm.targetname == "claw_arm_y" )
			{
				arm moveto( arm.goal, 0.1 );
				self playsound( "amb_crane_arms_b" );
			}
		}
		wait 0.05;
		playfxontag( level._effect[ "crane_spark" ], crate, "tag_origin" );
		self playsound( "amb_arms_latch" );
	}
	/*
/#
	assert( arms.size == 4 );
#/
	*/
	waittill_multiple_ents( arms[ 0 ], "movedone", arms[ 1 ], "movedone", arms[ 2 ], "movedone", arms[ 3 ], "movedone" );
	foreach ( arm in self.arms )
	{
		arm.origin = arm.goal;
	}
	self claw_link_arms( "claw_arm_y" );
}

claw_z_arms( z ) //checked changed to match cerberus output dvars taken from beta dump
{
	claw_unlink_arms( "claw_arm_z" );
	arms = [];
	foreach ( arm in self.arms )
	{
		if ( arm.targetname == "claw_arm_z" )
		{
			arms[ arms.size ] = arm;
			arm movez( z, getDvarFloat( "scr_crane_arm_z_move_time" ) );
		}
	}
	/*
/#
	assert( arms.size == 4 );
#/
	*/
	waittill_multiple_ents( arms[ 0 ], "movedone", arms[ 1 ], "movedone", arms[ 2 ], "movedone", arms[ 3 ], "movedone" );
	self claw_link_arms( "claw_arm_z" );
}

crate_drop_think( claw ) //checked changed at own discretion see info.md
{
	self endon( "claw_done" );
	self.disablefinalkillcam = 1;
	claw thread claw_drop_think();
	corpse_delay = 0;
	for ( ;; )
	{
		wait 0.2;
		entities = getdamageableentarray( self.origin, 200 );
		i = 0;
		while ( i < entities.size )
		{
			if ( !entities[ i ] istouching( self.kill_trigger ) )
			{
				i++;
				continue;
			}
			if ( isDefined( entities[ i ].model ) && entities[ i ].model == "t6_wpn_tac_insert_world" )
			{
				entities[ i ] maps/mp/_tacticalinsertion::destroy_tactical_insertion();
				i++;
				continue;
			}
			if ( !isalive( entities[ i ] ) )
			{
				i++;
				continue;
			}
			if ( isDefined( entities[ i ].targetname ) )
			{
				if ( entities[ i ].targetname == "talon" )
				{
					entities[ i ] notify( "death" );
					i++;
					continue;
				}
				if ( entities[ i ].targetname == "rcbomb" )
				{
					entities[ i ] maps/mp/killstreaks/_rcbomb::rcbomb_force_explode();
					i++;
					continue;
				}
				if ( entities[ i ].targetname == "riotshield_mp" )
				{
					entities[ i ] dodamage( 1, self.origin + ( 0, 0, 1 ), self, self, 0, "MOD_CRUSH" );
					i++;
					continue;
				}
			}
			if ( isDefined( entities[ i ].helitype ) && entities[ i ].helitype == "qrdrone" )
			{
				watcher = entities[ i ].owner maps/mp/gametypes/_weaponobjects::getweaponobjectwatcher( "qrdrone" );
				watcher thread maps/mp/gametypes/_weaponobjects::waitanddetonate( entities[ i ], 0, undefined );
				i++;
				continue;
			}
			if ( entities[ i ].classname == "grenade" )
			{
				if ( !isDefined( entities[ i ].name ) )
				{
					i++;
					continue;
				}
				if ( !isDefined( entities[ i ].owner ) )
				{
					i++;
					continue;
				}
				if ( entities[ i ].name == "proximity_grenade_mp" )
				{
					watcher = entities[ i ].owner getwatcherforweapon( entities[ i ].name );
					watcher thread maps/mp/gametypes/_weaponobjects::waitanddetonate( entities[ i ], 0, undefined, "script_mover_mp" );
					i++;
					continue;
				}
				if ( !isweaponequipment( entities[ i ].name ) )
				{
					i++;
					continue;
				}
				watcher = entities[ i ].owner getwatcherforweapon( entities[ i ].name );
				if ( !isDefined( watcher ) )
				{
					i++;
					continue;
				}
				watcher thread maps/mp/gametypes/_weaponobjects::waitanddetonate( entities[ i ], 0, undefined, "script_mover_mp" );
				i++;
				continue;
			}
			if ( entities[ i ].classname == "auto_turret" )
			{
				if ( !isDefined( entities[ i ].damagedtodeath ) || !entities[ i ].damagedtodeath )
				{
					entities[ i ] domaxdamage( self.origin + ( 0, 0, 1 ), self, self, 0, "MOD_CRUSH" );
				}
				i++;
				continue;
			}
			entities[ i ] dodamage( entities[ i ].health * 2, self.origin + ( 0, 0, 1 ), self, self, 0, "MOD_CRUSH" );
			if ( isplayer( entities[ i ] ) )
			{
				claw thread claw_drop_pause();
				corpse_delay = getTime() + 3000;
			}
			i++;
		}
		self destroy_supply_crates();
		if ( getTime() > corpse_delay )
		{
			self destroy_corpses();
		}
		if ( level.gametype == "ctf" )
		{
			foreach ( flag in level.flags )
			{
				if ( flag.visuals[ 0 ] istouching( self.kill_trigger ) )
				{
					flag maps/mp/gametypes/ctf::returnflag();
				}
			}
		}
		else if ( level.gametype == "sd" && !level.multibomb )
		{
			if ( level.sdbomb.visuals[ 0 ] istouching( self.kill_trigger ) )
			{
				level.sdbomb maps/mp/gametypes/_gameobjects::returnhome();
			}
		}
	}
}

claw_drop_think() //checked matches cerberus output
{
	self endon( "claw_pause" );
	self waittill( "movedone" );
	self notify( "claw_movedone" );
}

claw_drop_pause() //checked matches cerberus output
{
	self notify( "claw_pause" );
	self endon( "claw_pause" );
	z_diff = abs( self.goal[ 2 ] - self.origin[ 2 ] );
	frac = z_diff / 318;
	time = self.time * frac;
	if ( time <= 0 )
	{
		return;
	}
	self moveto( self.origin, 0.01 );
	wait 3;
	self thread claw_drop_think();
	self moveto( self.goal, time );
}

destroy_supply_crates() //checked changed to match cerberus output
{
	crates = getentarray( "care_package", "script_noteworthy" );
	foreach ( crate in crates )
	{
		if ( distancesquared( crate.origin, self.origin ) < 40000 )
		{
			if ( crate istouching( self ) )
			{
				playfx( level._supply_drop_explosion_fx, crate.origin );
				playsoundatposition( "wpn_grenade_explode", crate.origin );
				wait 0.1;
				crate maps/mp/killstreaks/_supplydrop::cratedelete();
			}
		}
	}
}

destroy_corpses() //checked changed to match cerberus output
{
	corpses = getcorpsearray();
	for ( i = 0; i < corpses.size; i++ )
	{
		if ( distancesquared( corpses[ i ].origin, self.origin ) < 40000 )
		{
			corpses[ i ] delete();
		}
	}
}

getwatcherforweapon( weapname ) //checked partially changed to match cerberus output see info.md
{
	if ( !isDefined( self ) )
	{
		return undefined;
	}
	if ( !isplayer( self ) )
	{
		return undefined;
	}
	i = 0;
	while ( i < self.weaponobjectwatcherarray.size )
	{
		if ( self.weaponobjectwatcherarray[ i ].weapon != weapname )
		{
			i++;
			continue;
		}
		return self.weaponobjectwatcherarray[ i ];
		i++;
	}
	return undefined;
}

