/*
  ____              _      _____       _     _                        _____           _                   ____          _____                                      _ 
 |  _ \            | |    |  __ \     | |   | |                      / ____|         | |                 |  _ \        |  __ \                                    | |
 | |_) | __ _ _ __ | | __ | |__) |___ | |__ | |__   ___ _ __ _   _  | (___  _   _ ___| |_ ___ _ __ ___   | |_) |_   _  | |  | | ___  ___ _ __ ___   ___  _ __   __| |
 |  _ < / _` | '_ \| |/ / |  _  // _ \| '_ \| '_ \ / _ \ '__| | | |  \___ \| | | / __| __/ _ \ '_ ` _ \  |  _ <| | | | | |  | |/ _ \/ __| '_ ` _ \ / _ \| '_ \ / _` |
 | |_) | (_| | | | |   <  | | \ \ (_) | |_) | |_) |  __/ |  | |_| |  ____) | |_| \__ \ ||  __/ | | | | | | |_) | |_| | | |__| |  __/\__ \ | | | | | (_) | | | | (_| |
 |____/ \__,_|_| |_|_|\_\ |_|  \_\___/|_.__/|_.__/ \___|_|   \__, | |_____/ \__, |___/\__\___|_| |_| |_| |____/ \__, | |_____/ \___||___/_| |_| |_|\___/|_| |_|\__,_|
                                                              __/ |          __/ |                               __/ |                                               
                                                             |___/          |___/                               |___/                                                

Author: Desmond
Refer to this thread:


Big vault - 10 minutes to break into, need drill, level 4+, at least 7 LEOs on (count PD and DEA)
Once the big vault opens, it says "Head to the safes to start gathering money.\nNOTE: you may use /stopbankrob to finish the robbery however this will end the robbery and you may not gather more money."

Inside the vault - 6 safes, 3Ds that say "/usedrill\nX seconds left" in green. When the safe is opened, change to "$250,000, /loadsafemoney to load the money."

Each safe takes 90 seconds to break into and give $250,000 each.
While breaking into the safe, the player will get 2 seconds skill checks, they will be required to press "SPACE" before the check ends or else they fail to drill into the safe.
Script counts 5 seconds and randomizes a number - either 0 or 1. If 1, show the skill check, if 0, don't show.

Once all safes are cleared or the robber did /stopbankrob, the script sets "bankmoneygathered" to 250,000 multiplied by how many safes the robbers broke into.
They will then have to load the money in a random location around SF.

Additionals:
Interactable stuff - blocking doors, PD have to use C2s to get through.
*/

#define MAX_VAULTS	6
#define BANK_VAULT_COORDINATES	1640.82, 2907.77, 988.54
#define BANK_VAULT_OUTSIDE	1633.3864, 2907.5081, 988.5403
#define BANK_VAULT_INSIDE	1648.8212, 2907.6978, 988.4282
#define CHECKPOINT_BANKROB 65

/*
	Bank variables
*/

new bankmoneygathered, 		// total money collected by robbers
	bool:bankactive, 		// is a bank robbery active?
	bool:breakingvault,		// used to see if the robbers are currently breaking the big vualt
	bigvault,				// big vault's door object
	bool:goinglaundering,	// used to see if the robbers finished robbing the  vaults (so if they left early no1 can continue breaking into vaults)
	bankrobber;				// the main robber - the person that can collect the money, determined by who did /usedrill on the vault's door


enum vaultinfo
{
	vMoney, 			// how much money left in the safe?
	bool:vBreaking, 	// is someone currently breaking into the safe?
	bool:vOpened,		// is the safe opened?
	Text3D:v3DText, 	// the 3D text above the safe
	Float:vPosX,		// coordinates X of the safe
	Float:vPosY,		// coordinates Y of the safe
	Float:vPosZ, 		// coordinates Z of the safe
	Float:vPosRX,		// coordinates X rotation of the safe
	Float:vPosRY,		// coordinates Y rotation of the safe
	Float:vPosRZ,		// coordinates Z rotation of the safe
	vObject 			// object id of the safe
};

new Vaults[MAX_VAULTS][vaultinfo];

