/*----------------------------------------------------------------------------*-
					==================================
					Y Sever Includes - Properties Core
					==================================
Description:
	Handles properties and other common features for some modes.
Legal:
	Copyright (C) 2007 Alex "Y_Less" Cole

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
	MA 02110-1301, USA.
Version:
	0.1
Changelog:
	03/07/07:
		Added invert option on CreateRestrictedArea.
	24/06/07:
		Modifed a few functions to use Bit_GetBit for speed.
	19/06/07:
		Added minor functions (IsValid, GetType and GetLink)
	18/06/07:
		Added commands.
	14/06/07:
		Updated functions to use checkpoint and area lookups
	18/04/07:
		First version.
Functions:
	Public:
		Property_Loop
	Core:
		Property_OnPlayerSelectedMenuRow - Called when someone selects something.
		Property_OnPlayerExitedMenu - Called when someone exits a menu.
		Property_OnPlayerEnterCheckpointEx - Called when someone enters a checkpoint.
		Property_OnPlayerLeaveCheckpointEx - Called when someone leaves a checkpoint.
		Property_OnPlayerSpawn - Called when someone spawns.
		Property_OnPlayerConnect - Called when comaone connects.
		Property_OnPlayerLeaveArea - Called when someone leaves an area.
		Property_OnPlayerEnterArea - Called when someone enters an area.
	Stock:
		Property_GetPlayerPropCount - Gets the number of properties for a player.
		Property_GetLink - Gets the area or checkpoint for a property.
		Property_GetType - Gets a property's type.
		Property_IsValid - Checks if a property is valid.
		Property_Bank - Moves money to/from an account.
		Property_SavePlayerWeapon - Saves a players weapon for spawn.
		Property_SaveWeapons - Saves a player's spawn weapons.
		Property_LoadWeapons - Loads a player's spawn weapons.
		Property_SaveBank - Saves a player's banked money.
		Property_LoadBank - Loads a player's banked money.
	Static:
		Property_GetWeapon - Gets weapon data for a slotid slot.
		Property_WeaponName - Gets a weapon name for a slotid.
		Property_GetWeaponFromSlot - Gets a weaponid from a slotid.
		Property_GenerateAmmuMenu - Generates the menu for ammunation.
	Inline:
		Property_SetOption - Sets a property's custom flag.
		Property_IsActive - Checks if a property is active.
		Property_GetOption - Gets a property's custom flag.
		Property_GivePlayerWeapon - Gives a player a weapon from it's slot.
		Property_WeaponCost - Gets the cost of a slot,
		Property_WeaponAmmo - Gets the ammo of a slot.
		Property_IsPlayerProperty - Checks if a player can have a property.
	API:
		CreateProperty - Creates a business.
		CreateBank - Creates a bank.
		CreateAmmunation - Creates an ammunation.
		CreateMoneyArea - Creates a money area.
		CreateMoneyPoint - Creates a money point.
		CreateTeleport - Creates a teleport.
		CreateForbiddenArea - Creates a forbidden area.
		DestroyProperty - Deletes a property from the array.
Callbacks:
	-
Definitions:
	PROPERTY_LOOP_GRANULARITY - Number of itterations of the main loop a second.
	MAX_PROP_NAME - Max length of the name of a property.
	MAX_PROPERTIES - Max number of properties.
	NO_PROPERTY - Invalid return.
	WEAPON_ARMOUR - Type for salling armour at ammunation.
	PROPERTY_SELL_PERCENT - % of money got back for a reduced sale.
Enums:
	e_PROP_FLAGS - Flags for property data.
	E_PROP_DATA - Data for a property.
	E_PROP_AMMU - Data for a persons current ammunation menu.
Macros:
	WEAPON_DATA_OFFSET - Offset for saving weapons with variable size arrays,
	WEAPON_DATA - Gets ammo and cost from parameters and compresses them.
	WEAPON - Saves a weapon id in the top byte of a cell.
Tags:
	e_PROP_FLAGS - Flags.
Variables:
	Global:
		-
	Static:
		YSI_g_sProperties - Array of all property data.
		YSI_g_sMoney - Array of player's banked money.
		YSI_g_sPlayerProperties - Bit array of properties a player has.
		YSI_g_sSpawnWeapons - Array of weapons a player will spawn with.
		YSI_g_sShopMenu - Array of players current menus.
		YSI_g_sAreaPointers - Array of properties for each area.
		YSI_g_sCheckpointPointers - Array of properties for each checkpoint.
Commands:
	buy - Lets you buy your current property.
	bank - Lets you bank money.
	properties - Lists properties and their owners.
	balance - Displays your current balance.
	withdraw - Allows you to take out money.
	sell - Allows you to sell a property.
Compile options:
	-
Operators:
	-
-*----------------------------------------------------------------------------*/

#if !defined PROPERTY_LOOP_GRANULARITY
	#define PROPERTY_LOOP_GRANULARITY 5
#endif

#if defined MAX_PROP_NAME
	#if MAX_PROP_NAME < (39 - PLAYER_BIT_ARRAY)
		#undef MAX_PROP_NAME
	#endif
#endif

#if !defined MAX_PROP_NAME
	#define MAX_PROP_NAME (39 - PLAYER_BIT_ARRAY)
#endif

#if !defined MAX_PROPERTIES
	#define MAX_PROPERTIES 256
#endif

#define NO_PROPERTY -1

#define WEAPON_ARMOUR 100

#define PROPERTY_SELL_PERCENT 60

#define PROPERTY_INCREASE_PERCENT 125

#define WEAPON_DATA_OFFSET (PLAYER_BIT_ARRAY - 2)

#define WEAPON_DATA (((getarg(pos++) & 0xFFF) << 20) | (getarg(pos++) & 0xFFFFF))

#define WEAPON(%1) ((%1) << 24)

enum e_PROP_FLAGS (+= 0x10000)
{
	e_PROP_FLAGS_LINK		= 0x0000FFFF,
	e_PROP_FLAGS_TYPES		= 0x00FF0000,
	e_PROP_FLAGS_TYPE_PROP	= 0x00010000,
	e_PROP_FLAGS_TYPE_BANK,
	e_PROP_FLAGS_TYPE_AMMU,
	e_PROP_FLAGS_TYPE_TELS,
	e_PROP_FLAGS_TYPE_TELT,
	e_PROP_FLAGS_TYPE_MONP,
	e_PROP_FLAGS_TYPE_MONA,
	e_PROP_FLAGS_TYPE_RSRC,
	e_PROP_FLAGS_FLAGS		= 0xFF000000,
	e_PROP_FLAGS_CUST_1		= 0x01000000,
	e_PROP_FLAGS_CUST_2		= 0x02000000,
	e_PROP_FLAGS_CUST_3		= 0x04000000,
	e_PROP_FLAGS_CUST_4		= 0x08000000,
	e_PROP_FLAGS_CUST_5		= 0x10000000,
	e_PROP_FLAGS_ACTIVE		= 0x80000000
}

enum E_PROP_DATA
{
	E_PROP_DATA_NAME[MAX_PROP_NAME],
	e_PROP_FLAGS:E_PROP_DATA_FLAGS,
	E_PROP_DATA_DATA_1,
	E_PROP_DATA_DATA_2,
	Bit:E_PROP_DATA_PLAYERS[PLAYER_BIT_ARRAY]
}

enum E_PROP_AMMU
{
	Menu:E_PROP_AMMU_MENU,
	E_PROP_AMMU_DATA
}

static
	YSI_g_sProperties[MAX_PROPERTIES][E_PROP_DATA],
	YSI_g_sMoney[MAX_PLAYERS],
	Bit:YSI_g_sPlayerProperties[MAX_PLAYERS][Bit_Bits(MAX_PROPERTIES)],
	YSI_g_sSpawnWeapons[MAX_PLAYERS][13],
	YSI_g_sShopMenu[MAX_PLAYERS][E_PROP_AMMU],
	YSI_g_sAreaPointers[MAX_AREAS],
	YSI_g_sCheckpointPointers[MAX_CHECKPOINTS];

