#include "scripts/common.txt"
#include "scripts/bp_common.txt"
#include "scripts/map/common_level_script.txt"

array<kActor@> villagers;
array<kActor@> enemies;
array<int> enemyTarget;
array<float> enemySpawnCooldown = {0, 0, 0, 0, 0, 0};
float objDisplayTime = 5.0f;
float spawnDelayTime = 1.0f;
//------------------------------------------------------------------------------------------------------------------------
$script 0 {
	Player.GiveWeapon(TW_WEAPON_PISTOL, 200); //Refill pistol ammo
	// SetOnlyEnabledWeapon(TW_WEAPON_PISTOL); //disable all weapons except the pistol
	
	kActor @spawner1 = SpawnActor("BP_PickupSpawner", -3206, 2373, 132, 4402); //sector 4402
	spawner1.Radius() = 15.0f; //spawn time
	
	SpawnActor("BP_PickupSpawner", -2840, 2698, 102, 3873).Radius() = 15.0f;
	SpawnActor("BP_PickupSpawner", -3143, 3157, 102, 3773).Radius() = 15.0f;
	
	AddVillager("BPVillager1", -2908, 2082, 102, 4313, -59);
	AddVillager("BPVillager1", -2826, 2413, 102, 4307, -108);
	AddVillager("BPVillager1", -2886, 2943, 102, 3864, -130);
	AddVillager("BPVillager1", -3341, 3114, 102, 3858, -160);
	AddVillager("BPVillager1", -3127, 2602, 102, 3871, -104);

	Game.CallDelayedMapScript(1, instigator, 0); //Update Game
	Game.CallDelayedMapScript(5, instigator, 0); //Spawn Enemies
}
//------------------------------------------------------------------------------------------------------------------------
void AddVillager(const kStr &in name, const float x, const float y, const float z, const int sector, const int yawDegrees) {
	kActor @actor = SpawnSolidActor(name, x, y, z, sector, Math::Deg2Rad(yawDegrees));
	actor.Health() = 320;
	actor.ModelVariation() = 14 + Math::RandMax(3);
	villagers.insertLast(actor);
}
//------------------------------------------------------------------------------------------------------------------------
// Returns true if successully added enemy
//------------------------------------------------------------------------------------------------------------------------
void AddEnemy(const kStr &in name, bool inAir) {
	while (true) {
		if (enemies.length() < 20) {
			array<int> validSpawnIDs;
			for (int i = 0; i < 6; i++) {
				if (((i > 3 and inAir) or (i <= 3 and !inAir)) and enemySpawnCooldown[i] <= 0.0f) {
					validSpawnIDs.insertLast(i);
				}
			}
			int validSpawnIDsLength = validSpawnIDs.length();
			if (validSpawnIDsLength > 0) {
				int spawnID = validSpawnIDs[Math::RandMax(validSpawnIDsLength)];
				enemySpawnCooldown[spawnID] = 1.0f;
				kActor @actor;
				if (spawnID == 0) {
					@actor = SpawnActor(name, -4071, 1785, 102, 4452, Math::Deg2Rad(90)); //(in front of checkpoint)
				} else if (spawnID == 1) {
					@actor = SpawnActor(name, -3719, 2457, 100, 3893, Math::Deg2Rad(72)); //(water inner corner)
				} else if (spawnID == 2) {
					@actor = SpawnActor(name, -3387, 1482, 102, 4456, Math::Deg2Rad(0)); //(in front of checkpoint close to hut)
				} else if (spawnID == 3) {
					@actor = SpawnActor(name, -4170, 2344, 102, 4454, Math::Deg2Rad(98)); //(left of checkpoint)
				} else if (spawnID == 4) {
					@actor = SpawnActor(name, -3938, 2833, 266, 3955, Math::Deg2Rad(108)); //(over water in air)
				} else if (spawnID == 5) {
					@actor = SpawnActor(name, -3684, 1932, 374, 4459, Math::Deg2Rad(49)); //(in front of checkpoint in air)
				}
				actor.Health() = 1;
				enemyTarget.insertLast(Math::RandMax(villagers.length()));
				enemies.insertLast(actor);
				actor.CastToAI().AIFlags() |= (1 << 15);
				delay(spawnDelayTime);
				return;
			}
		}
		delay(0.1f);
	}
}
//------------------------------------------------------------------------------------------------------------------------
// Update Game
//------------------------------------------------------------------------------------------------------------------------
$script 1 {
	if (objDisplayTime > 0.0f) {
		objDisplayTime -= GAME_DELTA_TIME;
		Game.PrintLine("Defend the villagers at all costs", 0, 30);
	}
	
	int villagersLength = int(villagers.length());
	for (int i = villagersLength - 1; i >= 0; i--) {
		kActor @actor = villagers[i];
		if (actor.Health() <= 0) {
			villagers.removeAt(i);
			break;
		}
	}
	villagersLength = int(villagers.length());
	if (villagersLength == 0) {
		Game.PrintLine("Villagers Killed", 0, 360);
		Game.HaltMapScript(5);
		Game.CallDelayedMapScript(3, instigator, 3.0f); //Failed
		return;
	}
	
	int enemiesLength = int(enemies.length());
	for (int i = enemiesLength - 1; i >= 0; i--) {
		kActor @actor = enemies[i];
		if ((actor.Flags() & AF_DEAD) != 0) {
			enemies.removeAt(i);
			enemyTarget.removeAt(i);
			continue;
		} else {
			if (villagersLength > 0) {
				if (enemyTarget[i] >= villagersLength) {
					enemyTarget[i] = Math::RandMax(villagersLength);
				}
				actor.SetTarget(villagers[enemyTarget[i]]);
			}
			actor.CastToAI().AIFlags() |= (1 << 23); //damage panic (forces AI to target)
		}
	}
	int enemySpawnCooldownLength = int(enemySpawnCooldown.length());
	for (int i = 0; i < enemySpawnCooldownLength; i++) {
		enemySpawnCooldown[i] = Math::Maxf(enemySpawnCooldown[i] - GAME_DELTA_TIME, 0.0f);
	}
	
	//if not in valid area then warp to valid area
	if (!InAreaMinMax(kVec3(-4310, 1250, -200), kVec3(-2100, 4003, 2000))) {
		PlayLoop.StartFreeWarp(Player.Actor().CastToActor(), kVec3(-3124, 2753, 102), Math::Deg2Rad(46), 3878, 56);
	}
	
	UpdateGeneralGame();
	$restart;
}
//------------------------------------------------------------------------------------------------------------------------
// On Player Death
//------------------------------------------------------------------------------------------------------------------------
$script 2 {
	PlayLoop.HandlePlayerDeath();
	ExitChallengeWarp();
}
//------------------------------------------------------------------------------------------------------------------------
// Failed (All Villagers Dead)
//------------------------------------------------------------------------------------------------------------------------
$script 3 {
	ExitChallengeWarp();
}
//------------------------------------------------------------------------------------------------------------------------
// Victory
//------------------------------------------------------------------------------------------------------------------------
$script 4 {	
	Camera.StartCinematic(CMF_LOCK_PLAYER|CMF_NO_INITIAL_FADEOUT);
	Camera.SetLookAtActor(Player.Actor().CastToActor());
	Camera.fov = 74.0f;
	Player.Actor().ModelVariation() = TV_NONE;
	Player.Actor().AnimState().Set(anim_player_acquire_key, 8.0f, ANF_LOOP);
	Player.Actor().Flags() &= ~AF_NODRAW;
	Player.Actor().Pitch() = 0;
	Player.Actor().Roll() = 0;
	kVec3 pos = Player.Actor().Origin();
	pos.z = Player.Actor().FloorHeight();
	Player.Actor().Origin() = pos;
	Camera.SetFinalView(0);
	Camera.SetRotationTrack(0,
							Math::Deg2Rad(0.0f),  // start angle
							Math::Deg2Rad(25.0f),   // dest angle
							10*GAME_SCALE,           // start distance
							6*GAME_SCALE,           // dest distance
							7*GAME_SCALE,           // start height
							12*GAME_SCALE,           // dest height
							4*GAME_SCALE,           // start look at height
							7*GAME_SCALE);          // dest look at height
	Camera.AutoPlayRotationTrack(0, 4.5f, CMLT_COSINE);
	delay(2.5f);
	Game.PlaySound("sounds/shaders/ItemCatch.ksnd");
	Game.PrintLine("The Power is yours", 0, 120);
	kActor @decoWeapon = SpawnActor("BP_Wpn_Pistol", Player.Actor().Origin().x, Player.Actor().Origin().y, Player.Actor().Origin().z + 65, Player.Actor().SectorIndex());
	decoWeapon.Flags() &= ~AF_CANBETOUCHED;
	decoWeapon.Scale() = kVec3(0.2f, 0.2f, 0.2f);
	delay(4.0f);
	Player.GiveWeapon(TW_WEAPON_PISTOL, 200); //Refill normal arrow ammo
	SetUpgradePistol(true);
	ExitChallengeWarp();
	return;
}
//------------------------------------------------------------------------------------------------------------------------
// Spawn Enemies
//------------------------------------------------------------------------------------------------------------------------
$script 5 {
	delay(3.0f);
	spawnDelayTime = 1.0f;
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	delay(3.0f);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPHead", true);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	delay(5.0f);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPGruntRange_Poacher", false);
	delay(5.0f);
	spawnDelayTime = 0.75f;
	AddEnemy("BPHead", true);
	AddEnemy("BPHead", true);
	AddEnemy("BPStalker", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPRaptor_Common", false);
	delay(3.0f);
	AddEnemy("BPStalker", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPGruntMelee_PoacherKnife", false);
	delay(3.0f);
	spawnDelayTime = 0.5f;
	AddEnemy("BPStalker", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPRaptor_Common", false);
	AddEnemy("BPStalker", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPGruntRange_Poacher", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPBeetle", false);
	AddEnemy("BPHead", true);
	AddEnemy("BPHead", true);
	delay(7.5f);
	AddEnemy("BPRaptor_Common", false);
	for (int i = 0; i < 6; i++) {
		AddEnemy("BPHead", true);
		AddEnemy("BPRaptor_Common", false);
		AddEnemy("BPStalker", false);
		AddEnemy("BPGruntRange_Poacher", false);
		AddEnemy("BPGruntMelee_PoacherKnife", false);
		AddEnemy("BPBeetle", false);
	}
	while (enemies.length() > 0) { delay(1.0f); };
	Game.CallDelayedMapScript(4, instigator, 0); //Victory
}
//------------------------------------------------------------------------------------------------------------------------