new Float:VaultsPositions[][6] =
{
	{1645.54370, 2910.59302, 987.88550, 0.00000, 0.00000, 0.00000},
	{1649.48596, 2910.59302, 987.88550, 0.00000, 0.00000, 0.00000},
	{1653.44202, 2910.59302, 987.88550, 0.00000, 0.00000, 0.00000},
	{1653.40417, 2904.88403, 987.88550, 0.00000, 0.00000, -178.07994},
	{1649.48755, 2904.88916, 987.88550, 0.00000, 0.00000, -178.07990},
	{1645.62744, 2904.88916, 987.88550, 0.00000, 0.00000, -178.07990}
};

new Float:LaunderingPositions[][3] =
{
	{-2150.8677,-103.1125,35.3203},
	{-1815.8945,-73.5078,15.1094},
	{-1741.9487,1424.6788,7.1875}
};

/*
	Player variables
*/

new bankrobtimer[MAX_PLAYERS], skillchecktimer[MAX_PLAYERS], safebreaktime[MAX_PLAYERS], skillcheckrespond[MAX_PLAYERS];
new bool:playerperformskillcheck[MAX_PLAYERS];
new breakingintovault[MAX_PLAYERS];

GetLEOCount()
{
	new counter;
	foreach(new i: Player)
	{
		if(PlayerInfo[i][pAdmin] < 2 && (PlayerInfo[i][pMember] == FACTION_SAPD || PlayerInfo[i][pMember] == FACTION_FBI || PlayerInfo[i][pMember] == FACTION_SANG)) counter++;
	}
	return counter;
}

IsNearBank(playerid) // used to determine if the player is in the no healing zone
{
	// this if checks both inside and outside
	if(IsPlayerInRangeOfPoint(playerid, 200, 1633.3864, 2907.5081, 988.5403) || IsPlayerInRangeOfPoint(playerid, 200, 1450.8630, -1012.5521, 26.8438)) return 1;
	return 0;
}

StartBankRobbery(playerid)
{
	SendClientMessageToAll(COLOR_LIGHTBLUE, "City Hall: The LS Bank is being robbed - 10 minutes left to break into the vault.");
	SendClientMessage(playerid, COLOR_LIGHTBLUE, "Note: If you move from the vault door the robbery will fail but you may look around and shoot.");
	bankactive = true;
	breakingvault = true;

	bankrobtimer[playerid] = SetTimerEx("BankRobberyTimer", 1000*600, false, "d", playerid); // 10 minutes
	bankrobber = playerid;
	return 1;
}

SetupMoneyLaundering(playerid)
{
	foreach(new i: Player)
	{
		safebreaktime[i] = 0;
		KillTimer(bankrobtimer[i]);
		KillTimer(skillchecktimer[i]);
		KillTimer(safebreaktime[i]);
		KillTimer(skillcheckrespond[i]);
	}
	for(new i = 0; i < MAX_VAULTS; i++) CancelVaultBreak(i);
	goinglaundering = true;
	new rand = random(3);
	SetPlayerCheckpointEx(playerid, LaunderingPositions[rand][0], LaunderingPositions[rand][1], LaunderingPositions[rand][2], 3.0);
	CP[playerid] = CHECKPOINT_BANKROB;
	SendClientMessage(playerid, COLOR_LIGHTBLUE, "Head to the checkpoint marked on your map to receive the laundered money.");
	return 1;
}

forward BankRobberyTimer(playerid);
public BankRobberyTimer(playerid)
{
	breakingvault = false;
	MoveDynamicObject(bigvault, 1641.00696, 2905.82178, 989.11761, 3, 0.00000, 0.00000, 91.36140);
	TimeAfterBankJob = gettime()+172800; // 48 hours
	SendClientMessageToAll(COLOR_LIGHTBLUE, "City Hall: The LS Bank's vault has been opened.");
	SetupVaults();
	SendClientMessage(playerid, COLOR_LIGHTBLUE, "Tip: Head to the safes to start gathering money.");
	SendClientMessage(playerid, -1, "{FF0000}NOTE: {FFFFFF}You may use /stopbankrob to finish the robbery however this will end the robbery and you may not gather more money.");
	return 1;
}