forward Property_Loop();
forward ycmd_sell(playerid, params[], help);
forward ycmd_bank(playerid, params[], help);
forward ycmd_balance(playerid, params[], help);
forward ycmd_buy(playerid, params[], help);
forward ycmd_withdraw(playerid, params[], help);
forward ycmd_properties(playerid, params[], help);
	
Text_RegisterTag(ysi_properties);

/*----------------------------------------------------------------------------*-
Function:
	Property_IsActive
Params:
	prop - Property to check.
Return:
	-
Notes:
	-
-*----------------------------------------------------------------------------*/

#define Property_IsActive(%1) \
	((%1) < MAX_PROPERTIES && (%1) >= 0 && YSI_g_sProperties[(%1)][E_PROP_DATA_FLAGS] & e_PROP_FLAGS_ACTIVE)

/*----------------------------------------------------------------------------*-
Function:
	Property_SetOption
Params:
	slot - Custom flag to set.
	value - Value to set it to.
Return:
	-
Notes:
	Can't be changed except from 0 to 1.
-*----------------------------------------------------------------------------*/

#define Property_SetOption(%1,%2) \
	((%2) ? e_PROP_FLAGS_CUST_%1 : e_PROP_FLAGS:0)

/*----------------------------------------------------------------------------*-
Function:
	Property_GetOption
Params:
	slot - Custom flag to get.
	flags - flags cell to get it from.
Return:
	-
Notes:
	-
-*----------------------------------------------------------------------------*/

#define Property_GetOption(%1,%2) \
	(e_PROP_FLAGS_CUST_%1 & e_PROP_FLAGS:(%2))

/*----------------------------------------------------------------------------*-
Function:
	Property_GivePlayerWeapon
Params:
	playerid - Player to gice a weapon to.
	slot - Weapon slot to give.
	ammo - Ammo to give.
Return:
	-
Notes:
	Gives a player a weapon by slot instead of weaponid or assignes armour.
-*----------------------------------------------------------------------------*/

#define Property_GivePlayerWeapon(%1,%2,%3) \
	if ((%2) == 39) SetPlayerArmour((%1), 100.0); else GivePlayerWeapon(%1, Property_GetWeaponFromSlot(%2), %3)

/*----------------------------------------------------------------------------*-
Function:
	Property_IsValid
Params:
	property - Property to check.
Return:
	Is the property a valid active property.
Notes:
	-
-*----------------------------------------------------------------------------*/

stock Property_IsValid(property)
{
	return Property_IsActive(property);
}

/*----------------------------------------------------------------------------*-
Function:
	Property_GetType
Params:
	property - Property to get the type of.
Return:
	0 if invalid or type.
Notes:
	-
-*----------------------------------------------------------------------------*/

stock Property_GetType(property)
{
	if (!Property_IsActive(property)) return 0;
	return _:(YSI_g_sProperties[property][E_PROP_DATA_FLAGS] & e_PROP_FLAGS_TYPES);
}

/*----------------------------------------------------------------------------*-
Function:
	Property_Property
Params:
	-
Return:
	-
Notes:
	Sets variable initial states.
-*----------------------------------------------------------------------------*/

