#include "scripts/common.txt"
#include "scripts/Math.txt"
#include "scripts/BP_Input.txt"

// Next Checkpoint/Save should be 2022

//---Title---
//Mountain of the Sun

//---Story---
//After the destruction of the campaigner and his fortress, Turok realizes the destructive power of the Chronoscepter must never fall into the wrong hands.
//Making the choice to destroy it for the good of all the lost land, Turok's path is clear. The metallurgical elements of the Chronoscepter can only be destroyed by extreme heat.
//The temperatures necessary to complete the task may only be found at the mountain of the sun, the home of the Fireborn.

//Current amount of pickups
//6 defense
//5 speeds
//4 mortal wounds
//4 collectiables (doom in lava catacombs, SW in top lava water in fireborn caves, duke in uderwater secret jungle start, blood in water temple top area with purlins)
//10+ feathers

const bool BP_DEBUG = false; //set to false for release
const int BP_VERSION = 4; //doesn't matter

const int CHEAT_ARGH = 0;
const int CHEAT_DISMEMBERMENT = 1;
const int CHEAT_ADON = 2;

const array<int> CHEAT_TEXT = {13, 14, 31};

const int PROGESS_BEATSPIRITS = 0;

const int SYSFLAG_BEATTUROK = 0;
const int SYSFLAG_BEATADON = 1;

//Weapon Upgrade Flags
const int WUF_DUALMINIGUN = 0;

const int PLAYER_CHAR_TUROK = 0;
const int PLAYER_CHAR_ADON = 1;
const int PLAYER_CHAR_HUMMER = 2;

const int EnemyDeathScriptID = 9999;
const int PlayerDamageScriptID = 9998;
const int PlayerDeathScriptID = 9997;
const int CoyotePickupScriptID = 9996;
const int CougarPickupScriptID = 9995;
const int SpawnFirebornDeathScriptID = 9994;
const int CameraMoveScriptID = 9993;
const int AdonFeathers = 10;
const int HummerFeathers = 10;
const bool OggMusic = false;
const int LT_STARTINDEX = 205; //188;
const int MaxArrows = 30;
const kStr EditorStartMap = "09";//"04";
kStr MusicOggPath = "music/";
kActor @wavMusic;
int startDifficulty;

enum ClipResultFlags
{
	CRF_NOCOLLISION = 0,
	CRF_FLOOR = (1 << 0), // made contact with a floor
	CRF_CEILING = (1 << 1), // made contact with a ceiling
	CRF_WALL = (1 << 2), // made contact with an edge (sector edge that isn't linked to another sector)
	CRF_OBJECT = (1 << 3), // made contact with an actor
	CRF_MESH = (1 << 4), // made contact with a static object
	CRF_ADJUST = (1 << 5), // z-axis was adjusted (ceiling or floor)
	CRF_WALLRADIUS = (1 << 6) // made contact with an edge during the wall radius collision test
}

enum EnumAIFlagsEx
{
	//AIF_ATTACKING = (1 << 0) //Already defined
	//AIF_WASSOLID = (1 << 1) //Already defined
	AIF_FIRSTATTACK = (1 << 2),
	AIF_HEARDLOUDNOISE = (1 << 3),
	//AIF_BLOWNAWAY = (1 << 4) //Already defined
	AIF_GOBACKTOLEASH = (1 << 5),
	AIF_RESURRECT = (1 << 6),
	AIF_REGENDELAY = (1 << 8),
	AIF_SEETARGET = (1 << 9),
	//AIF_NOCHASE = (1 << 10) //Already defined
	AIF_REGENERATEANIM = (1 << 11),
	//AIF_NOTHINK = (1 << 12) //Already defined
	//AIF_RUNNING = (1 << 13) //Already defined
	AIF_GETATTENTION = (1 << 14),
	AIF_REGENDELAY2 = (1 << 15),
	AIF_HEARDQUIETNOISE = (1 << 16),
	AIF_AWAYFROMLEASH = (1 << 17),
	AIF_TELEPORTAWAY = (1 << 18),
	AIF_TELEPORTMOVESLOW = (1 << 19),
	//AIF_TELEPORTING = (1 << 20) //Already defined
	AIF_WAITFORCYCLE = (1 << 22),
	AIF_DAMAGEPANIC = (1 << 23)
}

enum WeaponAmmoType
{
    WEAPONAMMO_NONE = 0,
    WEAPONAMMO_ARROWS,
    WEAPONAMMO_BULLETS,
    WEAPONAMMO_CELLS,
    WEAPONAMMO_SHELLS,
    WEAPONAMMO_EXPSHELLS,
    WEAPONAMMO_MINIGUN,
    WEAPONAMMO_GRENADES,
    WEAPONAMMO_ROCKETS,
    WEAPONAMMO_FUSION,
    WEAPONAMMO_CHRONO,
	