BreakVault(playerid, safeid)
{
	Vaults[safeid][vBreaking] = true;
	breakingintovault[playerid] = safeid;
	safebreaktime[playerid] = 90;	

	skillchecktimer[playerid] = SetTimerEx("SkillCheckTimer", 5000, true, "dd", playerid, safeid); // 5 seconds, playerid and the safeid
	UpdateDynamic3DTextLabelText(Vaults[safeid][v3DText], -1, "{FF0000}Player Drilling\n{FFFFFF}90 seconds left");
	SendClientMessage(playerid, COLOR_LIGHTBLUE, "Note: If you move from the safe the breakin will fail but you may look around and shoot.");
	return 1;
}

forward SkillCheckTimer(playerid, safeid);
public SkillCheckTimer(playerid, safeid)
{
	new msg[60];
	safebreaktime[playerid] -= 5; // no skill check
	format(msg, sizeof(msg), "{FF0000}Player Drilling\n{FFFFFF}%d seconds left", safebreaktime[playerid]);
	UpdateDynamic3DTextLabelText(Vaults[safeid][v3DText], -1, msg);

	if(safebreaktime[playerid] == 0) // finished breaking
	{
		Vaults[safeid][vOpened] = true;
		UpdateDynamic3DTextLabelText(Vaults[safeid][v3DText], -1, "{00FF00}Opened\n{FFFFFF}/loadsafemoney ({00FF00}$250,000 {FFFFFF}inside)");		
		DestroyDynamicObjectEx(Vaults[safeid][vObject]); // destroy safe object
		Vaults[safeid][vObject] = CreateDynamicObject(1829, Vaults[safeid][vPosX], Vaults[safeid][vPosY], Vaults[safeid][vPosZ], Vaults[safeid][vPosRX], Vaults[safeid][vPosRY], Vaults[safeid][vPosRZ]); // place opened safe object
		KillTimer(skillchecktimer[playerid]);
		KillTimer(skillcheckrespond[playerid]);
		return 1;
	}

	new rand = random(2);
	if(rand) // skill check
	{
		displayCenterHUDInfo(playerid,  "~w~SKILL CHECK~n~~r~PRESS SPACE", 2); //display for 2 seconds
		skillcheckrespond[playerid] = SetTimerEx("SkillCheckRespond", 3000, false, "dd", playerid, safeid);
		playerperformskillcheck[playerid] = false;
	}
	return 1;
}

forward SkillCheckRespond(playerid, safeid);
public SkillCheckRespond(playerid, safeid)
{
	if(!playerperformskillcheck[playerid]) // failed to do skill check
	{
		CancelVaultBreak(safeid);
		SendClientMessage(playerid, COLOR_GREY, "You have failed to perform your skill check - safe breakin failed.");
		safebreaktime[playerid] = 0;
		KillTimer(skillchecktimer[playerid]);
		KillTimer(skillcheckrespond[playerid]);
	}
	else KillTimer(skillcheckrespond[playerid]);
	return 1;
}

Hook:bank_OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
	if(PRESSED(KEY_SPRINT))
	{
		if(safebreaktime[playerid] != 0 && bankactive) playerperformskillcheck[playerid] = true; // if is breaking into a vault, did the skill check!
	}
	return 1;
}

Hook:br_OnPlayerEnterCheckpoint(playerid)
{
	if(CP[playerid] == CHECKPOINT_BANKROB)
	{
		new msg[128];
		CP[playerid] = CHECKPOINT_NONE;
		DisablePlayerCheckpointEx(playerid);

		format(msg, sizeof(msg), "You have received $%d money for robbing the bank.", bankmoneygathered);
		SendClientMessage(playerid, COLOR_LIGHTBLUE, msg);
		GameTextForPlayer(playerid, "~w~Good job!", 4000, 1);

		format(msg, sizeof(msg), "City Hall: The laundered money from the bank has been successfully robbed and gathered - $%d.", bankmoneygathered);
		SendClientMessageToAll(COLOR_LIGHTBLUE, msg);

		GiveMoney(playerid, bankmoneygathered);
		GOV -= bankmoneygathered;

		CancelBankRobbery();

		new family = PlayerInfo[playerid][pFMember];
		if(family != 255)
		{
			FamilyInfo[family][fXP] += 15;
			foreach(new i: Player)
			{
				if(PlayerInfo[i][pFMember] == family) 
				{
					SendClientMessage(i, COLOR_LIGHTBLUE, "Your family has been given 15 XP for successfully robbing the bank!");
					GrantFamXP(family, 15);
				}	
			}
		}
	}
	return 1;
}