Property_Property()
{
	SetTimer("Property_Loop", 1000 / PROPERTY_LOOP_GRANULARITY, 1);
	#if defined _YSI_CORE_COMMANDS
		ycmd("buy");
		ycmd("bank");
		ycmd("properties");
		ycmd("balance");
		ycmd("withdraw");
		ycmd("sell");
	#endif
	for (new i = 0; i < MAX_AREAS; i++) YSI_g_sAreaPointers[i] = NO_PROPERTY;
	for (new i = 0; i < MAX_CHECKPOINTS; i++) YSI_g_sCheckpointPointers[i] = NO_PROPERTY;
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	DestroyProperty
Params:
	property - Property to destroy.
Return:
	-
Notes:
	-
-*----------------------------------------------------------------------------*/

stock DestroyProperty(property)
{
	if (!Property_IsActive(property)) return 0;
	new
		e_PROP_FLAGS:flags = YSI_g_sProperties[property][E_PROP_DATA_FLAGS];
	YSI_g_sProperties[property][E_PROP_DATA_FLAGS] = e_PROP_FLAGS:0;
	if (flags & e_PROP_FLAGS_TYPES > e_PROP_FLAGS_TYPE_MONP)
	{
		YSI_g_sAreaPointers[_:(flags & e_PROP_FLAGS_LINK)] = NO_PROPERTY;
		Area_Delete(_:(flags & e_PROP_FLAGS_LINK));
	}
	else
	{
		YSI_g_sCheckpointPointers[_:(flags & e_PROP_FLAGS_LINK)] = NO_PROPERTY;
		DestroyCheckpoint(_:(flags & e_PROP_FLAGS_LINK));
	}
	for (new i = 0; i < MAX_PLAYERS; i++) Bit_Set(YSI_g_sPlayerProperties[i], property, 0);
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateProperty
Params:
	name[] - Name of the property.
	Float:x - X position.
	Float:y - Y position.
	Float:z - Z position.
	price - Cost of the property.
	reward - Money earnt from the property periodically.
	interval - How often you earn money.
	sell - Wether you need to seel the property of people can just buy you out.
	multi - Wether more than one person can own this property at once.
	reduce - Wether selling this property gets you less then you paid for it.
	increase - Wether buying this property will put the price up for the future.
Return:
	-
Notes:
	Creates a buyable property (business).
-*----------------------------------------------------------------------------*/

stock CreateProperty(name[], Float:x, Float:y, Float:z, price, reward, interval = 60000, sell = 0, multi = 0, reduce = 0, increase = 0)
{
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	new
		cp = CreateCheckpoint(x, y, z, 3.0);
	if (cp == NO_CHECKPOINT) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_PROP | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:cp | Property_SetOption(1, sell) | Property_SetOption(2, multi) | Property_SetOption(3, reduce) | Property_SetOption(4, increase);
	if (multi) Bit_SetAll(YSI_g_sProperties[i][E_PROP_DATA_PLAYERS], 0);
	else YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][0] = Bit:INVALID_PLAYER_ID;
	YSI_g_sProperties[i][E_PROP_DATA_NAME][MAX_PROP_NAME - 1] = interval & 0xFFFFF;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_1] = (interval & 0xFFFFF) | ((price & 0x00000FFF) << 20);
	YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = (reward & 0x3FFFF) | ((price & 0x03FFF000) << 6);
	strcpy(YSI_g_sProperties[i][E_PROP_DATA_NAME], name, MAX_PROP_NAME - 1);
	YSI_g_sCheckpointPointers[cp] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateBank
Params:
	Float:x - X position.
	Float:y - Y position.
	Float:z - Z position.
	name[] - Name of the bank.
Return:
	-
Notes:
	-
-*----------------------------------------------------------------------------*/

stock CreateBank(Float:x, Float:y, Float:z, name[] = "")
{
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	new
		cp = CreateCheckpoint(x, y, z, 3.0);
	if (cp == NO_CHECKPOINT) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_BANK | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:cp;
	strcpy(YSI_g_sProperties[i][E_PROP_DATA_NAME], name, MAX_PROP_NAME);
	YSI_g_sCheckpointPointers[cp] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateAmmunation
Params:
	Float:x - X position.
	Float:y - Y position.
	Float:z - Z position.
	spawn - Wether you spawn with the weapon.
	instant - Wether you get the weapon as soon as you buy it.
	...
Return:
	-
Notes:
	spawn and instant can BOTH be 1.  The format of the additional parameters
	is:
	
	weapon, ammo, price
	
	They MUST come in sets of three of the function will fail.  Weapon is in
	the form of the WEAPON_ defines in a_samp or WEAPON_ARMOUR.
-*----------------------------------------------------------------------------*/

stock CreateAmmunation(Float:x, Float:y, Float:z, spawn, instant, ...)
{
	new
		num = numargs();
	if (((num - 5) / 3) * 3 != (num - 5)) return NO_PROPERTY;
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	new
		cp = CreateCheckpoint(x, y, z, 3.0);
	if (cp == NO_CHECKPOINT) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_AMMU | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:cp | Property_SetOption(1, spawn) | Property_SetOption(2, instant);
	new
		pos = 5,
		count;
	while (pos < num)
	{
		switch (getarg(pos++))
		{
			case WEAPON_BRASSKNUCKLE:
				YSI_g_sProperties[i][E_PROP_DATA_DATA_1] = WEAPON_DATA;
			case WEAPON_GOLFCLUB:
				YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = WEAPON_DATA;
			case WEAPON_NITESTICK:
				YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][0] = Bit:WEAPON_DATA;
			case WEAPON_KNIFE:
				YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][1] = Bit:WEAPON_DATA;
			case WEAPON_BAT:
				#if PLAYER_BIT_ARRAY > 2
					YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][2] = Bit:WEAPON_DATA;
				#else
					YSI_g_sProperties[i][E_PROP_DATA_NAME][0 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
				#endif
			case WEAPON_SHOVEL:
				#if PLAYER_BIT_ARRAY > 3
					YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][3] = Bit:WEAPON_DATA;
				#else
					YSI_g_sProperties[i][E_PROP_DATA_NAME][1 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
				#endif
			case WEAPON_POOLSTICK:
				#if PLAYER_BIT_ARRAY > 4
					YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][4] = Bit:WEAPON_DATA;
				#else
					YSI_g_sProperties[i][E_PROP_DATA_NAME][2 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
				#endif
			case WEAPON_KATANA:
				#if PLAYER_BIT_ARRAY > 5
					YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][5] = Bit:WEAPON_DATA;
				#else
					YSI_g_sProperties[i][E_PROP_DATA_NAME][3 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
				#endif
			case WEAPON_CHAINSAW:
				#if PLAYER_BIT_ARRAY > 6
					YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][6] = Bit:WEAPON_DATA;
				#else
					YSI_g_sProperties[i][E_PROP_DATA_NAME][4 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
				#endif
			case WEAPON_DILDO:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][5 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_DILDO2:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][6 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_VIBRATOR:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][7 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_VIBRATOR2:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][8 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_FLOWER:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][9 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_CANE:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][10 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_GRENADE:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][11 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_TEARGAS:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][12 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_MOLTOV:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][13 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_COLT45:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][14 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SILENCED:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][15 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_DEAGLE:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][16 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SHOTGUN:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][17 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SAWEDOFF:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][18 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SHOTGSPA:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][19 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_UZI:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][20 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_MP5:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][21 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_AK47:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][22 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_M4:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][23 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_TEC9:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][24 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_RIFLE:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][25 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SNIPER:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][26 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_ROCKETLAUNCHER:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][27 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_FLAMETHROWER:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][28 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_MINIGUN:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][29 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SATCHEL:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][30 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_SPRAYCAN:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][31 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_FIREEXTINGUISHER:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][32 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_CAMERA:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][33 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_PARACHUTE:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][34 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			case WEAPON_ARMOUR:
				YSI_g_sProperties[i][E_PROP_DATA_NAME][35 - WEAPON_DATA_OFFSET] = WEAPON_DATA;
			default:
			{
				pos += 2;
				count--;
			}
		}
		count++;
	}
	YSI_g_sProperties[i][E_PROP_DATA_NAME][MAX_PROP_NAME - 1] = count;
	YSI_g_sCheckpointPointers[cp] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateMoneyArea
Params:
	area - Reference to a defined area.
	money - How much you earn.
	interval - How often you earn.
Return:
	-
Notes:
	This function has internal checking for invalid areas so you can just pass
	the return from an Area_ funtion directly.
-*----------------------------------------------------------------------------*/

stock CreateMoneyArea(area, money = 100, interval = 10000)
{
	if (area == NO_AREA) return NO_PROPERTY;
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_MONA | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:area;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_1] = money;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = interval;
	YSI_g_sProperties[i][E_PROP_DATA_NAME][0] = interval;
	YSI_g_sAreaPointers[area] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateMoneyPoint
Params:
	Float:x - X position.
	Float:y - Y position.
	Float:z - Z position.
	Float:s - Size of checkpoint.
	money - How much you earn.
	interval - How often you earn.
Return:
	-
Notes:
	Like CreateMoneyArea but you must be in a checkpoint.
-*----------------------------------------------------------------------------*/

stock CreateMoneyPoint(Float:x, Float:y, Float:z, Float:s, money = 100, interval = 10000)
{
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	new
		cp = CreateCheckpoint(x, y, z, s);
	if (cp == NO_CHECKPOINT) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_MONP | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:cp;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_1] = money;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = interval;
	YSI_g_sProperties[i][E_PROP_DATA_NAME][0] = interval;
	YSI_g_sCheckpointPointers[cp] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateTeleport
Params:
	Float:sx - Start X.
	Float:sy - Start Y.
	Float:sz - Start Z.
	Float:tz - Target X.
	Float:ty - Target Y.
	Float:tz - Target Z.
	cost - Price to use the teleport.
	name[] - Name of the destination.
Return:
	-
Notes:
	Will teleport you the moment you step in.
-*----------------------------------------------------------------------------*/

stock CreateTeleport(Float:sx, Float:sy, Float:sz, Float:tx, Float:ty, Float:tz, cost = 0, name[] = "")
{
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	new
		cp = CreateCheckpoint(sx, sy, sz, 3.0);
	if (cp == NO_CHECKPOINT) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_TELS | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:cp;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_1] = cost;
	YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = _:tx;
	YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][0] = Bit:ty;
	YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][1] = Bit:tz;
	strcpy(YSI_g_sProperties[i][E_PROP_DATA_NAME], name, MAX_PROP_NAME);
	YSI_g_sCheckpointPointers[cp] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	CreateForbiddenArea
Params:
	area - Reference to a defined area.
	kick - Wether they should be kicked out not hurt.
	health - How much health should be removed every itteration they're in.
	invert - Not allowed to leave the area rather than enter.
	name[] - Name of the area.
Return:
	-
Notes:
	If kick is 1 people will simply be constantly replaced outside the area
	from the direction they came.
-*----------------------------------------------------------------------------*/

