#include "scripts/common.txt"
#include "scripts/BP_common.txt"
#include "scripts/BP_MapCommon.txt"

//Tag IDs
//1000: Longhunter Human
//1001: Exit Door
//2007: Checkpoint
//1002: Longhunter Robot
//1003: Point 1
//1004: Point 2
//1005: Point 3
//1006: Point 4
//1007: Point 5
//1008: Point 6
//1009: Point 7
//1010: Point 8
//1011: Point 9
//1012: Point 10
//1013: Point 11
//1014: Point 12
//1015: 
//1020: Longhunter Head
//1021: Back Door
//1022: Back Door Blocker (deleted)
//1023: Back Trap Doors
//1024: Back Door Trap Enabled
//1025: Back Trap Doors Enemies
//1026: Longhunter Head Model
//1027: Flute Item Pickup
//1028: 


kActor@ boss;
kActor@ bossRobot;
kActor@ head;
kActor@ headModel;
kActor@ exitDoor;
kActor@ backDoor;
kActor@ flute;
//kActor@ backDoorBlocker;
kActor@ backDoorTrap;
array<kActor@> bossWarpPoints;
bool wavMusicActive;
float bossWarpTime;
float bossMaxWarpTime;
int bossRobotHealth;
float headMoveTime;
float headAttackTime;
float tauntTime;
bool bossStartedDead;
bool bossRobotStartedDead;
bool headStartedDead;
kActor@ headTarget;
const bool useCustomMusic = true;
const kVec3 headTargetOffset(0.0f, 0.0f, 0.0f);
//------------------------------------------------------------------------------------------------------------------------
// Start Map
//------------------------------------------------------------------------------------------------------------------------
$script 0
{
	StartMap();
	wavMusicActive = false;
	bossStartedDead = false;
	bossRobotStartedDead = false;
	headStartedDead = false;
	tauntTime = 0.0f;
	bossWarpTime = 0.0f;
	bossMaxWarpTime = 10.0f;
	headMoveTime = 0.0f;
	headAttackTime = 0.0f;
	bossRobotHealth = 1640;
	@flute = World.GetActorByTID(1027);
	if (GetPlayerFlute())
		flute.Flags() |= AF_NODRAW;

	@backDoor = World.GetActorByTID(1021);
	//@backDoorBlocker = World.GetActorByTID(1022);
	@backDoorTrap = World.GetActorByTID(1024);
	@exitDoor = World.GetActorByTID(1001);
	@headTarget = null;
	for (int i = 0; i < 12; i++)
	{
		bossWarpPoints.insertLast(World.GetActorByTID(1003 + i));
	}
	
	@boss = World.GetActorByTID(1000);
	@bossRobot = World.GetActorByTID(1002);
	@head = World.GetActorByTID(1020);
	@headModel = World.GetActorByTID(1026);
	if (ActorExists(headModel))
	{
		HideAI(headModel);
	}
	
	// boss.MarkPersistentBit(false);
	// bossRobot.MarkPersistentBit(false);
	
	if (ActorExists(boss))
	{
		HideAI(boss);
	}
	if (ActorExists(bossRobot))
	{
		bossRobot.ClipFlags() |= CF_DROPOFF | CF_WALKWALLS;
		HideAI(bossRobot);
	}
	if (ActorExists(head))
	{
		if (head.IsPersistentMarked())
		{
			head.Remove();
		}
		else
		{
			//head.ClipFlags() |= CF_DROPOFF | CF_WALKWALLS;
			head.Flags() |= AF_STATIONARY;
			head.Gravity() = 0.0f;
			//head.CastToAI().AIFlags() |= AIF_NOTHINK;
			HideAI(head);
			head.Flags() |= AF_NODRAW;
			
			for (int i = 0; i < 24; i++)
			{
				headModel.RenderModel().SetVisibility(i, false);	
			}
			headModel.RenderModel().SetVisibility(20, true);
			HideAI(headModel);
			
			// for (int i = 0; i < 24; i++)
			// {
				// head.RenderModel().SetVisibility(i, false);	
			// }
			// head.RenderModel().SetVisibility(20, true);
		}
	}
	
	PlayLoop.RemoveBossActor();
	
	bossStartedDead = boss.IsPersistentMarked();
	bossRobotStartedDead = bossRobot.IsPersistentMarked();
	headStartedDead = head.IsPersistentMarked();
	
	Game.CallDelayedMapScript(1, instigator, 0); //Update Map
	Game.CallDelayedMapScript(2, instigator, 0); //Boss Robot Update
	Game.CallDelayedMapScript(3, instigator, 0); //Head Update
	
	CheckBossDead();
}
//------------------------------------------------------------------------------------------------------------------------
// Update Map
//------------------------------------------------------------------------------------------------------------------------
$script 1
{
	TickMap();

	if (!GetPlayerFlute())
	{
		flute.Yaw() +=  0.017453f;
	}
	
	if (useCustomMusic)
	{
		if (wavMusicActive)
		{
			PlayWavMusic("music_lh.ksnd");
		}
	}
	
	tauntTime -= GAME_DELTA_TIME;
	
	$restart;
}
//------------------------------------------------------------------------------------------------------------------------
bool IsBossRobotActive()
{
	return (ActorExists(bossRobot) && !IsActorDead(bossRobot) && !bossRobot.IsPersistentMarked() && !IsAIDisabled(bossRobot) && !Camera.Active() && !bossRobotStartedDead);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsHeadActive()
{
	return (ActorExists(head) && !IsActorDead(head) && !head.IsPersistentMarked() && !IsAIDisabled(head) && !Camera.Active() && Player.Actor().Origin().z < -6300.0f && !headStartedDead);
}
//------------------------------------------------------------------------------------------------------------------------
// Boss Robot Update
//------------------------------------------------------------------------------------------------------------------------
$script 2
{
	//update boss robot ai
	if (IsBossRobotActive())
	{
		if (bossRobotHealth != bossRobot.Health())
		{
			if (bossRobotHealth > 750 && bossRobot.Health() <= 750)
			{
				bossMaxWarpTime = 5.0f;
			}
			bossRobotHealth = bossRobot.Health();
		}
		
		bossWarpTime += GAME_DELTA_TIME;
		if (bossWarpTime >= bossMaxWarpTime)
		{
			bossWarpTime = 0.0f;
			kActor@ pActor = Player.Actor().CastToActor();
			if (Math::RandMax(2) == 0)
			{
				kActor@ warpPoint = ArrayRandomElement(bossWarpPoints);
				bossRobot.PlaySound("sounds/shaders/campainger_spell.ksnd");
				bossRobot.RunFxEvent("Campaigner_TeleportOut");
				delay(0.5f);
				bossRobot.SetPosition(warpPoint.Origin());
				bossRobot.RunFxEvent("Campaigner_TeleportIn");
			}
			else
			{
				int closestIndex = -1;
				float closestDist = 99999.0f;
				for (int i = 0; i < int(bossWarpPoints.length()); i++)
				{
					float d = pActor.Origin().Distance(bossWarpPoints[i].Origin());
					if (d < closestDist)
					{
						closestDist = d;
						closestIndex = i;
					}
				}
				if (closestIndex != -1)
				{
					kActor@ warpPoint = bossWarpPoints[closestIndex];
					bossRobot.PlaySound("sounds/shaders/campainger_spell.ksnd");
					bossRobot.RunFxEvent("Campaigner_TeleportOut");
					delay(0.5f);
					bossRobot.SetPosition(warpPoint.Origin());
					bossRobot.RunFxEvent("Campaigner_TeleportIn");
				}
			}
		}
	}
	
	$restart;
}
//------------------------------------------------------------------------------------------------------------------------
// Head Update
//------------------------------------------------------------------------------------------------------------------------
$script 3
{
	//update boss robot ai
	if (IsHeadActive())
	{
		kActor@ pActor = Player.Actor().CastToActor();
		headMoveTime -= GAME_DELTA_TIME;
		headAttackTime -= GAME_DELTA_TIME;
		if (headModel !is null)
		{
			headModel.Origin() = head.Origin() + kVec3(0.0f, 0.0f, -1400.0f);
			headModel.SetSector(head.SectorIndex());
			FacePlayer(headModel);
			//headModel.Pitch() = (head.Origin() - pActor.Origin()).ToPitch();
		}

		if (headTarget !is null)
		{
			head.Yaw() = 0.0f;
			head.Pitch() = 0.0f;
			head.Roll() = 0.0f;
			kVec3 targetPos = headTarget.Origin() + headTargetOffset;
			targetPos.z += 600.0f;
			
			if (head.Origin().Distance(targetPos) < 100.0f)
			{
				head.Velocity() = Math::vecZero;
			}
			else
			{
				head.Velocity() = (targetPos - head.Origin()).Normalize() * 10.0f;
			}
			
			if (headAttackTime <= 0.0f)
			{
				headAttackTime = 0.1f;
				
				targetPos = headTarget.Origin() + headTargetOffset;
				float range = 500.0f;
				targetPos.x += Math::RandRange(-range, range);
				targetPos.y += Math::RandRange(-range, range);
				targetPos.z += 50.0f;
				if (Math::RandMax(20) == 0)
				{
					head.SpawnProjectile("fx/generic_247.kfx", kVec3(0.0f, 0.0f, 100.0f), targetPos, Math::Deg2Rad(45.0f));
				}
				else
				{
					int attack = Math::RandMax(3);
					switch (attack)
					{
						case 0:
						{
							head.SpawnProjectile("fx/spellcast.kfx", kVec3(0.0f, 0.0f, 100.0f), targetPos, Math::Deg2Rad(45.0f));
							break;
						}
						case 1:
						{
							head.SpawnProjectile("fx/generic_258.kfx", kVec3(0.0f, 0.0f, 100.0f), targetPos, Math::Deg2Rad(45.0f));
							break;
						}
						default:
						{
							head.SpawnProjectile("fx/enemy_plasma1.kfx", kVec3(0.0f, 0.0f, 100.0f), targetPos, Math::Deg2Rad(45.0f));
							break;
						}
					}
				}
			}
		}

		if (headMoveTime <= 0.0f)
		{
			headMoveTime = 2.0f;
			if (Math::RandMax(2) == 0)
			{
				kActor@ point = ArrayRandomElement(bossWarpPoints);
				@headTarget = @point;
			}
			else
			{
				int closestIndex = -1;
				float closestDist = 99999.0f;
				for (int i = 0; i < int(bossWarpPoints.length()); i++)
				{
					float d = pActor.Origin().Distance(bossWarpPoints[i].Origin());
					if (d < closestDist)
					{
						closestDist = d;
						closestIndex = i;
					}
				}
				if (closestIndex != -1)
				{
					kActor@ point = bossWarpPoints[closestIndex];
					@headTarget = @point;
				}
			}
		}
	}
	
	$restart;
}
//------------------------------------------------------------------------------------------------------------------------
// Spawn Head
//------------------------------------------------------------------------------------------------------------------------
$script 4
{
	if (ActorExists(head))
	{
		if (useCustomMusic)
		{
			wavMusicActive = true;
			Game.StopMusic();
		}
		delay(8.0f);
		ShowAI(head);
		PlayLoop.TagActorForBossBar(head);
		head.SetPosition(kVec3(190.0f, -1467.0f, -6795.0f));
		head.ImpactType() = IT_FLESH_UNDEAD;
		head.Height() = 350.0f;
		head.Radius() = 200.0f;
		//head.RenderModel().Offset() = kVec3(0.0f, 0.0f, -1000.0f);
		head.AnimState().flags |= (ANF_PAUSED|ANF_NOINTERRUPT);
		head.ClipFlags() |= CF_IGNOREBLOCKERS | CF_DROPOFF | CF_NOCLIPSTATICS | CF_NOCLIPACTORS | CF_WALKWALLS | CF_NOSTEPDOWN | CF_NOSLOPESTEP | CF_NOCOLLIDEFUNC | CF_NOFLOORADJUST | CF_NOCEILINGADJUST;
		headModel.Scale() = kVec3(10.0f, 10.0f, 10.0f);
		headModel.ClipFlags() |= CF_IGNOREBLOCKERS | CF_DROPOFF | CF_NOCLIPSTATICS | CF_NOCLIPACTORS | CF_WALKWALLS | CF_NOSTEPDOWN | CF_NOSLOPESTEP | CF_NOCOLLIDEFUNC | CF_NOFLOORADJUST | CF_NOCEILINGADJUST;
		headModel.Flags() |= AF_ALWAYSACTIVE;
		if (headModel !is null)
		{
			ShowAI(headModel);
			// headModel.SetTarget(Player.Actor().CastToActor());
			// headModel.SetHeadTrackTarget(Player.Actor().CastToActor());
			headModel.AnimState().flags |= (ANF_PAUSED|ANF_NOINTERRUPT);
		}
		
		//head.AnimState().Pause();
		
		// if (ActorExists(bossRobot))
		// {
			// head.SetPosition(kVec3(bossRobot.Origin().x, bossRobot.Origin().y, bossRobot.Origin().z + 100.0f));
		// }
	}
}
//------------------------------------------------------------------------------------------------------------------------
void CheckBossDead()
{
	kActor@ instigator = Player.Actor().CastToActor();
	if (head.IsPersistentMarked() || headStartedDead)
	{
		//open door
		if (!exitDoor.IsPersistentMarked())
		{
			World.TriggerActorsByTID(instigator, 1001); //exit door
		}
		
		if (!backDoor.IsPersistentMarked())
		{
			World.TriggerActorsByTID(instigator, backDoor.TID()); //back door
			//World.TriggerActorsByTID(instigator, backDoorBlocker.TID()); //back door blocker
		}
	}
	else if (!boss.IsPersistentMarked() && !bossRobot.IsPersistentMarked())
	{
		//close door
		if (exitDoor.IsPersistentMarked())
		{
			World.TriggerActorsByTID(instigator, 1001); //exit door
		}
	}
	else if (boss.IsPersistentMarked() && !bossRobot.IsPersistentMarked())
	{
		//open door
		if (!exitDoor.IsPersistentMarked())
		{
			World.TriggerActorsByTID(instigator, 1001); //exit door
		}
	}
	else if (bossRobot.IsPersistentMarked() && !head.IsPersistentMarked())
	{
		//close door
		if (exitDoor.IsPersistentMarked())
		{
			World.TriggerActorsByTID(instigator, 1001); //exit door
		}
		Game.CallDelayedMapScript(4, instigator, 0); //Spawn Head
	}
}
//------------------------------------------------------------------------------------------------------------------------
// On Player Death
//------------------------------------------------------------------------------------------------------------------------
$script 9997
{
	if (IsBossRobotActive())
	{
		bossRobot.PlaySound("sounds/shaders/longhunter_pdeath_taunt.ksnd");
	}
	else if (IsHeadActive())
	{
		head.PlaySound("sounds/shaders/longhunter_pdeath_taunt.ksnd");
	}
}
//------------------------------------------------------------------------------------------------------------------------
// On Player Damage
//------------------------------------------------------------------------------------------------------------------------
$script 9998
{
	if (tauntTime > 0.0f)
	{
		return;
	}
	tauntTime = 3.0f;
	
	if (IsBossRobotActive())
	{
		if (Math::RandMax(4) == 0)
		{
			bossRobot.PlaySound("sounds/shaders/longhunter_pdamage_taunt" + (Math::RandMax(3) + 1) + ".ksnd");
		}
	}
	else if (IsHeadActive())
	{
		if (Math::RandMax(4) == 0)
		{
			head.PlaySound("sounds/shaders/longhunter_pdamage_taunt" + (Math::RandMax(3) + 1) + ".ksnd");
		}
	}
}
//------------------------------------------------------------------------------------------------------------------------
// On Enemy killed
//------------------------------------------------------------------------------------------------------------------------
$script 9999
{
	if (instigator.TID() == 1000)
	{
		PlayLoop.RemoveBossActor();
		CheckBossDead();
	}
	else if (instigator.TID() == 1002)
	{
		PlayLoop.RemoveBossActor();
		CheckBossDead();
		bossRobot.SpawnFx("fx/tech_explosion.kfx", Math::vecZero);
		delay(0.5f);
		bossRobot.RenderModel().SetVisibility(20, false);
		delay(7.5f);
		bossRobot.PlaySound("sounds/shaders/longhunter_head_intro_taunt.ksnd");
		
	}
	else if (instigator.TID() == 1020)
	{
		PlayLoop.RemoveBossActor();
		CheckBossDead();
		head.SpawnFx("fx/tech_explosion.kfx", kVec3(0.0f, 0.0f, 0.0f));
		head.Remove();
		headModel.Remove();
		if (useCustomMusic)
		{
			wavMusicActive = false;
			StopWavMusic();
		}
		Game.PlayMusic("music/track13.ogg", true);
	}
}
//------------------------------------------------------------------------------------------------------------------------
// Try show Normal Boss
//------------------------------------------------------------------------------------------------------------------------
$script 10000
{
	if (!boss.IsPersistentMarked() && IsAIDisabled(boss))
	{	
		Camera.StartCinematic(CMF_NO_LETTERBOX | CMF_LOCK_PLAYER | CMF_UNLOCK_PLAYER_ON_FINISH);
		Camera.SetLookAtActor(boss);
		Camera.ClearFinalView();
		Camera.ClearViewTracks();
		Camera.origin = kVec3(0.0f, -2341.0f, -7183.0f);
		delay(0.3f);
		ShowAI(boss);
		boss.AnimState().Set(anim_longHunterTaunt, 4.0f, ANF_ROOTMOTION);
		instigator.SetPosition(kVec3(0.0f, -1843.0f, -7296.0f));
		instigator.Yaw() = Math::Deg2Rad(180.0f);
		delay(0.1f);
		boss.AnimState().Set(anim_longHunterTaunt, 4.0f, ANF_ROOTMOTION | ANF_BLEND);
		boss.PlaySound("sounds/shaders/longhunter_taunt_1.ksnd");
		delay(3.0f);
		Camera.StopCinematic();
		delay(0.3f);
		ShowAI(boss);
		PlayLoop.TagActorForBossBar(boss);
	}
}
//------------------------------------------------------------------------------------------------------------------------
// Try show Robot Boss
//------------------------------------------------------------------------------------------------------------------------
$script 10001
{
	//Sys.Print("boss " + boss.IsPersistentMarked() + " bossStartedDead " + bossStartedDead + " bossRobot " + bossRobot.IsPersistentMarked() + " isDisabled " + IsAIDisabled(bossRobot) + " bossRobotStartedDead " + bossRobotStartedDead);
	//if (!bossRobot.IsPersistentMarked() && IsAIDisabled(bossRobot))
	if ((boss.IsPersistentMarked() || bossStartedDead) && !bossRobot.IsPersistentMarked() && !bossRobotStartedDead && IsAIDisabled(bossRobot))
	{
		//close door
		if (ActorExists(boss))
		{
			boss.Remove();
		}
		Game.StopMusic();
		Camera.StartCinematic(CMF_NO_LETTERBOX | CMF_LOCK_PLAYER | CMF_UNLOCK_PLAYER_ON_FINISH);
		Camera.ClearFinalView();
		Camera.ClearViewTracks();
		Camera.SetLookAtActor(exitDoor);
		Camera.origin = kVec3(0.0f, -2439.0f, -7200.0f);
		delay(0.5f);
		if (exitDoor.IsPersistentMarked())
		{
			World.TriggerActorsByTID(instigator, 1001); //exit door
		}
		delay(2.5f);
		instigator.PlaySound("sounds/shaders/lhrobot_intro.ksnd");
		Camera.SetLookAtActor(bossRobot);
		Camera.origin = kVec3(0.0f, -1927.0f, -7211.0f);
		instigator.SetPosition(kVec3(0.0f, -2462.0f, -7296.0f));
		instigator.Yaw() = Math::Deg2Rad(0.0f);
		ShowAI(bossRobot);
		bossRobot.AnimState().Set(anim_aiStanding, 4.0f, ANF_ROOTMOTION);
		bossRobot.AnimState().SetLastFrame();
		delay(3.0f);
		bossRobot.AnimState().Blend(anim_longHunterTaunt, 4.0f, 8.0f, ANF_ROOTMOTION);
		bossRobot.PlaySound("sounds/shaders/longhunter_intro_taunt.ksnd");
		delay(3.0f);
		Camera.StopCinematic();
		if (Game.GetDifficulty() < DIFFICULTY_HARD)
		{
			bossRobot.Health() = Math::Max(GetLHBossHealth(), 1);
		}
		delay(0.3f);
		PlayLoop.TagActorForBossBar(bossRobot);
		delay(3.0f);
		if (useCustomMusic)
		{
			wavMusicActive = true;
		}
		else
		{
			Game.PlayMusic("music/track04.ogg", true);
		}
	}
}
//------------------------------------------------------------------------------------------------------------------------
// Try set back door area trap
//------------------------------------------------------------------------------------------------------------------------
$script 10002
{
	if (!backDoorTrap.IsPersistentMarked())
	{
		backDoorTrap.MarkPersistentBit(false);
		World.TriggerActorsByTID(instigator, 1023); //Back Trap Doors
		World.TriggerActorsByTID(instigator, 1025); //Back Trap Doors enemies	
	}
}
//------------------------------------------------------------------------------------------------------------------------
// In Area - Hummer Exit
//------------------------------------------------------------------------------------------------------------------------
$script 10003
{
    if ((instigator.Flags() & AF_ENTEREDAREAEVENT) != 0)
        return;

	PlayLoop.StartWarp(Player.Actor().CastToActor(), 1004, 106); //skip driving
	
	//old exit is 1004, 106
	//start cinematic and drive away
	//then show robot standing with no head
}
//------------------------------------------------------------------------------------------------------------------------
// In Area - Flute Item Pickup
//------------------------------------------------------------------------------------------------------------------------
$script 10004
{
	if ((instigator.Flags() & AF_ENTEREDAREAEVENT) == 0)
	{
		if (!GetPlayerFlute())
		{
			SetPlayerFlute(true);
			Game.PlaySound("sounds/shaders/health_pickup_1.ksnd");
			Game.PrintLine(LTKey(34), 0); // Flute
			flute.Flags() |= AF_NODRAW;
		}
	}
}
//------------------------------------------------------------------------------------------------------------------------