Hook:bank_OnPlayerDisconnect(playerid, reason)
{
	if(bankrobber == playerid && bankactive)
	{
		SendClientMessageToAll(COLOR_LIGHTBLUE, "City Hall: Robber disconnected, bank robbery failed.");
		CancelBankRobbery();
	}
	KillTimer(bankrobtimer[playerid]);
	KillTimer(skillchecktimer[playerid]);
	KillTimer(safebreaktime[playerid]);
	KillTimer(skillcheckrespond[playerid]);
	return 1;
}

Hook:bank_OnPlayerUpdate(playerid)
{
	if(safebreaktime[playerid] != 0)
	{
		new safeid = breakingintovault[playerid];
		if(!IsPlayerInRangeOfPoint(playerid, 1.5, Vaults[safeid][vPosX], Vaults[safeid][vPosY], Vaults[safeid][vPosZ]))
		{
			SendClientMessage(playerid, COLOR_GREY, "You have moved too much from the safe, you have failed the breakin.");
			KillTimer(skillchecktimer[playerid]);
			KillTimer(safebreaktime[playerid]);
			KillTimer(skillcheckrespond[playerid]);
			CancelVaultBreak(safeid);
			safebreaktime[playerid] = 0;
			return 1;
		}
	}
	if(breakingvault && bankactive)
	{
		if(bankrobber == playerid)
		{
			if(!IsPlayerInRangeOfPoint(playerid, 1.5, BANK_VAULT_COORDINATES))
			{
				SendClientMessage(playerid, COLOR_GREY, "You have moved too much from the vault, bank robbery failed.");
				CancelBankRobbery();
			}
		}
	}
	return 1;
}

Hook:bankrobbery_OnPlayerDeath(playerid, killerid, reason)
{
	if(bankrobber == playerid && bankactive)
	{
		PlayerInfo[playerid][pDrill] = 0;
		CP[playerid] = CHECKPOINT_NONE;
		DisablePlayerCheckpointEx(playerid);
		SendClientMessageToAll(COLOR_LIGHTBLUE, "City Hall: Robber eliminated, bank robbery failed.");
		CancelBankRobbery();

		foreach(new i: Player)
		{
			if(safebreaktime[i] != 0)
			{
				CancelVaultBreak(i);
				KillTimer(skillchecktimer[i]);
				KillTimer(safebreaktime[i]);
				KillTimer(skillcheckrespond[i]);
			}
		}
	}
	KillTimer(bankrobtimer[playerid]);
	KillTimer(skillchecktimer[playerid]);
	KillTimer(safebreaktime[playerid]);
	KillTimer(skillcheckrespond[playerid]);
	return 1;
}

Hook:br_OnGameModeInit()
{
	bigvault = CreateDynamicObject(2634, 1641.35510, 2907.80640, 989.11761,	0.00000, 0.00000, 91.36140);
	return 1;
}

CancelBankRobbery()
{
	for(new i = 0; i < MAX_VAULTS; i++)
	{
		DestroyDynamicObjectEx(Vaults[i][vObject]);
		DestroyDynamic3DTextLabel(Vaults[i][v3DText]);
		Vaults[i][vMoney] = 0;
	}

	bankrobber = -1;
	bankactive = false;
	breakingvault = false;
	bankmoneygathered = 0;
	goinglaundering = false;

	foreach(new i: Player)
	{
		if(IsPlayerInRangeOfPoint(i, 10, BANK_VAULT_INSIDE)) SetPlayerPos(i, BANK_VAULT_OUTSIDE); // tp players that are inside the vault to avoid them getting stuck
		KillTimer(bankrobtimer[i]);
		KillTimer(skillchecktimer[i]);
		KillTimer(safebreaktime[i]);
		KillTimer(skillcheckrespond[i]);
	}
	MoveDynamicObject(bigvault, 1641.35510, 2907.80640, 989.11761, 3, 0.00000, 0.00000, 91.36140);
	return 1;
}