stock CreateForbiddenArea(area, kick = 0, health = 1000, invert = 0, name[] = "")
{
	if (area == NO_AREA) return NO_PROPERTY;
	new
		i;
	while (i < MAX_PROPERTIES)
	{
		if (!Property_IsActive(i)) break;
		i++;
	}
	if (i == MAX_PROPERTIES) return NO_PROPERTY;
	YSI_g_sProperties[i][E_PROP_DATA_FLAGS] = e_PROP_FLAGS_TYPE_RSRC | e_PROP_FLAGS_ACTIVE | e_PROP_FLAGS:area | Property_SetOption(1, kick) | Property_SetOption(2, invert);
	YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = health;
	strcpy(YSI_g_sProperties[i][E_PROP_DATA_NAME], name, MAX_PROP_NAME);
	YSI_g_sAreaPointers[area] = i;
	return i;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerEnterArea
Params:
	playerid - Player who entered an area.
	areaid - Area they entered.
Return:
	-
Notes:
	Internal callback from YSI_areas.
-*----------------------------------------------------------------------------*/

Property_OnPlayerEnterArea(playerid, area)
{
	new
		prop = YSI_g_sAreaPointers[area];
	if (prop == NO_PROPERTY) return 0;
	if (YSI_g_sProperties[prop][E_PROP_DATA_FLAGS] & e_PROP_FLAGS_TYPES == e_PROP_FLAGS_TYPE_RSRC)
	{
		if (!Property_GetOption(2, YSI_g_sProperties[prop][E_PROP_DATA_FLAGS]))
		{
			if (YSI_g_sProperties[prop][E_PROP_DATA_NAME][0]) Text_SendFormat(playerid, "YSI_FORBIDDEN_2", YSI_g_sProperties[prop][E_PROP_DATA_NAME]);
			else Text_Send(playerid, "YSI_FORBIDDEN");
			Bit_Set(YSI_g_sPlayerProperties[playerid], prop, 1);
		}
	}
	else Bit_Set(YSI_g_sPlayerProperties[playerid], prop, 1);
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerLeaveArea
Params:
	playerid - Player who left an area.
	areaid - Area they left.
Return:
	-
Notes:
	Internal callback from YSI_areas.
-*----------------------------------------------------------------------------*/

Property_OnPlayerLeaveArea(playerid, area)
{
	new
		prop = YSI_g_sAreaPointers[area];
	if (prop == NO_PROPERTY) return 0;
	if (YSI_g_sProperties[prop][E_PROP_DATA_FLAGS] & e_PROP_FLAGS_TYPES == e_PROP_FLAGS_TYPE_RSRC && Property_GetOption(2, YSI_g_sProperties[prop][E_PROP_DATA_FLAGS]))
	{
		Bit_Set(YSI_g_sPlayerProperties[playerid], prop, 1);
	}
	else Bit_Set(YSI_g_sPlayerProperties[playerid], prop, 0);
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerConnect
Params:
	playerid - Player who connected.
Return:
	-
Notes:
	Internal callback.
-*----------------------------------------------------------------------------*/

Property_OnPlayerConnect(playerid)
{
	YSI_g_sShopMenu[playerid][E_PROP_AMMU_MENU] = Menu:-1;
	Bit_SetAll(YSI_g_sPlayerProperties[playerid], 0);
	for (new i = 0; i < 13; i++) YSI_g_sSpawnWeapons[playerid][i] = 0;
	YSI_g_sMoney[playerid] = 0;
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerSpawn
Params:
	playerid - Player who spawned.
Return:
	-
Notes:
	Internal callback.
-*----------------------------------------------------------------------------*/

Property_OnPlayerSpawn(playerid)
{
	for (new i = 0; i < 12; i++) if (YSI_g_sSpawnWeapons[playerid][i]) GivePlayerWeapon(playerid, (YSI_g_sSpawnWeapons[playerid][i] >> 24) & 0xFF, YSI_g_sSpawnWeapons[playerid][i] & 0xFFFFFF);
	if (YSI_g_sSpawnWeapons[playerid][12]) SetPlayerArmour(playerid, 100.0);
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerLeaveCheckpointEx
Params:
	playerid - Player who left a checkpoint.
	cpid - Checkpoint they left.
Return:
	-
Notes:
	Internal callback from YSI_checkpoints.
-*----------------------------------------------------------------------------*/

#define Property_OnPlayerLeaveCheckpointEx Property_OnPlayerLeaveCP

Property_OnPlayerLeaveCheckpointEx(playerid, cpid)
{
	new
		cp = YSI_g_sCheckpointPointers[cpid];
	if (cp == NO_CHECKPOINT) return 0;
	new
		flags = YSI_g_sProperties[cp][E_PROP_DATA_FLAGS];
	if (((_:flags & _:e_PROP_FLAGS_TYPES) != _:e_PROP_FLAGS_TYPE_PROP || Property_GetOption(2, flags)) ?(!Bit_GetBit(YSI_g_sProperties[cp][E_PROP_DATA_PLAYERS], playerid)) : (!(_:YSI_g_sProperties[cp][E_PROP_DATA_PLAYERS][0] == playerid))) Bit_Set(YSI_g_sPlayerProperties[playerid], cp, 0);
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerEnterCheckpointEx
Params:
	playerid - Player who entered a checkpoint.
	cpid - Checkpoint they entered.
Return:
	-
Notes:
	Internal callback from YSI_checkpoints.
-*----------------------------------------------------------------------------*/

#define Property_OnPlayerEnterCheckpointEx Property_OnPlayerEnterCP

Property_OnPlayerEnterCheckpointEx(playerid, cpid)
{
	new
		prop = YSI_g_sCheckpointPointers[cpid];
	if (prop == NO_PROPERTY) return 0;
	new
		e_PROP_FLAGS:flag = YSI_g_sProperties[prop][E_PROP_DATA_FLAGS];
	switch (flag & e_PROP_FLAGS_TYPES)
	{
		case e_PROP_FLAGS_TYPE_PROP:
		{
			Text_SendFormat(playerid, "YSI_PROP_NAME", YSI_g_sProperties[prop][E_PROP_DATA_NAME], ((YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] >>> 6) & 0x03FFF000) | ((YSI_g_sProperties[prop][E_PROP_DATA_DATA_1] >>> 20) & 0x00000FFF), YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] & 0x3FFFF);
			if (Property_GetOption(2, flag))
			{
				if (Bit_GetBit(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS], playerid)) Text_Send(playerid, "YSI_PROP_YOURS");
				else Text_SendFormat(playerid, "YSI_PROP_BUY", "buy");
			}
			else
			{
				new
					owner = _:YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0];
				if (owner == playerid) Text_Send(playerid, "YSI_PROP_YOURS");
				else if (IsPlayerConnected(owner)) Text_SendFormat(playerid, "YSI_PROP_OWNER", ReturnPlayerName(owner), owner);
				if (owner != playerid) Text_SendFormat(playerid, "YSI_PROP_BUY", "buy");
			}
		}
		case e_PROP_FLAGS_TYPE_AMMU:
		{
			Property_GenerateAmmuMenu(playerid, prop, 0, 0);
			TogglePlayerControllable(playerid, 0);
		}
		case e_PROP_FLAGS_TYPE_BANK:
		{
			if (YSI_g_sProperties[prop][E_PROP_DATA_NAME][0]) Text_SendFormat(playerid, "YSI_BANK_NAME", YSI_g_sProperties[prop][E_PROP_DATA_NAME]);
			else Text_Send(playerid, "YSI_BANK_WELCOME");
			Text_SendFormat(playerid, "YSI_BANK_HELP1", "bank");
			Text_SendFormat(playerid, "YSI_BANK_HELP2", "withdraw");
			Text_SendFormat(playerid, "YSI_BANK_HELP3", "balance");
			Text_SendFormat(playerid, "YSI_BANK_BALANCE", YSI_g_sMoney[playerid]);
		}
		case e_PROP_FLAGS_TYPE_TELS:
		{
			if (GetPlayerMoney(playerid) >= YSI_g_sProperties[prop][E_PROP_DATA_DATA_1])
			{
				GivePlayerMoney(playerid, -YSI_g_sProperties[prop][E_PROP_DATA_DATA_1]);
				SetPlayerPos(playerid, Float:YSI_g_sProperties[prop][E_PROP_DATA_DATA_2], Float:YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0], Float:YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][1]);
				if (YSI_g_sProperties[prop][E_PROP_DATA_NAME][0]) Text_SendFormat(playerid, "YSI_TELS_NAME", YSI_g_sProperties[prop][E_PROP_DATA_NAME]);
				else Text_Send(playerid, "YSI_TELS_TELE");
			}
			else Text_SendFormat(playerid, "YSI_TELS_MONEY", YSI_g_sProperties[prop][E_PROP_DATA_DATA_1]);
			return 1;
		}
		case e_PROP_FLAGS_TYPE_MONP: {}
		default: return 0;
	}
	Bit_Set(YSI_g_sPlayerProperties[playerid], prop, 1);
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_GetWeapon
Params:
	ammu - Ammunation to get the weapon from.
	slot - Number of active slots to skip.
	&page - Start slot.
Return:
	-
Notes:
	Gets the slot active weapon from page start.  If you want the first one
	use page = 0 and slot = 0.  If you want the second use page = 0 and slot
	= 1 or slot = 0 and page > position of first.
-*----------------------------------------------------------------------------*/

static stock Property_GetWeapon(ammu, slot, &page = 0)
{
	new
		weapon;
	while (page < 40)
	{
		if (!page)
		{
			weapon = YSI_g_sProperties[ammu][E_PROP_DATA_DATA_1];
		}
		else if (page == 1)
		{
			weapon = YSI_g_sProperties[ammu][E_PROP_DATA_DATA_2];
		}
		else if (page < PLAYER_BIT_ARRAY + 2)
		{
			weapon = _:YSI_g_sProperties[ammu][E_PROP_DATA_PLAYERS][page - 2];
		}
		else
		{
			weapon = YSI_g_sProperties[ammu][E_PROP_DATA_NAME][page - (PLAYER_BIT_ARRAY + 2)];
		}
		if (weapon)
		{
			if (!slot) return weapon;
			else slot--;
		}
		page++;
	}
	return 0;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_WeaponName
Params:
	weapon - Weapon slot to get the name of.
Return:
	-
Notes:
	Gets a real weapon name based on the slot of the weapon, not the weapon id.
-*----------------------------------------------------------------------------*/

static stock Property_WeaponName(weapon)
{
	static
		names[][18] = 
		{
			"Brassknuckle",			"Golfclub",		"Night Stick",		"Knife",
			"Bat",					"Shovel",		"Poolstick",		"Katana",
			"Chainsaw",				"Dildo",		"Dildo 2",			"Vibrator",
			"Vibrator 2",			"Flower",		"Cane",				"Grenade",
			"Teargas",				"Molotov",		"Colt 45",			"Silenced Pistol",
			"Desert Eagle",			"Shotgun",		"Sawnoff",			"Spaz 9",
			"Uzi",					"MP5",			"AK47",				"M4",
			"TEC9",					"Rifle",		"Sniper Rifle",		"Rocket Launcher",
			"Flame Thrower",		"Minigun",		"Satchel Charge",	"Spraycan",
			"Fire Extinguisher",	"Camera",		"Parachute",		"Armour"
		},
		none[18] = "NONE";
	if (weapon >=0 && weapon < sizeof (names)) return names[weapon];
	else return none;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_WeaponCost
Params:
	data - Compressed data.
Return:
	-
Notes:
	Extracts the cost from the passed data.
-*----------------------------------------------------------------------------*/

#define Property_WeaponCost(%1) \
	((%1) & 0xFFFFF)

/*----------------------------------------------------------------------------*-
Function:
	Property_WeaponAmmo
Params:
	data = Compressed data.
Return:
	-
Notes:
	Extracts the ammo from the passed data.
-*----------------------------------------------------------------------------*/

#define Property_WeaponAmmo(%1) \
	(((%1) >> 20) & 0xFFF)

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerExitedMenu
Params:
	playerid - Player who left a menu.
Return:
	-
Notes:
	Internal callback.
-*----------------------------------------------------------------------------*/

#define Property_OnPlayerExitedMenu Property_OnPlayerExited

Property_OnPlayerExitedMenu(playerid)
{
	new
		data = YSI_g_sShopMenu[playerid][E_PROP_AMMU_DATA];
	if (data & 0x02)
	{
		if (data & 0x01)
		{
			Property_GenerateAmmuMenu(playerid, data >> 16, 0, 0);
			return 1;
		}
	}
	YSI_g_sShopMenu[playerid][E_PROP_AMMU_MENU] = Menu:-1;
	TogglePlayerControllable(playerid, 1);
	YSI_g_sShopMenu[playerid][E_PROP_AMMU_DATA] = 0;
	return 0;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_GetWeaponFromSlot
Params:
	slot - Data slot to translate.
Return:
	weaponid represented byt that slot.
Notes:
	-
-*----------------------------------------------------------------------------*/

static stock Property_GetWeaponFromSlot(slot)
{
	if (slot < 18) return slot + 1;
	else if (slot < 33) return slot + 4;
	else if (slot < 36) return slot + 5;
	else if (slot < 38) return slot + 6;
	else if (slot == 38) return WEAPON_PARACHUTE;
	else if (slot == 39) return WEAPON_ARMOUR;
	else return 0;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_SavePlayerWeapon
Params:
	playerid - Player to save the weapon for.
	weaponslot - Slot of the weapon.
	ammo - Ammo to save.
Return:
	-
Notes:
	Saves weapons based on slots so you only have 13 spawn weapons based on
	real weapon slots (armour is slot 12).  This is similar to weapon slot
	sorting but it's sorting slots which are packed from original weapon
	numbers and missing some.
-*----------------------------------------------------------------------------*/

stock Property_SavePlayerWeapon(playerid, weaponslot, ammo)
{
	new
		slot,
		weapon;
	switch (weaponslot)
	{
		case 0: slot = 0, weapon = WEAPON(WEAPON_BRASSKNUCKLE);
		case 1, 2, 3, 4, 5, 6, 7, 8: slot = 1, weapon = WEAPON(weaponslot + 1);
		case 18, 19, 20: slot = 2, weapon = WEAPON(weaponslot + 4);
		case 21, 22, 23: slot = 3, weapon = WEAPON(weaponslot + 4);
		case 24, 25, 28: slot = 4, weapon = WEAPON(weaponslot + 4);
		case 26, 27: slot = 5, weapon = WEAPON(weaponslot + 4);
		case 29, 30: slot = 6, weapon = WEAPON(weaponslot + 4);
		case 31: slot = 7, weapon = WEAPON(WEAPON_ROCKETLAUNCHER); 
		case 32, 33: slot = 7, weapon = WEAPON(weaponslot + 5);
		case 15, 16, 17: slot = 8, weapon = WEAPON(weaponslot + 1);
		case 34: slot = 8, weapon = WEAPON(WEAPON_SATCHEL);
		case 35, 36, 37: slot = 9, weapon = WEAPON(weaponslot + 6);
		case 9, 10, 11, 12, 13, 14: slot = 10, weapon = WEAPON(weaponslot + 1);
		case 38: slot = 11, weapon = WEAPON(WEAPON_PARACHUTE);
		case 39: slot = 12, weapon = 1;
		default: return;
	}
	if (YSI_g_sSpawnWeapons[playerid][slot] & 0xFF000000 == weapon) YSI_g_sSpawnWeapons[playerid][slot] += ammo & 0x00FFFFFF;
	else YSI_g_sSpawnWeapons[playerid][slot] = (weapon & 0xFF000000) | (ammo & 0x00FFFFFF);
}

/*----------------------------------------------------------------------------*-
Function:
	Property_SaveWeapons
Params:
	playerid - Player to save weapons for.
Return:
	-
Notes:
	Saves a players current spawn weapon data to an ini (i.e. their save file).
-*----------------------------------------------------------------------------*/

stock Property_SaveWeapons(playerid)
{
	for (new i = 0; i <= 12; i++)
	{
		new
			str[8] = "wslot";
		if (i >= 10)
		{
			new
				tens = i / 10;
			str[5] = tens + '0';
			str[6] = (i - (tens * 10)) + '0';
			str[7] = '\0';
		}
		else
		{
			str[5] = i + '0';
			str[6] = '\0';
		}
		Player_WriteInt(str, YSI_g_sSpawnWeapons[playerid][i]);
	}
}

/*----------------------------------------------------------------------------*-
Function:
	Property_LoadWeapons
Params:
	playerid - Player to load weapons for.
	identifier[] - Slot data.
	text[] - Weapon data.
Return:
	-
Notes:
	Called when a player logs in to load their previous weapons.
-*----------------------------------------------------------------------------*/

stock Property_LoadWeapons(playerid, identifier[], text[])
{
	if (strcmp(identifier, "wslot", false, 5)) return;
	new
		slot = strval(identifier[5]);
	if (slot < 0 || slot > 12) return;
	YSI_g_sSpawnWeapons[playerid][slot] = strval(text);
}

/*----------------------------------------------------------------------------*-
Function:
	Property_SaveBank
Params:
	playerid - Player to save bank for.
Return:
	-
Notes:
	Saves a players current bank data to an ini (i.e. their save file).
-*----------------------------------------------------------------------------*/

stock Property_SaveBank(playerid)
{
	Player_WriteInt("bankmoney", YSI_g_sMoney[playerid]);
}

/*----------------------------------------------------------------------------*-
Function:
	Property_LoadBank
Params:
	playerid - Player to load bank for.
	identifier[] - Identifier name to check.
	text[] - Possibly bank amount.
Return:
	-
Notes:
	Called when a player logs in to load their previous banked money.
-*----------------------------------------------------------------------------*/

stock Property_LoadBank(playerid, identifier[], text[])
{
	if (!strcmp(identifier, "bankmoney")) YSI_g_sMoney[playerid] = strval(text);
}

/*----------------------------------------------------------------------------*-
Function:
	Property_OnPlayerSelectedMenuRow
Params:
	playerid - Player who selected a row.
	row - Row they selected.
Return:
	-
Notes:
	Internal callback.
-*----------------------------------------------------------------------------*/

#define Property_OnPlayerSelectedMenuRow Property_OnPlayerSelectedRow

Property_OnPlayerSelectedMenuRow(playerid, row)
{
	new
		data = YSI_g_sShopMenu[playerid][E_PROP_AMMU_DATA];
	if (data & 0x02)
	{
		new
			slot = (data >> 8) & 0xFF,
			ammu = data >> 16,
			count = YSI_g_sProperties[ammu][E_PROP_DATA_NAME][MAX_PROP_NAME - 1];
		if (data & 0x01)
		{
			new
				weapon,
				flags = _:YSI_g_sProperties[ammu][E_PROP_DATA_FLAGS],
				wdat;
			wdat = Property_GetWeapon(ammu, slot, weapon);
			if (GetPlayerMoney(playerid) >= Property_WeaponCost(wdat))
			{
				if (Property_GetOption(1, flags)) Property_SavePlayerWeapon(playerid, weapon, Property_WeaponAmmo(wdat));
				if (Property_GetOption(2, flags)) Property_GivePlayerWeapon(playerid, weapon, Property_WeaponAmmo(wdat));
				GivePlayerMoney(playerid, 0 - Property_WeaponCost(wdat));
			}
			else Text_Send(playerid, "YSI_AMMU_NO_MONEY");
			Property_GenerateAmmuMenu(playerid, ammu, 0, 0);
		}
		else
		{
			if (count > slot && row == 11) Property_GenerateAmmuMenu(playerid, ammu, 0, slot);
			else if (count > 12)
			{
				if (slot > 11 && row == 0) Property_GenerateAmmuMenu(playerid, ammu, 0, ((slot - 12) / 11) * 11);
				else Property_GenerateAmmuMenu(playerid, ammu, 1, ((slot - 1) / 11) * 10 + row);
			}
			else
			{
				Property_GenerateAmmuMenu(playerid, ammu, 1, row);
			}
		}
		return 1;
	}
	else YSI_g_sShopMenu[playerid][E_PROP_AMMU_DATA] = 0;
	return 0;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_GenerateAmmuMenu
Params:
	playerid - Player who watns the menu.
	ammu - Ammunation they're in.
	stage - Current menu view if any.
	page - Current menu page.
Return:
	-
Notes:
	If slot is 0 the main selection will be displayed and page will represent
	the offset from the start if there are more than 12 weapons for sale.  If
	stage is 1 the individual confirmation menu for one weapon will show and
	page will determine which weapon to show.
-*----------------------------------------------------------------------------*/

static stock Property_GenerateAmmuMenu(playerid, ammu, stage, page)
{
	new
		Menu:menu = CreateMenu("Ammunation", 2, 270.0, 150.0, 200.0, 50.0),
		slot = page;
	SetMenuColumnHeader(menu, 0, "Weapon");
	SetMenuColumnHeader(menu, 1, "Price");
	if (stage)
	{
		new
			start,
			weapon = Property_GetWeapon(ammu, page, start);
		if (weapon)
		{
			AddMenuItem(menu, 0, Property_WeaponName(start));
			AddMenuItem(menu, 1, numstr(Property_WeaponCost(weapon)));
		}
	}
	else
	{
		new
			weapon,
			start,
			row,
			end = page + 12,
			next = page,
			money = GetPlayerMoney(playerid);
		if (YSI_g_sProperties[ammu][E_PROP_DATA_NAME][MAX_PROP_NAME - 1] - page > 12) end--;
		if (YSI_g_sProperties[ammu][E_PROP_DATA_NAME][MAX_PROP_NAME - 1] > 12 && page)
		{
			end--;
			AddMenuItem(menu, 0, "Back...");
			AddMenuItem(menu, 1, " ");
		}
		while (slot < end && (weapon = Property_GetWeapon(ammu, next, start)))
		{
			new
				cost = Property_WeaponCost(weapon);
			AddMenuItem(menu, 0, Property_WeaponName(start));
			AddMenuItem(menu, 1, numstr(cost));
			if (cost > money) DisableMenuRow(menu, row);
			slot++;
			row++;
			start++;
			next = 0;
		}
		if (end != 12 && Property_GetWeapon(ammu, 0, start))
		{
			AddMenuItem(menu, 0, "Next...");
			AddMenuItem(menu, 1, " ");
		}
	}
	YSI_g_sShopMenu[playerid][E_PROP_AMMU_MENU] = menu;
	YSI_g_sShopMenu[playerid][E_PROP_AMMU_DATA] = ((ammu & 0xFFFF) << 16) | ((slot & 0xFF) << 8) | (stage ? 1 : 0) | 2;
	ShowMenuForPlayer(menu, playerid);
	DestroyMenu(menu);
}

/*----------------------------------------------------------------------------*-
Command:
	sell
Parameters:
	<id> - Property to sell.
Notes:
	Sells a property you own if it can be sold.
-*----------------------------------------------------------------------------*/

public ycmd_sell(playerid, params[], help)
{
	if (help)
	{
		Text_Send(playerid, "YSI_SELL_HELP_1");
		Text_Send(playerid, "YSI_SELL_HELP_2");
		Text_Send(playerid, "YSI_SELL_HELP_3");
		Text_Send(playerid, "YSI_SELL_HELP_4");
	}
	else
	{
		if (params[0] >= '0' && params[0] <= '9')
		{
			new
				prop = strval(params);
			if (Property_IsActive(prop))
			{
				new
					e_PROP_FLAGS:flag = YSI_g_sProperties[prop][E_PROP_DATA_FLAGS];
				if ((flag & e_PROP_FLAGS_TYPES) == e_PROP_FLAGS_TYPE_PROP)
				{
					if (Property_GetOption(1, flag))
					{
						if (Property_GetOption(2, flag))
						{
							if (Bit_GetBit(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS], playerid)) Bit_Set(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS], playerid, 0);
							else return Text_Send(playerid, "YSI_SELL_CANT");
						}
						else
						{
							if (_:YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0] == playerid) YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0] = Bit:INVALID_PLAYER_ID;
							else return Text_Send(playerid, "YSI_SELL_CANT");
						}
						new
							cost = ((((YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] >>> 6) & 0x03FFF000) | ((YSI_g_sProperties[prop][E_PROP_DATA_DATA_1] >>> 20) & 0x00000FFF)) * ((Property_GetOption(3, flag)) ? PROPERTY_SELL_PERCENT : 100)) / 100;
						Text_SendFormat(playerid, "YSI_SELL_SOLD", cost);
						Checkpoint_SetVisible(_:(flag & e_PROP_FLAGS_LINK), 1);
						GivePlayerMoney(playerid, cost);
						Bit_Set(YSI_g_sPlayerProperties[playerid], prop, 0);
					}
					else Text_Send(playerid, "YSI_SELL_CANT");
					return 1;
				}
			}
		}
		Text_Send(playerid, "YSI_SELL_NO_ID");
	}
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_Bank
Params:
	playerid - Player who is banking money.
	amount - Amount of money they're banking.
Return:
	-
Notes:
	Does banks and withdrawls.
-*----------------------------------------------------------------------------*/

stock Property_Bank(playerid, amount)
{
	if (IsPlayerInCheckpoint(playerid))
	{
		new
			cpid = Checkpoint_Get(playerid),
			prop = YSI_g_sCheckpointPointers[cpid];
		if (prop != NO_PROPERTY)
		{
			new
				e_PROP_FLAGS:flag = YSI_g_sProperties[prop][E_PROP_DATA_FLAGS];
			if (flag & e_PROP_FLAGS_TYPES == e_PROP_FLAGS_TYPE_BANK)
			{
				if ((amount > 0) ? (GetPlayerMoney(playerid) >= amount) : (YSI_g_sMoney[playerid] + amount >= 0))
				{
					YSI_g_sMoney[playerid] += amount;
					GivePlayerMoney(playerid, -amount);
				}
				else return 0;
			}
			else Text_Send(playerid, "YSI_BANK_NOT");
		}
		else Text_Send(playerid, "YSI_BANK_NOT");
	}
	else Text_Send(playerid, "YSI_BANK_CP");
	return 1;
}

/*----------------------------------------------------------------------------*-
Command:
	bank
Parameters:
	<amount> - Value to bank.
Notes:
	Calls Property_Bank with positive input amount.
-*----------------------------------------------------------------------------*/

public ycmd_bank(playerid, params[], help)
{
	if (help)
	{
		Text_Send(playerid, "YSI_BANK_HELP_1");
		Text_Send(playerid, "YSI_BANK_HELP_2");
		Text_Send(playerid, "YSI_BANK_HELP_3");
		Text_SendFormat(playerid, "YSI_BANK_HELP_4", "bank");
	}
	else
	{
		new
			bank = strval(params);
		if (Property_Bank(playerid, bank)) Text_SendFormat(playerid, "YSI_BANK_BANKED", bank, YSI_g_sMoney[playerid]);
		else Text_Send(playerid, "YSI_BANK_INSUFFICIENT");
	}
	return 1;
}

/*----------------------------------------------------------------------------*-
Command:
	balance
Parameters:
	-
Notes:
	Displays how much you have in the bank.  Does not require you to be in a
	bank.
-*----------------------------------------------------------------------------*/

public ycmd_balance(playerid, params[], help)
{
	if (help) Text_Send(playerid, "YSI_BAL_HELP");
	else Text_SendFormat(playerid, "YSI_BANK_BALANCE", YSI_g_sMoney[playerid]);
	return 1;
}

/*----------------------------------------------------------------------------*-
Command:
	withdraw
Parameters:
	<amount> - Amount to withdraw.
Notes:
	Calls Property_Bank with negative input amount.
-*----------------------------------------------------------------------------*/

public ycmd_withdraw(playerid, params[], help)
{
	if (help)
	{
		Text_Send(playerid, "YSI_WITH_HELP1");
		Text_Send(playerid, "YSI_WITH_HELP2");
		Text_Send(playerid, "YSI_WITH_HELP3");
		Text_SendFormat(playerid, "YSI_WITH_HELP4", "withdraw");
	}
	else
	{
		new
			bank = -strval(params);
		if (Property_Bank(playerid, bank)) Text_SendFormat(playerid, "YSI_WITHDREW", bank, YSI_g_sMoney[playerid]);
		else Text_Send(playerid, "YSI_BANK_INSUFFUCUENT");
	}
	return 1;
}

/*----------------------------------------------------------------------------*-
Command:
	buy
Parameters:
	-
Notes:
	Allows you to purchase the property you are at.
-*----------------------------------------------------------------------------*/

public ycmd_buy(playerid, params[], help)
{
	if (help)
	{
		Text_Send(playerid, "YSI_BUY_HELP_1");
		Text_Send(playerid, "YSI_BUY_HELP_2");
		Text_Send(playerid, "YSI_BUY_HELP_3");
		Text_Send(playerid, "YSI_BUY_HELP_4");
	}
	else
	{
		if (IsPlayerInCheckpoint(playerid))
		{
			new
				cpid = Checkpoint_Get(playerid),
				prop = YSI_g_sCheckpointPointers[cpid];
			if (prop != NO_PROPERTY)
			{
				new
					e_PROP_FLAGS:flag = YSI_g_sProperties[prop][E_PROP_DATA_FLAGS];
				if (flag & e_PROP_FLAGS_TYPES == e_PROP_FLAGS_TYPE_PROP)
				{
					new
						price = ((YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] >>> 6) & 0x03FFF000) | ((YSI_g_sProperties[prop][E_PROP_DATA_DATA_1] >>> 20) & 0x00000FFF);
					if (GetPlayerMoney(playerid) >= price)
					{
						if (Property_GetOption(2, flag))
						{
							if (Bit_GetBit(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS], playerid))
							{
								Text_Send(playerid, "YSI_PROP_OWN");
								return 1;
							}
							Bit_Set(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS], playerid, 1);
						}
						else
						{
							new
								owner = _:YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0];
							if (owner == playerid)
							{
								Text_Send(playerid, "YSI_PROP_OWN");
								return 1;
							}
							if (IsPlayerConnected(owner))
							{
								GivePlayerMoney(owner, price);
								Text_SendFormat(owner, "YSI_PROP_OUT", YSI_g_sProperties[prop][E_PROP_DATA_NAME], ReturnPlayerName(playerid), playerid);
							}
							YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0] = Bit:playerid;
							if (Property_GetOption(1, flag)) Checkpoint_SetVisible(cpid, 0);
						}
						GivePlayerMoney(playerid, -price);
						Text_SendFormat(playerid, "YSI_PROP_BOUGHT", YSI_g_sProperties[prop][E_PROP_DATA_NAME], price);
						if (Property_GetOption(4, flag))
						{
							YSI_g_sProperties[prop][E_PROP_DATA_DATA_1] &= 0xFFFFF;
							YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] &= 0x3FFFF;
							price *= PROPERTY_INCREASE_PERCENT;
							price /= 100;
							YSI_g_sProperties[prop][E_PROP_DATA_DATA_1] |= ((price & 0x00000FFF) << 20);
							YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] |= ((price & 0x03FFF000) << 6);
						}
						if (Property_GetOption(1, flag)) Text_SendFormat(playerid, "YSI_PROP_SELL", "sell", prop, (price * ((Property_GetOption(3, flag)) ? PROPERTY_SELL_PERCENT : 100)) / 100);
					}
					else Text_Send(playerid, "YSI_PROP_AFFORD");
				}
				else Text_Send(playerid, "YSI_PROP_NOT");
			}
			else Text_Send(playerid, "YSI_PROP_NOT");
		}
		else Text_Send(playerid, "YSI_PROP_CP");
	}
	return 1;
	#pragma unused params
}