    WEAPONAMMO_COUNT,
}

//------------------------------------------------------------------------------------------------------------------------
// On any map start
//------------------------------------------------------------------------------------------------------------------------
void StartMap()
{
	// SetPlayerLavaArmor(true);
	// SetPlayerChronoUpgrade(true);
	// SetSpecialPickups(0, true);
	// SetSpecialPickups(1, true);
	// SetSpecialPickups(2, true);
	// SetSpecialPickups(3, true);
	// Player.GiveWeapon(14, 20); //rope bow
	//SetPlayerFlute(true);
	
	CheckNotStarted();
	startDifficulty = GetStartDifficulty();
	GiveCharacterStartWeapons();
}
//------------------------------------------------------------------------------------------------------------------------
void TickMap()
{
	// if (Game.GetDifficulty() != startDifficulty)
	// {
		// GameVariables.SetValue("g_difficulty", "" + startDifficulty);
		// Game.PrintLine(LTKey(28), 0, 240);
		//PlayLoop.HandlePlayerDeath();
	// }
}
//------------------------------------------------------------------------------------------------------------------------
kStr LTKey(const int index)
{
	return "$str_" + (LT_STARTINDEX + index);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsMenuOpen()
{
	return (Player.Actor().PlayerFlags() & (1 << 29)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
void DisableWeapon()
{
	Player.Actor().PlayerFlags() |= (1 << 28);
}
//------------------------------------------------------------------------------------------------------------------------
void EnableWeapon()
{
	Player.Actor().PlayerFlags() &= ~(1 << 28);
}
//------------------------------------------------------------------------------------------------------------------------
//Can't shoot and doesn't show weapon
//------------------------------------------------------------------------------------------------------------------------
bool IsWeaponDisabled()
{
	return (Player.Actor().PlayerFlags() & (1 << 28)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
int AsciiToTexID(int8 asciiValue)
{
	return Math::Clamp(asciiValue - 32, 0, 95);
}
//------------------------------------------------------------------------------------------------------------------------
void CheckNotStarted()
{
	if (!BP_DEBUG && !GetStarted())
	{
		Game.Restart();
	}
}
//------------------------------------------------------------------------------------------------------------------------
void GiveCharacterStartWeapons()
{
	switch (GetPlayerCharacter())
	{
		case PLAYER_CHAR_TUROK:
		{
			if (Game.GetCurrentMapID() != 115 && Game.GetCurrentMapID() != 120 && !Player.HasWeapon(13)) //chronoscepter
			{
				Player.GiveWeapon(13, 50); //chronoscepter
			}
			
			if (!Player.HasWeapon(1)) //bow
			{
				Player.GiveWeapon(1, 20); //bow
			}
			break;
		}
		case PLAYER_CHAR_ADON:
		{
			if (Game.GetCurrentMapID() != 115 && !Player.HasWeapon(13)) //chronoscepter
			{
				Player.GiveWeapon(13, 50); //chronoscepter
			}
			
			for (int i = 17; i >= 15; i--)
			{
				if (!Player.HasWeapon(i))
				{
					Player.GiveWeapon(i, 9999);
				}
			}
			break;
		}
		// case PLAYER_CHAR_HUMMER:
		// {
			// if (!Player.HasWeapon(20))
			// {
				// Player.GiveWeapon(20, 9999); //Hummer
			// }
			// break;
		// }
	}
}
//------------------------------------------------------------------------------------------------------------------------
void ClearHUDMessages()
{
	//clear text messages
	Game.PrintLine("", 0, 10);
	Game.PrintLine("", 1, 10);
	Game.PrintLine("", 2, 10);
	Game.PrintLine("", 3, 10);
}
//------------------------------------------------------------------------------------------------------------------------
// Prints all lines top to bottom
//------------------------------------------------------------------------------------------------------------------------
void PrintLines(array<kStr> lines, int time)
{
	int maxLines = 8;
	lines.resize(maxLines);
	for (int i = 0; i < maxLines; i++)
	{
		Game.PrintLine(lines[i], maxLines - i, time);
	}
}
//------------------------------------------------------------------------------------------------------------------------
// Prints all lines bottom to top
//------------------------------------------------------------------------------------------------------------------------
void PrintLinesReverse(array<kStr> lines, int time)
{
	int maxLines = 8;
	lines.resize(maxLines);
	for (int i = 0; i < maxLines; i++)
	{
		Game.PrintLine(lines[i], i, time);
	}
}
//------------------------------------------------------------------------------------------------------------------------
void ClearLines()
{
	for (int i = 0; i < 8; i++)
	{
		Game.PrintLine("", i, 10);
	}
}
//------------------------------------------------------------------------------------------------------------------------
void FacePlayer(kActor @actor)
{
	actor.Yaw() = Player.Actor().Yaw() + Math::pi;
}
//------------------------------------------------------------------------------------------------------------------------
bool IsPlayerOverSave()
{
	return (World.GetAreaFlags(Player.Actor().AreaID()) & AAF_SAVEGAME) != 0 && Player.Actor().OnGround();
}
//------------------------------------------------------------------------------------------------------------------------
bool IsPlayerClimbing()
{
	return (World.GetAreaFlags(Player.Actor().AreaID()) & AAF_CLIMB) != 0 && Player.Actor().OnGround();
}
//------------------------------------------------------------------------------------------------------------------------
void PlayLastOggMusic()
{
	if (OggMusic)
	{
		kStr musicName = GetMusic();
		if (!StringEquals(musicName, ""))
		{
			Game.PlayMusic(MusicOggPath + musicName, true);
		}
	}
}
//------------------------------------------------------------------------------------------------------------------------
void PlayOggMusic(const kStr &in name)
{
	if (OggMusic)
	{
		SetMusic(name);
		delay(0.001f);
		Game.PlayMusic(MusicOggPath + name, true);
	}
}
//------------------------------------------------------------------------------------------------------------------------
void PlayWavMusic(kStr &in name)
{
	if (!OggMusic && StringLength(name) != 0 && !Camera.Active()) //!Player.Locked())
	{
		if (@wavMusic == null)
		{
			@wavMusic = ActorFactory.Spawn("BP_Dummy", 0, 0, 0, 0, -1);
		}
		wavMusic.Origin() = Player.Actor().Origin();
		wavMusic.SetSector(Player.Actor().SectorIndex());
		wavMusic.PlaySound("sounds/shaders/" + name);
	}
}
//------------------------------------------------------------------------------------------------------------------------
void PlayWavCinematicMusic(kStr &in name)
{
	if (@wavMusic == null)
	{
		@wavMusic = ActorFactory.Spawn("BP_Dummy", 0, 0, 0, 0, -1);
	}
	wavMusic.Origin() = Camera.origin;
	wavMusic.PlaySound("sounds/shaders/" + name);
}
//------------------------------------------------------------------------------------------------------------------------
void StopWavMusic()
{
	if (@wavMusic != null)
	{
		wavMusic.StopLoopingSounds();
	}
}
//------------------------------------------------------------------------------------------------------------------------
// Interpolate the player and music actors position to the warp point actor in order to keep the music from restarting
// because it goes to sleep and stops the music when too far from the player which happens from a normal warp.
//------------------------------------------------------------------------------------------------------------------------
void WarpWithMusic(const int warpTID, const bool onGround)
{
	kActor@ actor = Player.Actor().CastToActor();
	if (onGround && !actor.OnGround())
	{
		return;
	}
		
	kActor@ warpTo = World.GetActorByTID(warpTID);
	actor.PlaySound("sounds/shaders/warp_inout.ksnd");
	
	Player.Lock();
	actor.Pitch() = 0.0f;
	actor.Roll() = 0.0f;
	kVec3 dir = (warpTo.Origin() - actor.Origin()).Normalize();
	delay(0.000001f);
	
	kActor@ viewBlocker = ActorFactory.Spawn(6070, 0, 0, 0, 0.0f);
	viewBlocker.Yaw() = actor.Yaw() + Math::pi;
	kVec3 playerForward = ActorForward(actor) * GAME_SCALE;
	viewBlocker.Origin() = actor.Origin() + playerForward;
	
	delay(0.1f);
	int waitCount = 25;
	float moveAmount = 1000.0f;
	while (actor.Origin().Distance(warpTo.Origin()) > moveAmount)
	{
		kVec3 newPos = actor.Origin() + (dir * moveAmount);
		actor.Origin() = newPos;
		if (wavMusic !is null)
		{
			wavMusic.Origin() = newPos;
		}
		viewBlocker.Origin() = newPos + playerForward;
		delay(0.000001f);
		waitCount--;
	}
	
	while (waitCount > 0)
	{
		delay(0.000001f);
		waitCount--;
	}

	actor.SetPosition(warpTo.Origin());
	actor.SetSector(warpTo.SectorIndex());
	actor.Yaw() = warpTo.Yaw() + Math::pi;
	if (wavMusic !is null)
	{
		wavMusic.SetPosition(warpTo.Origin());
		wavMusic.SetSector(warpTo.SectorIndex());
	}
	viewBlocker.Remove();
	
	Player.Unlock();
}
//------------------------------------------------------------------------------------------------------------------------
// Returns the forward direction of the actor
//------------------------------------------------------------------------------------------------------------------------
kVec3 ActorForward(kActor@ actor)
{
	return kVec3(0.0f, 1.0f, 0.0f) * actor.Rotation();
}
//------------------------------------------------------------------------------------------------------------------------
// Returns the right direction of the actor
//------------------------------------------------------------------------------------------------------------------------
kVec3 ActorRight(kActor@ actor)
{
	return kVec3(1.0f, 0.0f, 0.0f) * actor.Rotation();
}
//------------------------------------------------------------------------------------------------------------------------
// Should be used with BP_Bit Actor Types. Returns false if has been removed else returns true.
//------------------------------------------------------------------------------------------------------------------------
bool IsActorBitOff(kActor @actor)
{
	return @actor != null && !actor.IsPersistentMarked();
}
//------------------------------------------------------------------------------------------------------------------------
void BlockArea(const bool block, kActor @actor)
{
	BlockArea(block, actor.Origin().x, actor.Origin().y, actor.Origin().z, actor.SectorIndex());
}
//------------------------------------------------------------------------------------------------------------------------
void BlockArea(const bool block, const float x, const float y, const float z, const int sectorID)
{
	kActor @dummy = ActorFactory.Spawn(6001, x, y, z, 0.0f, sectorID);
	dummy.Flags() |= AF_NODRAW | AF_IGNORESOUNDEVENTS;
	dummy.AnimState().Set(block ? anim_doorClose : anim_doorOpen, 4.0f, 0);
	dummy.AnimState().SetLastFrame(true);
	dummy.Remove();
}
//------------------------------------------------------------------------------------------------------------------------
// Take Primary ammo from a weapon (will give that weapon if player doesn't have it). Does nothing if weapon has max ammo
//------------------------------------------------------------------------------------------------------------------------
void TakeAmmo(int weaponID, int amount)
{
	//if weaponID is current weapon then just use consume ammo
	if (Player.CurrentWeapon() == weaponID)
	{
		Player.ConsumeAmmo(amount);
		return;
	}
	
	int ammo = Player.GetAmmo(weaponID);
	if (ammo <= 0)
	{
		return;
	}
	
	//has weapons max ammo then doesn't work
	if (ammo == 20)
	{
		//idk
	}
	else
	{
		Player.GiveWeapon(weaponID, 2147483647);
		Player.GiveWeapon(weaponID, 2147483649 - amount);
		ammo = Player.GetAmmo(weaponID);
		if (ammo < 0)
		{
			Player.GiveWeapon(weaponID, Math::Abs(ammo));
		}
	}
}
//------------------------------------------------------------------------------------------------------------------------
void ExitCinematic(int haltScriptID)
{
	Game.HaltMapScript(haltScriptID);
	Camera.StopCinematic();
	delay(0.25f);
	Player.Actor().Flags() |= AF_NODRAW;
}
//------------------------------------------------------------------------------------------------------------------------
kActor @SpawnItem(const kStr &in itemName, const float x, const float y, const float z, const float yaw, const int sectorIndex)
{
	kActor @actor = ActorFactory.Spawn(itemName, x, y, z, yaw, sectorIndex);
	actor.Scale().Set(0.35f, 0.35f, 0.35f);
	actor.ClipFlags() = (CF_DROPOFF|CF_CLIPEDGES|CF_NOCLIPACTORS|CF_COLLIDEFLOORS|CF_COLLIDEHEIGHT);
	actor.Velocity() = kVec3(0, 0, 1).Randomize(0.25f).Normalize() * 200.0f;
	actor.Velocity() *= (1.0f / 60.0f);
	actor.BounceDamp() = 0.5f;
	actor.Gravity() = 0.5f;
	return actor;
}
//------------------------------------------------------------------------------------------------------------------------
// Returns a int number to an array of ints that contain the single digit value by index
//------------------------------------------------------------------------------------------------------------------------
array<int> NumberToArray(int num)
{
	if (num == 0)
	{
		array<int> result = {0};
		return result;
	}
	
	num = Math::Abs(num);
	array<int> nums;
	while (num > 0)
	{
		nums.insertLast(num % 10);
		num /= 10;
	}
	nums.reverse();
	return nums;
}
//------------------------------------------------------------------------------------------------------------------------
void HideAI(kActor @actor)
{
	actor.Flags() |= AF_HIDDEN | AF_DISABLED | AF_INVINCIBLE;
}
//------------------------------------------------------------------------------------------------------------------------
void ShowAI(kActor @actor)
{
	actor.Flags() &= ~(AF_HIDDEN | AF_DISABLED | AF_INVINCIBLE);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsAIDisabled(kActor @actor)
{
	return (actor.Flags() & AF_DISABLED) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
bool IsPlayerCenterInRadius(const kVec3 pos, const float radius)
{
	kVec3 p = Player.Actor().Origin();
	p.z += Player.Actor().Height() * 0.5f;
	return p.DistanceSq(pos) < radius;
}
//------------------------------------------------------------------------------------------------------------------------
bool IsPlayerInRadius(const kVec3 pos, const float radius)
{
	return Player.Actor().Origin().DistanceSq(pos) < radius;
}
//------------------------------------------------------------------------------------------------------------------------
bool IsInPlayerBounds(const kVec3 pos, const float radiusScale = 1.0f)
{
	kVec3 p = Player.Actor().Origin();
	float width = Player.Actor().Radius() * radiusScale;
	float height = Player.Actor().Height() * radiusScale;
	return (pos.x >= p.x - width && pos.x <= p.x + width) &&
			(pos.y >= p.y - width && pos.y <= p.y + width) &&
			(pos.z >= p.z - height && pos.z <= p.z + height);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsInBounds(const kVec3 pos, const kVec3 bPos, const float bWidth, const float bHeight)
{
	return (pos.x >= bPos.x - bWidth && pos.x <= bPos.x + bWidth) &&
			(pos.y >= bPos.y - bWidth && pos.y <= bPos.y + bWidth) &&
			(pos.z >= bPos.z - bHeight && pos.z <= bPos.z + bHeight);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsActorCenterInRadius(kActor @actor, const kVec3 pos, const float radius)
{
	kVec3 p = actor.Origin();
	p.z += actor.Height() * 0.5f;
	return p.DistanceSq(pos) < radius;
}
//------------------------------------------------------------------------------------------------------------------------
bool IsActorInRadius(kActor @actor, const kVec3 pos, const float radius)
{
	return actor.Origin().DistanceSq(pos) < radius;
}
//------------------------------------------------------------------------------------------------------------------------
// checks for outter and inner radius and ignores z value
//------------------------------------------------------------------------------------------------------------------------
bool IsInRadius2(kVec3 pos, kVec3 radiusCenter, const float outterRadius, const float innerRadius)
{
	pos.z = 0.0f;
	radiusCenter.z = 0.0f;
	float d = pos.DistanceSq(radiusCenter);
	return (d < outterRadius * outterRadius) && (d >= innerRadius * innerRadius);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsActorDead(kActor @actor)
{
	return ((actor.Flags() & AF_DEAD) != 0) || (actor.Health() <= 0);
}
//------------------------------------------------------------------------------------------------------------------------
bool ActorExists(kActor @actor)
{
	return @actor != null && !actor.IsStale();
}
//------------------------------------------------------------------------------------------------------------------------
bool IsInActorRadius(kActor @actor, kActor @actor2, float radiusScale = 1.0f)
{
	kVec3 p1 = actor.Origin();
	kVec3 p2 = actor2.Origin();
	float radius1 = (actor.Radius() > actor.Height() ? actor.Radius() : actor.Height()) * radiusScale;
	float radius2 = (actor2.Radius() > actor2.Height() ? actor2.Radius() : actor2.Height()) * radiusScale;
	float distance = Math::Sqrt((p1.x - p2.x) * (p1.x - p2.x) +
								(p1.y - p2.y) * (p1.y - p2.y) +
								(p1.z - p2.z) * (p1.z - p2.z));
	return distance < (radius1 + radius2);
}
//------------------------------------------------------------------------------------------------------------------------
bool IsInActorRadius2(kActor @actor, kActor @actor2, float radiusScale = 1.0f)
{
	kVec3 p1 = actor.Origin();
	kVec3 p2 = actor2.Origin();
	float radius1 = actor.Radius() * radiusScale;
	float radius2 = actor2.Radius() * radiusScale;
	float distance = Math::Sqrt((p1.x - p2.x) * (p1.x - p2.x) +
								(p1.y - p2.y) * (p1.y - p2.y));
								//(p1.z - p2.z) * (p1.z - p2.z));
	return distance < (radius1 + radius2);
}
//------------------------------------------------------------------------------------------------------------------------
int StringLength(kStr &in s)
{
	kStr key = ";L;";
	s += key;
	return s.IndexOf(key);
}
//------------------------------------------------------------------------------------------------------------------------
bool StringEquals(kStr &in s, kStr &in s2)
{
	return s.Hash() == s2.Hash();
}
//------------------------------------------------------------------------------------------------------------------------
// Returns the index of the final weight selection
//------------------------------------------------------------------------------------------------------------------------
int ArrayWeights(array<int> chances)
{
	int totalChance = 0;
	for (uint i = 0; i < chances.length(); i++)
	{
		totalChance += chances[i];
	}

	int top = 0;
	int rand = Math::RandMax(totalChance);
	for (uint i = 0; i < chances.length(); i++)
	{
		top += chances[i];
		if (rand < top)
		{
			return i;
		}
	}
	
	return 0;
}
//------------------------------------------------------------------------------------------------------------------------
int ArrayRandomElement(array<int> ary)
{
	return ary[Math::RandMax(ary.length())];
}
//------------------------------------------------------------------------------------------------------------------------
kStr ArrayRandomElement(array<kStr> ary)
{
	return ary[Math::RandMax(ary.length())];
}
//------------------------------------------------------------------------------------------------------------------------
kActor@ ArrayRandomElement(array<kActor@> ary)
{
	return ary[Math::RandMax(ary.length())];
}
//------------------------------------------------------------------------------------------------------------------------
void SetDefensePickups(const int amount)
{
	GameVariables.SetValue("BP_DefensePickups", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetDefensePickups()
{
	int amount;
	GameVariables.GetInt("BP_DefensePickups", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetSpeedPickups(const int amount)
{
	GameVariables.SetValue("BP_SpeedPickups", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetSpeedPickups()
{
	int amount;
	GameVariables.GetInt("BP_SpeedPickups", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetCheat(const int bit, const bool value)
{
	kStr key = "BP_Cheats";
	int flags;
	GameVariables.GetInt(key, flags);
	if (value)
	{
		flags |= (1 << bit);
	}
	else
	{
		flags &= ~(1 << bit);
	}
	GameVariables.SetValue(key, "" + flags);
	kStr cheatMsg = "";
	if (bit >= 0 && bit < int(CHEAT_TEXT.length()))
	{
		cheatMsg = LTKey(CHEAT_TEXT[bit]);
	}
	int msgTime = 240;
	if (value)
	{
		Game.PlaySound("sounds/shaders/pickupSpecial.ksnd");
		Game.PrintLine(LTKey(12), 0, msgTime);
		Game.PrintLine(cheatMsg, 1, msgTime);
		Game.PrintLine(" ", 2, msgTime);
		if (Game.GetCurrentMapID() == 42)
		{
			Game.PrintLine(" ", 3, msgTime);
			Game.PrintLine(" ", 4, msgTime);
			Game.PrintLine(" ", 5, msgTime);
			Game.PrintLine(" ", 6, msgTime);
			Game.PrintLine(" ", 7, msgTime);
		}
		Sys.Print(Game.GetLocalizedText(cheatMsg) + " " + Game.GetLocalizedText(LTKey(12)));
	}
	else
	{
		Game.PrintLine(LTKey(11), 0, msgTime);
		Game.PrintLine(cheatMsg, 1, msgTime);
		Game.PrintLine(" ", 2, msgTime);
		if (Game.GetCurrentMapID() == 42)
		{
			Game.PrintLine(" ", 3, msgTime);
			Game.PrintLine(" ", 4, msgTime);
			Game.PrintLine(" ", 5, msgTime);
			Game.PrintLine(" ", 6, msgTime);
			Game.PrintLine(" ", 7, msgTime);
		}
		Sys.Print(Game.GetLocalizedText(cheatMsg) + " " + Game.GetLocalizedText(LTKey(11)));
	}
}
//------------------------------------------------------------------------------------------------------------------------
bool IsCheatOn(const int bit)
{
	int flags;
	GameVariables.GetInt("BP_Cheats", flags);
	return (flags & (1 << bit)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
void SetSpecialPickups(const int bit, const bool value)
{
	kStr key = "BP_SpecialPickups";
	int flags;
	GameVariables.GetInt(key, flags);
	if (value)
	{
		flags |= (1 << bit);
	}
	else
	{
		flags &= ~(1 << bit);
	}
	GameVariables.SetValue(key, "" + flags);
}
//------------------------------------------------------------------------------------------------------------------------
bool HasSpecialPickup(const int bit)
{
	int flags;
	GameVariables.GetInt("BP_SpecialPickups", flags);
	return (flags & (1 << bit)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
int SpecialPickupCount()
{
	int flags;
	GameVariables.GetInt("BP_SpecialPickups", flags);
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((flags & (1 << i)) != 0)
		{
			count++;
		}
	}
	return count;
}
//------------------------------------------------------------------------------------------------------------------------
void SetUnlockCharacter(const int bit, const bool value)
{
	kStr key = "BP_UnlockedCharacters";
	int flags;
	GameVariables.GetInt(key, flags);
	if (value)
	{
		flags |= (1 << bit);
	}
	else
	{
		flags &= ~(1 << bit);
	}
	GameVariables.SetValue(key, "" + flags);
}
//------------------------------------------------------------------------------------------------------------------------
bool HasUnlockCharacter(const int bit)
{
	int flags;
	GameVariables.GetInt("BP_UnlockedCharacters", flags);
	return (flags & (1 << bit)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
void SetLHBossState(const int amount)
{
	GameVariables.SetValue("BP_LHState", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetLHBossState()
{
	int amount;
	GameVariables.GetInt("BP_LHState", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetLHBossHealth(const int amount)
{
	GameVariables.SetValue("BP_LHHealth", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetLHBossHealth()
{
	int amount;
	GameVariables.GetInt("BP_LHHealth", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetFBBossHealth(const int amount)
{
	GameVariables.SetValue("BP_FBBossHealth", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetFBBossHealth()
{
	int amount;
	GameVariables.GetInt("BP_FBBossHealth", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
int GetLifeForce()
{
	int amount;
	GameVariables.GetInt("BP_Lifeforce", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetKills(const int amount)
{
	GameVariables.SetValue("BP_Kills", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetKills()
{
	int amount;
	GameVariables.GetInt("BP_Kills", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetDeaths(const int amount)
{
	GameVariables.SetValue("BP_Deaths", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetDeaths()
{
	int amount;
	GameVariables.GetInt("BP_Deaths", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetStartDifficulty(const int amount)
{
	GameVariables.SetValue("BP_StartDif", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetStartDifficulty()
{
	int amount;
	GameVariables.GetInt("BP_StartDif", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetFeathers(const int amount)
{
	GameVariables.SetValue("BP_Feathers", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetFeathers()
{
	int amount;
	GameVariables.GetInt("BP_Feathers", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetNPickups(const int amount)
{
	GameVariables.SetValue("BP_N", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetNPickups()
{
	int amount;
	GameVariables.GetInt("BP_N", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetSecrets(const int amount)
{
	GameVariables.SetValue("BP_Secrets", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetSecrets()
{
	int amount;
	GameVariables.GetInt("BP_Secrets", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
int GetVersion()
{
	int amount;
	GameVariables.GetInt("BP_Version", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetPlayerCharacter(const int amount)
{
	GameVariables.SetValue("BP_Character", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetPlayerCharacter()
{
	int amount;
	GameVariables.GetInt("BP_Character", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetMinigunCount(const int amount)
{
	GameVariables.SetValue("BP_MinigunCount", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
int GetMinigunCount()
{
	int amount;
	GameVariables.GetInt("BP_MinigunCount", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetTime(const float amount)
{
	GameVariables.SetValue("BP_Time", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
float GetTime()
{
	float amount;
	GameVariables.GetFloat("BP_Time", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetPlayerDefense(const float amount)
{
	GameVariables.SetValue("BP_PlayerDefense", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
// Returns the PlayersDamage value
//------------------------------------------------------------------------------------------------------------------------
float GetPlayerDefense()
{
	float amount;
	GameVariables.GetFloat("BP_PlayerDefense", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetChronoTime(const float amount)
{
	GameVariables.SetValue("BP_ChronoTime", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
float GetChronoTime()
{
	float amount;
	GameVariables.GetFloat("BP_ChronoTime", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
void SetPlayerSpeed(const float amount)
{
	GameVariables.SetValue("BP_PlayerSpeed", "" + amount);
}
//------------------------------------------------------------------------------------------------------------------------
// Returns the PlayersSpeed value
//------------------------------------------------------------------------------------------------------------------------
float GetPlayerSpeed()
{
	float amount;
	GameVariables.GetFloat("BP_PlayerSpeed", amount);
	return amount;
}
//------------------------------------------------------------------------------------------------------------------------
bool GetFBBossStarted()
{
	bool b;
	GameVariables.GetBool("BP_FBBossStarted", b);
	return b;
}
//------------------------------------------------------------------------------------------------------------------------
void SetFBBossStarted(const bool b)
{
	GameVariables.SetValue("BP_FBBossStarted", b ? "1" : "0");
}
//------------------------------------------------------------------------------------------------------------------------
bool GetStarted()
{
	bool b;
	GameVariables.GetBool("BP_Started", b);
	return b;
}
//------------------------------------------------------------------------------------------------------------------------
void SetStarted(const bool b)
{
	GameVariables.SetValue("BP_Started", b ? "1" : "0");
}
//------------------------------------------------------------------------------------------------------------------------
bool GetPlayerFlute()
{
	bool b;
	GameVariables.GetBool("BP_PlayerFlute", b);
	return b;
}
//------------------------------------------------------------------------------------------------------------------------
void SetPlayerFlute(const bool b)
{
	GameVariables.SetValue("BP_PlayerFlute", b ? "1" : "0");
}
//------------------------------------------------------------------------------------------------------------------------
// Returns true if has armor against lava
//------------------------------------------------------------------------------------------------------------------------
bool GetPlayerLavaArmor()
{
	bool b;
	GameVariables.GetBool("BP_PlayerLava", b);
	return b;
}
//------------------------------------------------------------------------------------------------------------------------
void SetPlayerLavaArmor(const bool b)
{
	GameVariables.SetValue("BP_PlayerLava", b ? "1" : "0");
}
//------------------------------------------------------------------------------------------------------------------------
bool GetPlayerChronoUpgrade()
{
	bool b;
	GameVariables.GetBool("BP_PlayerChrono", b);
	return b;
}
//------------------------------------------------------------------------------------------------------------------------
void SetPlayerChronoUpgrade(const bool b)
{
	GameVariables.SetValue("BP_PlayerChrono", b ? "1" : "0");
}
//------------------------------------------------------------------------------------------------------------------------
void SetMusic(const kStr &in name)
{
	GameVariables.SetValue("BP_Music", name);
}
//------------------------------------------------------------------------------------------------------------------------
kStr GetMusic()
{
	kStr name;
	GameVariables.GetString("BP_Music", name);
	return name;
}
//------------------------------------------------------------------------------------------------------------------------
void SetProgress(const int bit, const bool value)
{
	kStr key = "BP_Progress";
	int flags;
	GameVariables.GetInt(key, flags);
	if (value)
	{
		flags |= (1 << bit);
	}
	else
	{
		flags &= ~(1 << bit);
	}
	GameVariables.SetValue(key, "" + flags);
}
//------------------------------------------------------------------------------------------------------------------------
bool HasProgress(const int bit)
{
	int flags;
	GameVariables.GetInt("BP_Progress", flags);
	return (flags & (1 << bit)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
void SetSysFlag(const int bit, const bool value)
{
	kStr key = "BP_SysFlags";
	int flags;
	GameVariables.GetInt(key, flags);
	if (value)
	{
		flags |= (1 << bit);
	}
	else
	{
		flags &= ~(1 << bit);
	}
	GameVariables.SetValue(key, "" + flags);
}
//------------------------------------------------------------------------------------------------------------------------
bool GetSysFlag(const int bit)
{
	int flags;
	GameVariables.GetInt("BP_SysFlags", flags);
	return (flags & (1 << bit)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
void SetWeaponUpgrade(const int bit, const bool value)
{
	kStr key = "BP_WeaponUpgrades";
	int flags;
	GameVariables.GetInt(key, flags);
	if (value)
	{
		flags |= (1 << bit);
	}
	else
	{
		flags &= ~(1 << bit);
	}
	GameVariables.SetValue(key, "" + flags);
}
//------------------------------------------------------------------------------------------------------------------------
bool GetWeaponUpgrade(const int bit)
{
	int flags;
	GameVariables.GetInt("BP_WeaponUpgrades", flags);
	return (flags & (1 << bit)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
void FoundKey(const int keyID)
{
	kStr key = "BP_Keys";
	int flags;
	GameVariables.GetInt(key, flags);
	flags |= (1 << keyID);
	GameVariables.SetValue(key, "" + flags);
}
//------------------------------------------------------------------------------------------------------------------------
bool HasKey(const int keyID)
{
	int flags;
	GameVariables.GetInt("BP_Keys", flags);
	return (flags & (1 << keyID)) != 0;
}
//------------------------------------------------------------------------------------------------------------------------
// console cheat code
//------------------------------------------------------------------------------------------------------------------------
void argh()
{
	SetCheat(CHEAT_ARGH, !IsCheatOn(CHEAT_ARGH));
}
//------------------------------------------------------------------------------------------------------------------------
// console cheat code
//------------------------------------------------------------------------------------------------------------------------
void limbs()
{
	SetCheat(CHEAT_DISMEMBERMENT, !IsCheatOn(CHEAT_DISMEMBERMENT));
}
//------------------------------------------------------------------------------------------------------------------------
// console cheat code
//------------------------------------------------------------------------------------------------------------------------
void adon()
{
	SetCheat(CHEAT_ADON, !IsCheatOn(CHEAT_ADON));
}
//------------------------------------------------------------------------------------------------------------------------
void help()
{
	Sys.Print("-----Cheat Codes-----");
	Sys.Print("call argh");
	Sys.Print("call limbs");
	Sys.Print("call adon");
}
//------------------------------------------------------------------------------------------------------------------------