CancelVaultBreak(safeid)
{
	Vaults[safeid][vBreaking] = false;
	UpdateDynamic3DTextLabelText(Vaults[safeid][v3DText], -1, "{FFFFFF}/usedrill\n{FF0000}No one drilling");
	return 1;
}

IsByVaultDoor(playerid)
{
	if(IsPlayerInRangeOfPoint(playerid, 1.5, BANK_VAULT_COORDINATES)) return 1;
	return 0;
}

IsBySafe(playerid)
{
	new safeid = -1;

	for(new i = 0; i < MAX_VAULTS; i++)
	{
		if(IsPlayerInRangeOfPoint(playerid, 1.5, Vaults[i][vPosX], Vaults[i][vPosY], Vaults[i][vPosZ]))
		{
			safeid = i;
			break;
		}
	}
	return safeid;
}

SetupVaults()
{
	for(new i = 0; i < MAX_VAULTS; i++)
	{
		Vaults[i][vMoney] = 250000;
		Vaults[i][vBreaking] = false;
		Vaults[i][vOpened] = false;
		Vaults[i][vPosX] = VaultsPositions[i][0];
		Vaults[i][vPosY] = VaultsPositions[i][1];
		Vaults[i][vPosZ] = VaultsPositions[i][2];
		Vaults[i][vPosRX] = VaultsPositions[i][3];
		Vaults[i][vPosRY] = VaultsPositions[i][4];
		Vaults[i][vPosRZ] = VaultsPositions[i][5];

		Vaults[i][vObject] = CreateDynamicObject(2332, Vaults[i][vPosX], Vaults[i][vPosY], Vaults[i][vPosZ], Vaults[i][vPosRX], Vaults[i][vPosRY], Vaults[i][vPosRZ]);
		Vaults[i][v3DText] = CreateDynamic3DTextLabel("{FFFFFF}/usedrill\n{FF0000}No one drilling", -1, Vaults[i][vPosX], Vaults[i][vPosY], Vaults[i][vPosZ]+0.8, 5);
	}
	return 1;
}

CMD:usedrill(playerid, params[])
{
	if(PlayerInfo[playerid][pLevel] < 4) return SendClientMessage(playerid, COLOR_GREY, "You need to be level 4+ to use this.");
	if(PlayerInfo[playerid][pDrill] != 1) return SendClientMessage(playerid, COLOR_GREY, "You don't have a drill");
	if(!IsByVaultDoor(playerid) && !bankactive) return SendClientMessage(playerid, COLOR_GREY, "You are not near the vault's door.");
	if(bankactive && IsBySafe(playerid) == -1) return SendClientMessage(playerid, COLOR_GREY, "You are not near a safe.");
	if(GetLEOCount() < 7 && !bankactive) return SendClientMessage(playerid, COLOR_GREY, "There must be 7 LEOs on (SAPD, FBI or SANG, admins excluded)");
	if(gettime() < TimeAfterBankJob && !bankactive) return SendClientMessage(playerid, COLOR_GREY, "The bank robbery system is currently on cooldown.");
	if(goinglaundering) return SendClientMessage(playerid, COLOR_GREY, "You may not launder more money as the robber stopped robbing.");

	if(!bankactive) StartBankRobbery(playerid); // break into the vault
	else if(bankactive) // break into a safe
	{
		new safeid = IsBySafe(playerid);
		if(Vaults[safeid][vOpened]) return SendClientMessage(playerid, COLOR_GREY, "This safe is already opened.");
		if(Vaults[safeid][vBreaking]) return SendClientMessage(playerid, COLOR_GREY, "Someone is already breaking into this safe");
		BreakVault(playerid, safeid);
	}
	return 1;
}