/*----------------------------------------------------------------------------*-
Function:
	Property_Loop
Params:
	-
Return:
	-
Notes:
	Does the main processing for the library.  Removes or kills people in areas
	they shouldn't be and gives out money to people who earnt it.
-*----------------------------------------------------------------------------*/

public Property_Loop()
{
	static
		Float:s_fLastGoodPos[MAX_PLAYERS][3],
		sLastTick = 0;
	new
		currentTick = GetTickCount(),
		elapse = currentTick - sLastTick;
	for (new i = 0; i < MAX_PROPERTIES; i++)
	{
		new
			flags = YSI_g_sProperties[i][E_PROP_DATA_FLAGS];
		if (flags & _:e_PROP_FLAGS_ACTIVE)
		{
			switch (flags & _:e_PROP_FLAGS_TYPES)
			{
				case e_PROP_FLAGS_TYPE_MONP, e_PROP_FLAGS_TYPE_MONA:
				{
					new
						time = YSI_g_sProperties[i][E_PROP_DATA_DATA_2];
					if (!time) time = YSI_g_sProperties[i][E_PROP_DATA_NAME][0];
					time -= elapse;
					if (time < 0) time = 0;
					YSI_g_sProperties[i][E_PROP_DATA_DATA_2] = time;
				}
				case e_PROP_FLAGS_TYPE_PROP:
				{
					new
						time = YSI_g_sProperties[i][E_PROP_DATA_NAME][MAX_PROP_NAME - 1];
					if (!time) time = YSI_g_sProperties[i][E_PROP_DATA_DATA_1] & 0x000FFFFF;
					time -= elapse;
					if (time <= 0) time = 0;
					YSI_g_sProperties[i][E_PROP_DATA_NAME][MAX_PROP_NAME - 1] = time;
				}
			}
		}
	}
	for (new i = 0; i < MAX_PLAYERS; i++)
	{
		if (!IsPlayerConnected(i)) continue;
		new
			money,
			bad;
		for (new j = 0; j < Bit_Bits(MAX_PROPERTIES); j++)
		{
			new
				props = _:YSI_g_sPlayerProperties[i][j],
				slot = 1,
				bit;
			while (props)
			{
				if (props & slot)
				{
					new
						prop = (j * 32) + bit,
						flags = YSI_g_sProperties[prop][E_PROP_DATA_FLAGS];
					if (flags & _:e_PROP_FLAGS_ACTIVE)
					{
						switch (flags & _:e_PROP_FLAGS_TYPES)
						{
							case e_PROP_FLAGS_TYPE_MONP, e_PROP_FLAGS_TYPE_MONA:
								if (!YSI_g_sProperties[prop][E_PROP_DATA_DATA_2]) GivePlayerMoney(i, YSI_g_sProperties[prop][E_PROP_DATA_DATA_1]);
							case e_PROP_FLAGS_TYPE_PROP:
							{
								if (((Property_GetOption(2, flags)) ? (_:Bit_GetBit(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS], i)) : (_:(YSI_g_sProperties[prop][E_PROP_DATA_PLAYERS][0] == Bit:i))) && !YSI_g_sProperties[prop][E_PROP_DATA_NAME][MAX_PROP_NAME - 1]) money += YSI_g_sProperties[prop][E_PROP_DATA_DATA_2] & 0x3FFFF;
							}
							case e_PROP_FLAGS_TYPE_RSRC:
							{
								if (Property_GetOption(1, flags))
								{
									SetPlayerPos(i, s_fLastGoodPos[i][0], s_fLastGoodPos[i][1], s_fLastGoodPos[i][2]);
									bad = 1;
								}
								else
								{
									new Float:health;
									GetPlayerHealth(i, health);
									SetPlayerHealth(i, health - YSI_g_sProperties[prop][E_PROP_DATA_DATA_2]);
								}
							}
						}
					}
					props ^= slot;
				}
				slot <<= 1;
				bit++;
			}
		}
		if (money)
		{
			Text_SendFormat(i, "YSI_PROP_EARNT", money);
			GivePlayerMoney(i, money);
		}
		if (!bad) GetPlayerPos(i, s_fLastGoodPos[i][0], s_fLastGoodPos[i][1], s_fLastGoodPos[i][2]);
	}
	sLastTick = currentTick;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_IsPlayerProperty
Params:
	flags - Property data to check.
	playerid - Player to check for.
Return:
	-
Notes:
	-
-*----------------------------------------------------------------------------*/

#define Property_IsPlayerProperty(%1,%2) \
	((%1) & e_PROP_FLAGS_ACTIVE && (%1) & e_PROP_FLAGS_TYPES == e_PROP_FLAGS_TYPE_PROP && Checkpoint_HasPlayerNoWorld(_:((%1) & e_PROP_FLAGS_LINK), (%2)))

/*----------------------------------------------------------------------------*-
Function:
	Property_GetPlayerPropCount
Params:
	playerid - Player to count for.
Return:
	-
Notes:
	Gets the number of properties this player could theoretically own.
-*----------------------------------------------------------------------------*/

stock Property_GetPlayerPropCount(playerid)
{
	new
		count;
	for (new i = 0; i < MAX_PROPERTIES; i++)
	{
		new
			e_PROP_FLAGS:flags = YSI_g_sProperties[i][E_PROP_DATA_FLAGS];
		if (Property_IsPlayerProperty(flags, playerid)) count++;
	}
	return count;
}

/*----------------------------------------------------------------------------*-
Command:
	properties
Parameters:
	<page> - Page of properties to view (optional).
Notes:
	Lista all properties available to a player and who owns them.
-*----------------------------------------------------------------------------*/