CMD:loadsafemoney(playerid, params[])
{
	if(!bankactive) return SendClientMessage(playerid, COLOR_GREY, "A bank robbery is not active.");
	new safeid = IsBySafe(playerid);
	if(safeid == -1) return SendClientMessage(playerid, COLOR_GREY, "You are not by a safe.");
	if(!Vaults[safeid][vOpened]) return SendClientMessage(playerid, COLOR_GREY, "This safe is not opened (/usedrill to start breaking into it).");
	if(Vaults[safeid][vMoney] == 0) return SendClientMessage(playerid, COLOR_GREY, "Someone already loaded the money from this safe.");

	bankmoneygathered += 250000;
	Vaults[safeid][vMoney] = 0;

	if(bankmoneygathered == 1500000) SetupMoneyLaundering(bankrobber);

	UpdateDynamic3DTextLabelText(Vaults[safeid][v3DText], -1, "{00FF00}Opened\n{FFFFFF}Loaded ({00FF00}$0 {FFFFFF}inside)");
	new msg[128];
	format(msg, sizeof(msg), "You have loaded $250,000 from the safe (total: %d), the main robber may use /stopbankrob to stop robbing.", bankmoneygathered);
	SendClientMessage(playerid, COLOR_LIGHTBLUE, msg);
	SendClientMessage(playerid, COLOR_LIGHTBLUE, "NOTE: This will prevent you from gathering more money in this robbery.");
	return 1;
}

CMD:stopbankrob(playerid, params[])
{
	if(!bankactive) return SendClientMessage(playerid, COLOR_GREY, "There is currently no active bank robbery.");
	if(bankrobber != playerid) return SendClientMessage(playerid, COLOR_GREY, "Only the main robber may use this.");
	if(bankmoneygathered < 750000) return SendClientMessage(playerid, COLOR_GREY, "There must be at least $750,000 money laundered before stopping the robbery.");

	SetupMoneyLaundering(playerid);
	return 1;
}


CMD:checkbanktime(playerid, params[])
{
    new msg[128];

    new 
        diff = TimeAfterBankJob-gettime(),
        hours, minutes, seconds;

    if(diff < 0) return SendClientMessage(playerid, COLOR_GREY, "Bank robbery is available.");

    hours = diff / 3600;
    diff %= 3600;

    minutes = diff/60, 
    diff %= 60;

    seconds = diff;

    format(msg, sizeof(msg), "Time left: %d:%d:%d", hours, minutes, seconds);
    SendClientMessage(playerid, COLOR_GREY, msg);
    return 1;    
}

CMD:allowrobbank(playerid, params[])
{
	if(PlayerInfo[playerid][pAdmin] < 3) return AdmErrorMsg;

	SendClientMessage(playerid, COLOR_LIGHTBLUE, "Bank robbery is now allowed.");
	TimeAfterBankJob = gettime();
	return 1;
}

CMD:bankhelp(playerid, params[])
{
	SendClientMessage(playerid, COLOR_YELLOW, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
	SendClientMessage(playerid, COLOR_WHITE, "/checkbanktime - check when you can rob the bank again (24 hours cooldown since last robbery)");
	SendClientMessage(playerid, COLOR_WHITE, "/usedrill - drill into the big vault or into a safe");
	SendClientMessage(playerid, COLOR_WHITE, "/stopbankrob - can only be used by the main robber, stops the robbery ($750,000 loaded required)");
	SendClientMessage(playerid, COLOR_WHITE, "/loadsafemoney - launder money (does not give you money) which can be collected by the main robber at the checkpoint");
	SendClientMessage(playerid, COLOR_WHITE, "Notes - drill is unlimited but lost on death. 7 LEOs+ (PD/FBI/SANG) are required to rob the bank (admins do not count).");
	SendClientMessage(playerid, COLOR_WHITE, "You have a 50 percents chance to get a skill check when breaking into a safe.");
	SendClientMessage(playerid, COLOR_WHITE, "You have 3 seconds to respond to it (press SPACE or your custom sprint key). Only the main robber may collect the money.");
	SendClientMessage(playerid, COLOR_YELLOW, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
	return 1;
}