public ycmd_properties(playerid, params[], help)
{
	if (help)
	{
		Text_Send(playerid, "YSI_LIST_HELP_1");
		Text_Send(playerid, "YSI_LIST_HELP_2");
		Text_Send(playerid, "YSI_LIST_HELP_3");
		return 1;
	}
	new
		props = Property_GetPlayerPropCount(playerid),
		pages = (props + 7) / 8,
		page = strval(params);
	if (props > 8)
	{
		if (page)
		{
			if (page <= pages)
			{
				for (new i = 0, j = 0, k = (page - 1) * 8, n = k + 8; i < MAX_PROPERTIES && j < n; i++)
				{
					new
						e_PROP_FLAGS:flags = YSI_g_sProperties[i][E_PROP_DATA_FLAGS];
					if (Property_IsPlayerProperty(flags, playerid))
					{
						if (j >= k)
						{
							if (Property_GetOption(2, flags)) Text_SendFormat(playerid, "YSI_LIST_MULTI", YSI_g_sProperties[i][E_PROP_DATA_NAME], Bit_GetCount(YSI_g_sProperties[i][E_PROP_DATA_PLAYERS], PLAYER_BIT_ARRAY));
							else Text_SendFormat(playerid, "YSI_LIST_FORM", YSI_g_sProperties[i][E_PROP_DATA_NAME], ReturnPlayerName(_:YSI_g_sProperties[i][E_PROP_DATA_PLAYERS][0]));
						}
						j++;
					}
				}
			}
			else Text_SendFormat(playerid, "YSI_LIST_PAGES", "properties", pages);
		}
		else
		{
			Text_Send(playerid, "YSI_LIST_MORE");
			Text_SendFormat(playerid, "YSI_LIST_PAGES", "properties", pages);
		}
	}
	else if (props)
	{
		for (new j = 0, i = 0; i < props && j < MAX_PROPERTIES; j++)
		{
			new
				e_PROP_FLAGS:flags = YSI_g_sProperties[j][E_PROP_DATA_FLAGS];
			if (Property_IsPlayerProperty(flags, playerid))
			{
				if (Property_GetOption(2, flags)) Text_SendFormat(playerid, "YSI_LIST_MULTI", YSI_g_sProperties[j][E_PROP_DATA_NAME], Bit_GetCount(YSI_g_sProperties[j][E_PROP_DATA_PLAYERS], PLAYER_BIT_ARRAY));
				else Text_SendFormat(playerid, "YSI_LIST_FORM", YSI_g_sProperties[j][E_PROP_DATA_NAME], ReturnPlayerName(_:YSI_g_sProperties[j][E_PROP_DATA_PLAYERS][0]));
				i++;
			}
		}
	}
	else Text_Send(playerid, "YSI_LIST_NONE");
	return 1;
}

/*----------------------------------------------------------------------------*-
Function:
	Property_GetLink
Params:
	property - Property to get link of.
Return:
	-
Notes:
	Returns a reference to the area or checkpoint used by this property or
	NO_PROPERTY on fail.
-*----------------------------------------------------------------------------*/

stock Property_GetLink(property)
{
	if (Property_IsActive(property)) return _:(YSI_g_sProperties[property][E_PROP_DATA_FLAGS] & e_PROP_FLAGS_LINK);
	return NO_PROPERTY;
}
