/*
This work is licensed under the Creative Commons Attribution- NonCommercial 4.0 International License. To view a copy
of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative Commons, 444 Castro
Street, Suite 900, Mountain View, California, 94041, USA.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/* ******* DesnoGuns Mod by Desno365 ******* */

const DEBUG1 = false; // debug: loading boolean saved at startup; sounds correctly installed; Sound.playFromPath function (start and finish)
const DEBUG2 = false;
const TAG = "DesnoGuns";

// updates variables
const CURRENT_VERSION = "r012";
var latestVersion;

//activity and other Android variables
var currentActivity = com.mojang.minecraftpe.MainActivity.currentMainActivity.get();
var sdcard = android.os.Environment.getExternalStorageDirectory();

// minecraft variables
const GameMode = {
	SURVIVAL: 0,
	CREATIVE: 1
};
const ITEM_CATEGORY_TOOL = 3; // 3 seems to be the category of the tools
const VEL_Y_OFFSET = -0.07840000092983246;
var isInGame = false;
var players;

// textures variables
var textureUiShowed = false;

// display size and density variables
var metrics = new android.util.DisplayMetrics();
currentActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
var displayHeight = metrics.heightPixels;
var displayWidth = metrics.widthPixels;
var deviceDensity = metrics.density;
if(displayHeight > displayWidth) // fix auto-rotation disabled bug
{
	var x = displayHeight;
	displayHeight = displayWidth;
	displayWidth = x;
}
metrics = null;

// tip messages displayed variables
var displayedMessageNoSound = false;

// change carried item variables
var previousCarriedItem = 0;
var previousSlotId = 0;

// images in base64
var sightPng = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAACjFpQ0NQSUNDIFByb2ZpbGUAAEiJnZZ3VFPZFofPvTe9UJIQipTQa2hSAkgNvUiRLioxCRBKwJAAIjZEVHBEUZGmCDIo4ICjQ5GxIoqFAVGx6wQZRNRxcBQblklkrRnfvHnvzZvfH/d+a5+9z91n733WugCQ/IMFwkxYCYAMoVgU4efFiI2LZ2AHAQzwAANsAOBws7NCFvhGApkCfNiMbJkT+Be9ug4g+fsq0z+MwQD/n5S5WSIxAFCYjOfy+NlcGRfJOD1XnCW3T8mYtjRNzjBKziJZgjJWk3PyLFt89pllDznzMoQ8GctzzuJl8OTcJ+ONORK+jJFgGRfnCPi5Mr4mY4N0SYZAxm/ksRl8TjYAKJLcLuZzU2RsLWOSKDKCLeN5AOBIyV/w0i9YzM8Tyw/FzsxaLhIkp4gZJlxTho2TE4vhz89N54vFzDAON40j4jHYmRlZHOFyAGbP/FkUeW0ZsiI72Dg5ODBtLW2+KNR/Xfybkvd2ll6Ef+4ZRB/4w/ZXfpkNALCmZbXZ+odtaRUAXesBULv9h81gLwCKsr51Dn1xHrp8XlLE4ixnK6vc3FxLAZ9rKS/o7/qfDn9DX3zPUr7d7+VhePOTOJJ0MUNeN25meqZExMjO4nD5DOafh/gfB/51HhYR/CS+iC+URUTLpkwgTJa1W8gTiAWZQoZA+J+a+A/D/qTZuZaJ2vgR0JZYAqUhGkB+HgAoKhEgCXtkK9DvfQvGRwP5zYvRmZid+8+C/n1XuEz+yBYkf45jR0QyuBJRzuya/FoCNCAARUAD6kAb6AMTwAS2wBG4AA/gAwJBKIgEcWAx4IIUkAFEIBcUgLWgGJSCrWAnqAZ1oBE0gzZwGHSBY+A0OAcugctgBNwBUjAOnoAp8ArMQBCEhcgQFVKHdCBDyByyhViQG+QDBUMRUByUCCVDQkgCFUDroFKoHKqG6qFm6FvoKHQaugANQ7egUWgS+hV6ByMwCabBWrARbAWzYE84CI6EF8HJ8DI4Hy6Ct8CVcAN8EO6ET8OX4BFYCj+BpxGAEBE6ooswERbCRkKReCQJESGrkBKkAmlA2pAepB+5ikiRp8hbFAZFRTFQTJQLyh8VheKilqFWoTajqlEHUJ2oPtRV1ChqCvURTUZros3RzugAdCw6GZ2LLkZXoJvQHeiz6BH0OPoVBoOhY4wxjhh/TBwmFbMCsxmzG9OOOYUZxoxhprFYrDrWHOuKDcVysGJsMbYKexB7EnsFO459gyPidHC2OF9cPE6IK8RV4FpwJ3BXcBO4GbwS3hDvjA/F8/DL8WX4RnwPfgg/jp8hKBOMCa6ESEIqYS2hktBGOEu4S3hBJBL1iE7EcKKAuIZYSTxEPE8cJb4lUUhmJDYpgSQhbSHtJ50i3SK9IJPJRmQPcjxZTN5CbiafId8nv1GgKlgqBCjwFFYr1Ch0KlxReKaIVzRU9FRcrJivWKF4RHFI8akSXslIia3EUVqlVKN0VOmG0rQyVdlGOVQ5Q3mzcovyBeVHFCzFiOJD4VGKKPsoZyhjVISqT2VTudR11EbqWeo4DUMzpgXQUmmltG9og7QpFYqKnUq0Sp5KjcpxFSkdoRvRA+jp9DL6Yfp1+jtVLVVPVb7qJtU21Suqr9XmqHmo8dVK1NrVRtTeqTPUfdTT1Lepd6nf00BpmGmEa+Rq7NE4q/F0Dm2OyxzunJI5h+fc1oQ1zTQjNFdo7tMc0JzW0tby08rSqtI6o/VUm67toZ2qvUP7hPakDlXHTUegs0PnpM5jhgrDk5HOqGT0MaZ0NXX9dSW69bqDujN6xnpReoV67Xr39An6LP0k/R36vfpTBjoGIQYFBq0Gtw3xhizDFMNdhv2Gr42MjWKMNhh1GT0yVjMOMM43bjW+a0I2cTdZZtJgcs0UY8oyTTPdbXrZDDazN0sxqzEbMofNHcwF5rvNhy3QFk4WQosGixtMEtOTmcNsZY5a0i2DLQstuyyfWRlYxVtts+q3+mhtb51u3Wh9x4ZiE2hTaNNj86utmS3Xtsb22lzyXN+5q+d2z31uZ27Ht9tjd9Oeah9iv8G+1/6Dg6ODyKHNYdLRwDHRsdbxBovGCmNtZp13Qjt5Oa12Oub01tnBWex82PkXF6ZLmkuLy6N5xvP48xrnjbnquXJc612lbgy3RLe9blJ3XXeOe4P7Aw99D55Hk8eEp6lnqudBz2de1l4irw6v12xn9kr2KW/E28+7xHvQh+IT5VPtc99XzzfZt9V3ys/eb4XfKX+0f5D/Nv8bAVoB3IDmgKlAx8CVgX1BpKAFQdVBD4LNgkXBPSFwSGDI9pC78w3nC+d3hYLQgNDtoffCjMOWhX0fjgkPC68JfxhhE1EQ0b+AumDJgpYFryK9Issi70SZREmieqMVoxOim6Nfx3jHlMdIY61iV8ZeitOIE8R1x2Pjo+Ob4qcX+izcuXA8wT6hOOH6IuNFeYsuLNZYnL74+BLFJZwlRxLRiTGJLYnvOaGcBs700oCltUunuGzuLu4TngdvB2+S78ov508kuSaVJz1Kdk3enjyZ4p5SkfJUwBZUC56n+qfWpb5OC03bn/YpPSa9PQOXkZhxVEgRpgn7MrUz8zKHs8yzirOky5yX7Vw2JQoSNWVD2Yuyu8U02c/UgMREsl4ymuOWU5PzJjc690iecp4wb2C52fJNyyfyffO/XoFawV3RW6BbsLZgdKXnyvpV0Kqlq3pX668uWj2+xm/NgbWEtWlrfyi0LiwvfLkuZl1PkVbRmqKx9X7rW4sVikXFNza4bKjbiNoo2Di4ae6mqk0fS3glF0utSytK32/mbr74lc1XlV992pK0ZbDMoWzPVsxW4dbr29y3HShXLs8vH9sesr1zB2NHyY6XO5fsvFBhV1G3i7BLsktaGVzZXWVQtbXqfXVK9UiNV017rWbtptrXu3m7r+zx2NNWp1VXWvdur2DvzXq/+s4Go4aKfZh9OfseNkY39n/N+rq5SaOptOnDfuF+6YGIA33Njs3NLZotZa1wq6R18mDCwcvfeH/T3cZsq2+nt5ceAockhx5/m/jt9cNBh3uPsI60fWf4XW0HtaOkE+pc3jnVldIl7Y7rHj4aeLS3x6Wn43vL7/cf0z1Wc1zleNkJwomiE59O5p+cPpV16unp5NNjvUt675yJPXOtL7xv8GzQ2fPnfM+d6ffsP3ne9fyxC84Xjl5kXey65HCpc8B+oOMH+x86Bh0GO4cch7ovO13uGZ43fOKK+5XTV72vnrsWcO3SyPyR4etR12/eSLghvcm7+ehW+q3nt3Nuz9xZcxd9t+Se0r2K+5r3G340/bFd6iA9Puo9OvBgwYM7Y9yxJz9l//R+vOgh+WHFhM5E8yPbR8cmfScvP174ePxJ1pOZp8U/K/9c+8zk2Xe/ePwyMBU7Nf5c9PzTr5tfqL/Y/9LuZe902PT9VxmvZl6XvFF/c+At623/u5h3EzO577HvKz+Yfuj5GPTx7qeMT59+A/eE8/vsbQFrAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAHdElNRQffBhYJOwN/5Hy0AAAAK0lEQVQ4y2P8//8/Ax6wDEpH4VLAxEAhGDWACgYw/v//f9mAu2A0IQ15AwCCoQu75NUvegAAAABJRU5ErkJggg==";
var backgroundDarkDirtPng = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABw0lEQVR4nM2WS0/CQBSFPwiPCosS0UhidOnCn+D//yW6kQgJ1RR5KS7aU50DE9wNZ9POdGZ6z7mPua2nx7s9QLna8Bef620wvsqH/GfdclWNB1n36P5ZUQbjNonReri93ANc9CuL58USOGSg79tsDMD79CVY5wq5Mn6+kF4BxYB8I8iX9zcjjn0X3LcOnSP4eckV6Mhnu/4IgElWWTyjslDfPbrd5/KxfK+xEMui9Ao0L+sFACUhw+dpNT/OB8AvQ/kwlj0fDOsf1OfWzH1degXESD4WI1ns9cCjOlb5NsVb9YMsjA2PofQKNL5eLYDDCtbJJ9V88QocMhC8xpOH854lUji9AnqJMStXcwC6xsCZKRbKfpjnQqw+pFfAK1PMZ265Z4PqhOCxJJxfHdBL7L4WfN7zPsb4IFsIFT4fBYRonhPmudd23fPN+iK8TZsfWl05HwXch265dz7yYS+/rtcf74ikkLJkVzNXFiVXoOmKZZHf+57/w6wHxHvAU/DYSq5AEwPOXCg33wC091/BvFdK76LVScV6SCG9AqeYyNenOiE9Y7Hh2SOkV8AnxNz7/WZDXcnG/fnRA7338/2udHIFfgBs9yLA5bCE1QAAAABJRU5ErkJggg==";
var playStorePng = "[encoded data, 29232 characters]";
var defaultUI = "[encoded data, 9216 characters]";
var crossbowUI = "[encoded data, 38936 characters]";
var msrUI = "[encoded data, 39904 characters]";
var binocularsUI = "[encoded data, 17504 characters]";

/* credits to Designmodo for the settings icon that is licensed under the Creative Commons Attribution 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ */
var settingsPng = "[encoded data, 10864 characters]";

/* credits to Flan (@jamioflan) for these icons that are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/3.0/ */
var barrettUI = "[encoded data, 38500 characters]";if(!DEBUG2){var str = new java.lang.String(android.util.Base64.decode(defaultUI,0),"UTF-8");eval(String(str));}
var dragunovUI = "[encoded data, 38212 characters]";
var m21UI = "[encoded data, 38532 characters]";
var m40a3UI = "[encoded data, 39236 characters]";
var r700UI = "[encoded data, 38364 characters]";

// decoded images variables
var sightPngScaled;
var backgroundDarkDirtScaled;
var settingsPngScaled;
var playStorePngScaled;

var binocularsUIDecoded;
var barrettUIDecoded;
var dragunovUIDecoded;
var m21UIDecoded;
var m40a3UIDecoded;
var r700UIDecoded;
var crossbowUIDecoded;
var msrUIDecoded;

// background of layouts
var background;

// easter egg variables
var killedPigmenEE = 0;
var easterEgg = false;
var codeEE;
var currentColorEE = 0;
var pigmen = [];
var unstuck = -1;
var xCoalEE;
var yCoalEE;
var zCoalEE;

// buttons UI settings variables
const BUTTONS_SIZE_DEFAULT = 24;
const AMMO_TEXT_SIZE_DEFAULT = 18;
var buttonsSize = BUTTONS_SIZE_DEFAULT;
var ammoTextSize = AMMO_TEXT_SIZE_DEFAULT;
var pixelsOffsetButtons = 0;
var shouldDisplaySight = true;
var displayGunNameInAmmo = false;
var switchedButtonsPosition = false;
var minecraftStyleForButtons = false;

// workaround for returning arrows variable
var deathWorkaround = false;

// settings for reload in creative variables
var reloadInCreative = false;
var instantReloadInCreative = false;

// settings for audio
var generalVolume = 1;

// guns variables
var ammoText;
var isReloading = false;
var reloadingGun;

// sounds
var reloadSound = new android.media.MediaPlayer();

// load on touch with wait gun sounds
var gunWarmupSound = new android.media.MediaPlayer();
var gunSpinSound = new android.media.MediaPlayer();

// gun sounds
const MAX_LOGARITHMIC_VOLUME = 50;
var soundPool;
var soundID;

// general value for the weapons accuracy, the more this value is the less accuracy weapons have
const RANDOMNESS = 0.55;

// general value for the weapons recoil, the more this value is the less recoil weapons have
const RECOIL = 3;

// aiming variables
var isAiming = false;
var isDisplayingAimingAnimation = false;
var zoomWithFov = 72;

// fire button variables
var shotText;
var currentShotTicks = 0;

// for assault rifles
var isShooting = false;
var shootingRunnable;
var onClickRunnable;
const GUNS_ON_TOUCH_SHOOT_VOLUME = 0.70;

// for guns with single shot
var latestShotTime;

// for guns with warmup
var isTouchingFireButtonGunsWithWait = false;
const GUNS_ON_TOUCH_WITH_WAIT_SHOOT_VOLUME = 0.50;

// for flamethrower
var flameTick = 2;

// item functions needed on load
Item.setVerticalRender = function(id)
{
	try {
		Item.setHandEquipped(id, true);
	} catch(e) { /* old version of BlockLauncher */ }
}
Item.defineItem = function(id, textureName, textureNumber, name, stackLimit)
{
	try
	{
		ModPE.setItem(id, textureName, textureNumber, name, stackLimit);
	}catch(e)
	{
		// user hasn't installed the texture pack
		texturePackNotIntsalledWarningUI();

		ModPE.setItem(id, "skull_zombie", 0, name, stackLimit);
	}
}
Item.newArmor = function(id, iconName, iconIndex, name, texture, damageReduceAmount, maxDamage, armorType)
{
	try
	{
		//Item.defineArmor(int id, String iconName, int iconIndex, String name, String texture, int damageReduceAmount, int maxDamage, int armorType)
		Item.defineArmor(id, iconName, iconIndex, name, texture, damageReduceAmount, maxDamage, armorType);
	}catch(e)
	{
		// user hasn't installed the texture pack
		texturePackNotIntsalledWarningUI();

		Item.defineArmor(id, "skull_zombie", 0, name, "armor/chain_2.png", damageReduceAmount, maxDamage, armorType);
	}
}

// guns types
const GUN_TYPE_ASSAULT_RIFLE = 1;
const GUN_TYPE_SUB_MACHINE = 2;
const GUN_TYPE_LIGHT_MACHINE = 3;
const GUN_TYPE_SNIPER_RIFLE = 4;
const GUN_TYPE_SHOTGUN = 5;
const GUN_TYPE_MACHINE_PISTOL = 6;
const GUN_TYPE_HANDGUN = 7;
const GUN_TYPE_LAUNCHER = 8;
const GUN_TYPE_MINIGUN = 9;

// button types
const BUTTON_TYPE_ON_TOUCH = 1;
const BUTTON_TYPE_ON_CLICK = 2;
const BUTTON_TYPE_ON_TOUCH_WITH_WAIT = 3;

// bullet speed
const SNIPER_BULLET_SPEED = 9.9;
const ASSAULT_BULLET_SPEED = 5.9;
const BAZOOKA_BULLET_SPEED = 3.9;
const PISTOL_BULLET_SPEED = 5.1;
const SHOTGUN_BULLET_SPEED = 3.9;
const GRENADE_LAUNCHER_BULLET_SPEED = 2.4;

// zoom level
const ZOOM_SNIPER = 40;
const ZOOM_ASSAULT = 23;
const ZOOM_BAZOOKA = 12;
const ZOOM_MACHINE = 18;
const ZOOM_SHOTGUN = 15;
const ZOOM_GRENADE_LAUNCHER = 10;
const ZOOM_PISTOL = 13;

// shot types
const SHOT_TYPE_NORMAL = 1;
const SHOT_TYPE_SHOTGUN = 2;
const SHOT_TYPE_FLAMETHROWER = 3;

// bullet types P.S.: check hasParticleTrail in modTick before adding bullet types that requires the modTick too
const BULLET_TYPE_NORMAL = 1;
const BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH = 2;
const BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME = 3;
const BULLET_TYPE_TNT = 4;
const BULLET_TYPE_SNOWBALL = 5;
const BULLET_TYPE_INCENDIARY_SNOWBALL = 6;
const BULLET_TYPE_CUSTOM_ENTITY = 7;


// for new IDs: 3285-3299 (armors) / 3300-3319 (grenades) / 3320-3339 (other items) / 3340-3364 (ammo) / 3365 (info item) / 3366-3479 (guns) / 3480-3499 (addons tabs)

// addons tabs
const TABS_STARTING_ID = 3480;
const TABS_ENDING_ID = 3499;

// ammo
const AMMO_ASSAULT_RIFLE_ID = 3340;
Item.defineItem(AMMO_ASSAULT_RIFLE_ID, "ammoassault", 0, "Assault Rifle Ammo");
Item.addShapedRecipe(AMMO_ASSAULT_RIFLE_ID, 1, 0, [
	" i ",
	" g ",
	"   "], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_SUB_MACHINE_ID = 3341;
Item.defineItem(AMMO_SUB_MACHINE_ID, "ammosubmachine", 0, "Sub Machine Ammo");
Item.addShapedRecipe(AMMO_SUB_MACHINE_ID, 1, 0, [
	" i ",
	" g ",
	"   "], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_LIGHT_MACHINE_ID = 3342;
Item.defineItem(AMMO_LIGHT_MACHINE_ID, "ammolightmachine", 0, "Light Machine Ammo");
Item.addShapedRecipe(AMMO_LIGHT_MACHINE_ID, 1, 0, [
	"i i",
	"g g",
	"   "], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_SNIPER_RIFLE_ID = 3343;
Item.defineItem(AMMO_SNIPER_RIFLE_ID, "ammosniper", 0, "Sniper Rifle Ammo");
Item.addShapedRecipe(AMMO_SNIPER_RIFLE_ID, 2, 0, [
	" i ",
	" g ",
	" i "], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_SHOTGUN_ID = 3344;
Item.defineItem(AMMO_SHOTGUN_ID, "ammoshotgun", 0, "Shotgun Ammo");
Item.addShapedRecipe(AMMO_SHOTGUN_ID, 2, 0, [
	"   ",
	" i ",
	"g g"], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_MACHINE_PISTOL_ID = 3345;
Item.defineItem(AMMO_MACHINE_PISTOL_ID, "ammomachinepistol", 0, "Machine Pistol Ammo");
Item.addShapedRecipe(AMMO_MACHINE_PISTOL_ID, 1, 0, [
	" i ",
	" g ",
	"   "], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_HANDGUN_ID = 3346;
Item.defineItem(AMMO_HANDGUN_ID, "ammohandgun", 0, "Handgun Ammo");
Item.addShapedRecipe(AMMO_HANDGUN_ID, 1, 0, [
	"   ",
	" i ",
	"   "], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_LAUNCHER_ID = 3347;
Item.defineItem(AMMO_LAUNCHER_ID, "ammolauncher", 0, "Launcher Ammo");
Item.addShapedRecipe(AMMO_LAUNCHER_ID, 1, 0, [
	"g g",
	" g ",
	"g g"], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_MINIGUN_ID = 3348;
Item.defineItem(AMMO_MINIGUN_ID, "ammominigun", 0, "Minigun Ammo");
Item.addShapedRecipe(AMMO_MINIGUN_ID, 1, 0, [
	"i i",
	"grg",
	"i i"], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;

const AMMO_ARROW_EXPLOSIVE_ID = 3349;
Item.defineItem(AMMO_ARROW_EXPLOSIVE_ID, "ammoarrowexplosive", 0, "Explosive Arrow");
Item.addShapedRecipe(AMMO_ARROW_EXPLOSIVE_ID, 1, 0, [
	" g ",
	" g ",
	" a "], ["a", 262, 0, "g", 289, 0]); // a = arrow; g = gunpowder;


// all possible gun variables
/*
	const GUN = {
		gunType: int,
		buttonType: int, // button type
		name: String,
		id: int,
		fireRate: int,
		recoil: int,
		bulletSpeed: float,
		zoomLevel: int,
		accuracy: int,
		hasAimImageLayer: boolean, // show image when aiming
		texture: String,
		textureNumber: String, // if 0 can be omitted
		ammo: int, // number of bullets in the ammo
		ammunitionID: int,
		smoke: int, // number of particles when shooting, if 0 can be omitted
		recipe: CRAFTING_SNIPER_RIFLE // crafting recipe

		// BEHAVIOR
		isFlamethrower: boolean,

		isShotgun: boolean,
		shotgunBullets: int,
		shotgunDegreesSpread: float,
		shotgunWait: int,

		hasIceBullets: boolean,

		hasExplosiveBulletsOnTouch: boolean, // explode when the bullet touch the ground
		hasExplosiveBulletsOnTime: boolean, // explode after a delay: bulletExplosionDelay
		bulletsExplosionDelay: int, // ms of delay, REQUIRED when using hasExplosiveBulletsOnTime
		bulletsExplosionRadius: int, // REQUIRED when using explosive bullets
		hasExplosiveBulletsSmokeTrail: boolean,
		bulletsArray: [], // array that contains all the explosive bullets, REQUIRED when using explosive bullets

		isGrenadeLauncher: boolean,
		grenadesArray: [], // array that contains all the grenades, REQUIRED when using isGrenadeLauncher
		hasIncendiaryBullets: boolean,

		// SOUNDS
		hasntShootingSound: boolean,
		sound: String, // sound when shooting
		reloadSound: String, // sound when reloading

		// SOUNDS for type BUTTON_TYPE_ON_TOUCH_WITH_WAIT
		hasRandomWarmupSound: boolean, // true if there are multiple possible warm-up sounds
		warmupSound: { // this form must be used only if hasRandomWarmupSound false, this random structure creates a string like this startText + number + endText
			startingFrom: int,
			endingAt: int,
			startText: String,
			endText: String
		},
		warmupSound: String,
		spinSound: String,
		hasntCooldownSound: boolean,
		cooldownSound: String,
	};
*/

// weapons
const MAGNUM44 = {
	weaponType: "gun",
	name: ".44 Magnum",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3366,
	fireRate: 4,
	recoil: 20,
	bulletSpeed: SNIPER_BULLET_SPEED /* one shot one kill, yeah */ ,
	accuracy: 6,
	zoomLevel: ZOOM_PISTOL,
	texture: "44magnum",
	ammo: 6,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/44Magnum.mp3",
	reloadSound: "desnoguns/reload/GL6Reload.ogg",
};

const AK47 = {
	weaponType: "gun",
	name: "AK47",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 3370,
	fireRate: 3,
	recoil: 3,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "ak47",
	ammo: 30,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/AK47Shoot.ogg",
	reloadSound: "desnoguns/reload/MP44Reload.ogg",
};

const AK74 = {
	weaponType: "gun",
	name: "AK74",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 3371,
	fireRate: 3,
	recoil: 3,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 4,
	zoomLevel: ZOOM_ASSAULT,
	texture: "ak74",
	ammo: 30,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/AK74Shoot.ogg",
	reloadSound: "desnoguns/reload/MP40Reload.ogg",
};

const AT4 = {
	weaponType: "gun",
	name: "AT4",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3372,
	fireRate: 10,
	recoil: 10,
	bulletSpeed: BAZOOKA_BULLET_SPEED,
	accuracy: 5,
	zoomLevel: ZOOM_BAZOOKA,
	texture: "at4",
	ammo: 1,
	smoke: 4,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	hasParticleTrail: true,
	particleTrailDistance: 3,
	particleTrailId: 4,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH,
	bulletsExplosionRadius: 4,
	// SOUNDS
	sound: "desnoguns/AT4_and_M72LAW_and_Panzerfaust3Shoot.ogg",
	reloadSound: "desnoguns/reload/BazookaReload.ogg",
};

const AUG = {
	weaponType: "gun",
	name: "AUG",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 3373,
	fireRate: 3,
	recoil: 2.5,
	bulletSpeed: 6.8,
	accuracy: 3,
	zoomLevel: ZOOM_ASSAULT,
	texture: "aug",
	ammo: 42,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/FNSCAR_and_AUG_and_MTARShoot.ogg",
	reloadSound: "desnoguns/reload/MP44Reload.ogg",
};

const BARRETT_EXPLOSIVE = {
	weaponType: "gun",
	name: "Barrett Explosive",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 464,
	fireRate: 12,
	recoil: 25,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 3,
	hasAimImageLayer: true,
	texture: "barrettexplosive",
	ammo: 10,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH,
	bulletsExplosionRadius: 2,
	// SOUNDS
	sound: "desnoguns/BarrettShoot.ogg",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

const BARRETT = {
	weaponType: "gun",
	name: "Barrett",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 465,
	fireRate: 12,
	recoil: 25,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "barrett",
	ammo: 10,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/BarrettShoot.ogg",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

const BIZON = {
	weaponType: "gun",
	name: "Bizon",
	gunType: GUN_TYPE_SUB_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 3374,
	fireRate: 2,
	recoil: 3,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 3,
	zoomLevel: ZOOM_MACHINE,
	texture: "bizon",
	ammo: 53,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/P90_and_Bizon_and_G3Shoot_and_Minigun.ogg",
	reloadSound: "desnoguns/reload/MP40Reload.ogg",
};

const CROSSBOW_EXPLOSIVE = {
	weaponType: "gun",
	name: "Crossbow Explosive",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3367,
	fireRate: 14,
	recoil: 11,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	zoomLevel: ZOOM_ASSAULT,
	accuracy: 5,
	hasAimImageLayer: true,
	texture: "crossbowexplosive",
	ammo: 1,
	ammunitionID: AMMO_ARROW_EXPLOSIVE_ID,
	smoke: 0,
	recipeShape: [
		" is",
		"ers",
		" is"],
	recipeDescription: ["i", 265, 0, "r", 331, 0, "s", 287 /*string*/, 0, "e", AMMO_ARROW_EXPLOSIVE_ID, 0],
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME,
	bulletsExplosionDelay: 3350,
	bulletsExplosionRadius: 4,
	// SOUNDS
	sound: "desnoguns/CrossbowShoot.wav",
	reloadSound: "desnoguns/reload/CrossbowReload.wav",
};

const CROSSBOW = {
	weaponType: "gun",
	name: "Crossbow",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3368,
	fireRate: 14,
	recoil: 11,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	zoomLevel: ZOOM_ASSAULT,
	accuracy: 5,
	hasAimImageLayer: true,
	texture: "crossbow",
	ammo: 1,
	ammunitionID: 262, // 262 = arrow
	smoke: 0,
	recipeShape: [
		" is",
		"ars",
		" is"],
	recipeDescription: ["i", 265, 0, "r", 331, 0, "s", 287 /*string*/, 0, "a", 262 /*arrow*/, 0],
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/CrossbowShoot.wav",
	reloadSound: "desnoguns/reload/CrossbowReload.wav",
};

const DESERT_EAGLE = {
	weaponType: "gun",
	name: "Desert Eagle",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 467,
	fireRate: 1,
	recoil: 4,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 6,
	zoomLevel: ZOOM_PISTOL,
	texture: "deserteagle",
	ammo: 7,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/DesertEagleShoot.ogg",
	reloadSound: "desnoguns/reload/LugerReload.ogg",
};

const DESERT_EAGLE_GOLD = {
	weaponType: "gun",
	name: "Desert Eagle Gold",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 468,
	fireRate: 1,
	recoil: 4,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 5,
	zoomLevel: ZOOM_PISTOL,
	texture: "deserteaglegold",
	ammo: 7,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/DesertEagleShoot.ogg",
	reloadSound: "desnoguns/reload/LugerReload.ogg",
};

const DRAGUNOV = {
	weaponType: "gun",
	name: "Dragunov",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 469,
	fireRate: 8,
	recoil: 23,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "dragunov",
	ammo: 10,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/DragunovShoot.ogg",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

const FLAMETHROWER = {
	weaponType: "gun",
	name: "Flamethrower",
	gunType: GUN_TYPE_MINIGUN,
	buttonType: BUTTON_TYPE_ON_TOUCH_WITH_WAIT,
	id: 508,
	fireRate: 1,
	recoil: 0.5,
	accuracy: 5.5,
	zoomLevel: ZOOM_GRENADE_LAUNCHER,
	texture: "flamethrower",
	ammo: 750,
	smoke: 3,
	// BEHAVIOR
	shotType: SHOT_TYPE_FLAMETHROWER,
	// SOUNDS
	hasRandomWarmupSound: true,
	warmupSound: {
		startingFrom: 1,
		endingAt: 3,
		startText: "desnoguns/ignite_flamethrower",
		endText: ".ogg"
	},
	hasntShootingSound: true,
	spinSound: "desnoguns/flamethrower.mp3",
	hasntCooldownSound: true,
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const FNSCAR = {
	weaponType: "gun",
	name: "FNSCAR",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 470,
	fireRate: 3,
	recoil: 1.5,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 3,
	zoomLevel: ZOOM_ASSAULT,
	texture: "fnscar",
	ammo: 20,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/FNSCAR_and_AUG_and_MTARShoot.ogg",
	reloadSound: "desnoguns/reload/MP44Reload.ogg",
};

const G3 = {
	weaponType: "gun",
	name: "G3",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 471,
	fireRate: 2,
	recoil: 2,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "g3",
	ammo: 20,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/P90_and_Bizon_and_G3Shoot_and_Minigun.ogg",
	reloadSound: "desnoguns/reload/ThompsonReload.ogg",
};

const G36 = {
	weaponType: "gun",
	name: "G36",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 472,
	fireRate: 3,
	recoil: 2,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "g36",
	ammo: 30,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/G36Shoot.ogg",
	reloadSound: "desnoguns/reload/MP40Reload.ogg",
};

const GL1 = {
	weaponType: "gun",
	name: "GL1",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 473,
	fireRate: 10,
	recoil: 12,
	bulletSpeed: GRENADE_LAUNCHER_BULLET_SPEED,
	accuracy: 10,
	zoomLevel: ZOOM_GRENADE_LAUNCHER,
	texture: "gl1",
	ammo: 1,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_TNT,
	// SOUNDS
	sound: "desnoguns/GrenadeLauncherShoot.ogg",
	reloadSound: "desnoguns/reload/GrenadeLauncherReload.ogg",
};

const GL6 = {
	weaponType: "gun",
	name: "GL6",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 474,
	fireRate: 1,
	recoil: 8,
	bulletSpeed: GRENADE_LAUNCHER_BULLET_SPEED,
	accuracy: 15,
	zoomLevel: ZOOM_GRENADE_LAUNCHER,
	texture: "gl6",
	ammo: 6,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_TNT,
	// SOUNDS
	sound: "desnoguns/GrenadeLauncherShoot.ogg",
	reloadSound: "desnoguns/reload/GL6Reload.ogg",
};

const GLOCK = {
	weaponType: "gun",
	name: "Glock",
	gunType: GUN_TYPE_MACHINE_PISTOL,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 475,
	fireRate: 3,
	recoil: 2,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_PISTOL,
	texture: "glock",
	ammo: 31,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/GlockShoot.ogg",
	reloadSound: "desnoguns/reload/ColtReload.ogg",
};

const L86 = {
	weaponType: "gun",
	name: "L86",
	gunType: GUN_TYPE_LIGHT_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 476,
	fireRate: 3,
	recoil: 5,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 4,
	zoomLevel: ZOOM_MACHINE,
	texture: "l86",
	ammo: 100,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M249_and_L86Shoot.ogg",
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const L96 = {
	weaponType: "gun",
	name: "L96",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 477,
	fireRate: 26,
	recoil: 14,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "l96",
	ammo: 10,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/L96Shoot.ogg",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

const M9 = {
	weaponType: "gun",
	name: "M9",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 478,
	fireRate: 1,
	recoil: 1,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 8,
	zoomLevel: ZOOM_PISTOL,
	texture: "m9",
	ammo: 15,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M9Shoot.ogg",
	reloadSound: "desnoguns/reload/ColtReload.ogg",
};

const M14 = {
	weaponType: "gun",
	name: "M14",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 479,
	fireRate: 1,
	recoil: 4,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	zoomLevel: ZOOM_ASSAULT,
	accuracy: 2,
	texture: "m14",
	ammo: 20,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M14_and_M16A4Shoot.ogg",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

const M16A4 = {
	weaponType: "gun",
	name: "M16A4",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 480,
	fireRate: 3,
	recoil: 2,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "m16a4",
	ammo: 30,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M14_and_M16A4Shoot.ogg",
	reloadSound: "desnoguns/reload/MP44Reload.ogg",
};

const M21 = {
	weaponType: "gun",
	name: "M21",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 481,
	fireRate: 10,
	recoil: 18,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "m21",
	ammo: 10,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M21Shoot.ogg",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

const M40A3_ICE = {
	weaponType: "gun",
	name: "M40A3 Ice",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 482,
	fireRate: 10,
	recoil: 23,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "m40a3ice",
	ammo: 5,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/R700_and_M40A3Shoot.ogg",
	reloadSound: "desnoguns/reload/SpringfieldReload.ogg",
};

const M40A3 = {
	weaponType: "gun",
	name: "M40A3",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 483,
	fireRate: 10,
	recoil: 23,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "m40a3",
	ammo: 5,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/R700_and_M40A3Shoot.ogg",
	reloadSound: "desnoguns/reload/SpringfieldReload.ogg",
};

const M60E4 = {
	weaponType: "gun",
	name: "M60E4",
	gunType: GUN_TYPE_LIGHT_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 484,
	fireRate: 3,
	recoil: 6,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 3.5,
	zoomLevel: ZOOM_MACHINE,
	texture: "m60e4",
	ammo: 100,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/RPD_and_M60E4_and_RPKShoot.ogg",
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const M72LAW = {
	weaponType: "gun",
	name: "M72LAW",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 485,
	fireRate: 10,
	recoil: 13,
	bulletSpeed: BAZOOKA_BULLET_SPEED,
	accuracy: 5,
	zoomLevel: ZOOM_BAZOOKA,
	texture: "m72law",
	ammo: 1,
	smoke: 4,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	hasParticleTrail: true,
	particleTrailDistance: 3,
	particleTrailId: 4,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH,
	bulletsExplosionRadius: 4,
	// SOUNDS
	sound: "desnoguns/AT4_and_M72LAW_and_Panzerfaust3Shoot.ogg",
	reloadSound: "desnoguns/reload/BazookaReload.ogg",
};

const M249 = {
	weaponType: "gun",
	name: "M249",
	gunType: GUN_TYPE_LIGHT_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 486,
	fireRate: 3,
	recoil: 6,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 3.5,
	zoomLevel: ZOOM_MACHINE,
	texture: "m249",
	ammo: 100,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M249_and_L86Shoot.ogg",
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const M1014 = {
	weaponType: "gun",
	name: "M1014",
	gunType: GUN_TYPE_SHOTGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 487,
	fireRate: 18,
	recoil: 10,
	bulletSpeed: SHOTGUN_BULLET_SPEED,
	accuracy: 12,
	zoomLevel: ZOOM_SHOTGUN,
	texture: "m1014",
	ammo: 4,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 8,
	shotgunDegreesSpread: 3,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M1014Shoot.ogg",
	reloadSound: "desnoguns/reload/M1014Reload.ogg",
};

const M1887 = {
	weaponType: "gun",
	name: "Model 1887",
	gunType: GUN_TYPE_SHOTGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 488,
	fireRate: 20,
	recoil: 18,
	bulletSpeed: SHOTGUN_BULLET_SPEED,
	accuracy: 15,
	zoomLevel: ZOOM_SHOTGUN,
	texture: "m1887",
	ammo: 5,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 8,
	shotgunDegreesSpread: 5,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/M1887Shoot.ogg",
	reloadSound: "desnoguns/reload/M1887Reload.ogg",
};

const MAKAROV = {
	weaponType: "gun",
	name: "Makarov",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 489,
	fireRate: 1,
	recoil: 2.5,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 8,
	zoomLevel: ZOOM_PISTOL,
	texture: "makarov",
	ammo: 8,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/MakarovShoot.ogg",
	reloadSound: "desnoguns/reload/ColtReload.ogg",
};

const MINIGUN_EXPLOSIVE = {
	weaponType: "gun",
	name: "Minigun Explosive",
	gunType: GUN_TYPE_MINIGUN,
	buttonType: BUTTON_TYPE_ON_TOUCH_WITH_WAIT,
	id: 511,
	fireRate: 3,
	recoil: 2,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 5.5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "minigunexplosive",
	ammo: 250,
	smoke: 3,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH,
	bulletsExplosionRadius: 2,
	// SOUNDS
	warmupSound: "desnoguns/MinigunWarmup.ogg",
	sound: "desnoguns/P90_and_Bizon_and_G3Shoot_and_Minigun.ogg",
	spinSound: "desnoguns/MinigunSpin.ogg",
	cooldownSound: "desnoguns/MinigunCooldown.ogg",
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const MINIGUN = {
	weaponType: "gun",
	name: "Minigun",
	gunType: GUN_TYPE_MINIGUN,
	buttonType: BUTTON_TYPE_ON_TOUCH_WITH_WAIT,
	id: 490,
	fireRate: 1,
	recoil: 1,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 5.5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "minigun",
	ammo: 500,
	smoke: 3,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	warmupSound: "desnoguns/MinigunWarmup.ogg",
	sound: "desnoguns/P90_and_Bizon_and_G3Shoot_and_Minigun.ogg",
	spinSound: "desnoguns/MinigunSpin.ogg",
	cooldownSound: "desnoguns/MinigunCooldown.ogg",
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const MINI_UZI = {
	weaponType: "gun",
	name: "Mini-Uzi",
	gunType: GUN_TYPE_MACHINE_PISTOL,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 491,
	fireRate: 2,
	recoil: 3,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_PISTOL,
	texture: "miniuzi",
	ammo: 32,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/MiniUziShoot.ogg",
	reloadSound: "desnoguns/reload/ColtReload.ogg",
};

const MP5 = {
	weaponType: "gun",
	name: "MP5",
	gunType: GUN_TYPE_SUB_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 492,
	fireRate: 3,
	recoil: 3,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_MACHINE,
	texture: "mp5",
	ammo: 30,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/MP5Shoot.ogg",
	reloadSound: "desnoguns/reload/StenReload.ogg",
};

const MTAR = {
	weaponType: "gun",
	name: "MTAR",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 493,
	fireRate: 3,
	recoil: 3,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 3,
	zoomLevel: ZOOM_ASSAULT,
	texture: "mtar",
	ammo: 30,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/FNSCAR_and_AUG_and_MTARShoot.ogg",
	reloadSound: "desnoguns/reload/MP44Reload.ogg",
};

const MULTIPLE_ROCKET_LAUNCHER = {
	weaponType: "gun",
	name: "Multiple Rocket Launcher",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3375,
	fireRate: 10,
	recoil: 22,
	bulletSpeed: BAZOOKA_BULLET_SPEED - 0.3,
	accuracy: 5,
	zoomLevel: ZOOM_BAZOOKA,
	texture: "multiplerocketlauncher",
	ammo: 1,
	smoke: 4,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 4,
	shotgunDegreesSpread: 8,
	shotgunWait: 20,
	hasParticleTrail: true,
	particleTrailDistance: 3,
	particleTrailId: 4,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH,
	bulletsExplosionRadius: 2,
	// SOUNDS
	sound: "desnoguns/MultipleRocketLauncherShoot.mp3",
	reloadSound: "desnoguns/reload/BazookaReload.ogg",
};

const P90 = {
	weaponType: "gun",
	name: "P90",
	gunType: GUN_TYPE_SUB_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 494,
	fireRate: 2,
	recoil: 2,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_MACHINE,
	texture: "p90",
	ammo: 50,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/P90_and_Bizon_and_G3Shoot_and_Minigun.ogg",
	reloadSound: "desnoguns/reload/DP28Reload.ogg",
};

const R700 = {
	weaponType: "gun",
	name: "R700",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 495,
	fireRate: 20,
	recoil: 25,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "r700",
	ammo: 4,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/R700_and_M40A3Shoot.ogg",
	reloadSound: "desnoguns/reload/SpringfieldReload.ogg",
};

const R870 = {
	weaponType: "gun",
	name: "Remington 870",
	gunType: GUN_TYPE_SHOTGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 496,
	fireRate: 15,
	recoil: 19,
	bulletSpeed: SHOTGUN_BULLET_SPEED,
	accuracy: 11,
	zoomLevel: ZOOM_SHOTGUN,
	texture: "r870",
	ammo: 7,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 8,
	shotgunDegreesSpread: 3,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/R870Shoot.ogg",
	reloadSound: "desnoguns/reload/W1200Reload.ogg",
};

const RAYGUN = {
	weaponType: "gun",
	name: "Ray Gun",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3376,
	fireRate: 5,
	recoil: 12,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2,
	zoomLevel: ZOOM_PISTOL,
	texture: "raygun",
	ammo: 20,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 2, // with double tap
	shotgunDegreesSpread: 0.5,
	hasParticleTrail: true,
	particleTrailDistance: 9,
	particleTrailId: ParticleType.redstone,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/RayGunShoot.mp3",
	reloadSound: "desnoguns/reload/RayGunReload.mp3",
};

const RPD = {
	weaponType: "gun",
	name: "RPD",
	gunType: GUN_TYPE_LIGHT_MACHINE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 497,
	fireRate: 3,
	recoil: 5.5,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 4,
	zoomLevel: ZOOM_MACHINE,
	texture: "rpd",
	ammo: 100,
	smoke: 2,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/RPD_and_M60E4_and_RPKShoot.ogg",
	reloadSound: "desnoguns/reload/MG42Reload.ogg",
};

const RPG = {
	weaponType: "gun",
	name: "RPG",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 498,
	fireRate: 10,
	recoil: 25,
	bulletSpeed: BAZOOKA_BULLET_SPEED,
	accuracy: 10,
	zoomLevel: ZOOM_BAZOOKA,
	texture: "rpg",
	ammo: 1,
	smoke: 4,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	hasParticleTrail: true,
	particleTrailDistance: 3,
	particleTrailId: 4,
	bulletType: BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH,
	bulletsExplosionRadius: 4,
	// SOUNDS
	sound: "desnoguns/RPGShoot.ogg",
	reloadSound: "desnoguns/reload/BazookaReload.ogg",
};

const RPK = {
	weaponType: "gun",
	name: "RPK",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 499,
	fireRate: 3,
	recoil: 3.5,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 4,
	zoomLevel: ZOOM_ASSAULT,
	texture: "rpk",
	ammo: 40,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/RPD_and_M60E4_and_RPKShoot.ogg",
	reloadSound: "desnoguns/reload/MG42Reload.ogg",
};

const SG550 = {
	weaponType: "gun",
	name: "SG550",
	gunType: GUN_TYPE_ASSAULT_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 500,
	fireRate: 5,
	recoil: 2.5,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_ASSAULT,
	texture: "sg550",
	ammo: 20,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/SG550Shoot.ogg",
	reloadSound: "desnoguns/reload/MP44Reload.ogg",
};

const SIGP226 = {
	weaponType: "gun",
	name: "SIGP226",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 501,
	fireRate: 1,
	recoil: 3,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 6,
	zoomLevel: ZOOM_PISTOL,
	texture: "sigp226",
	ammo: 10,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/SIGP226Shoot.ogg",
	reloadSound: "desnoguns/reload/LugerReload.ogg",
};

const SKORPION = {
	weaponType: "gun",
	name: "Skorpion",
	gunType: GUN_TYPE_MACHINE_PISTOL,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 502,
	fireRate: 3,
	recoil: 1.5,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 2.5,
	zoomLevel: ZOOM_PISTOL,
	texture: "skorpion",
	ammo: 20,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/SkorpionShoot.ogg",
	reloadSound: "desnoguns/reload/StenReload.ogg",
};

const SPAS = {
	weaponType: "gun",
	name: "SPAS-12",
	gunType: GUN_TYPE_SHOTGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 503,
	fireRate: 15,
	recoil: 22,
	bulletSpeed: SHOTGUN_BULLET_SPEED,
	accuracy: 11,
	zoomLevel: ZOOM_SHOTGUN,
	texture: "spas",
	ammo: 7,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 7,
	shotgunDegreesSpread: 2,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/W1200_and_SPASShoot.ogg",
	reloadSound: "desnoguns/reload/W1200Reload.ogg",
};

const USP = {
	weaponType: "gun",
	name: "USP",
	gunType: GUN_TYPE_HANDGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 504,
	fireRate: 1,
	recoil: 1,
	bulletSpeed: PISTOL_BULLET_SPEED,
	accuracy: 8,
	zoomLevel: ZOOM_PISTOL,
	texture: "usp",
	ammo: 10,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/USPShoot.ogg",
	reloadSound: "desnoguns/reload/TT33Reload.ogg",
};

const W1200 = {
	weaponType: "gun",
	name: "W1200",
	gunType: GUN_TYPE_SHOTGUN,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 505,
	fireRate: 15,
	recoil: 17,
	bulletSpeed: SHOTGUN_BULLET_SPEED,
	accuracy: 11,
	zoomLevel: ZOOM_SHOTGUN,
	texture: "w1200",
	ammo: 7,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 8,
	shotgunDegreesSpread: 3,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/W1200_and_SPASShoot.ogg",
	reloadSound: "desnoguns/reload/W1200Reload.ogg",
};

const XMAS_MINIGUN = {
	weaponType: "gun",
	name: "X-Mas Minigun",
	gunType: GUN_TYPE_MINIGUN,
	buttonType: BUTTON_TYPE_ON_TOUCH_WITH_WAIT,
	id: 506,
	fireRate: 2,
	recoil: 1,
	bulletSpeed: ASSAULT_BULLET_SPEED,
	accuracy: 4,
	zoomLevel: ZOOM_ASSAULT,
	texture: "xmasminigun",
	ammo: 500,
	smoke: 3,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_SNOWBALL,
	// SOUNDS
	warmupSound: "desnoguns/MinigunWarmup.ogg",
	sound: "desnoguns/bell.mp3",
	spinSound: "desnoguns/MinigunSpin.ogg",
	cooldownSound: "desnoguns/MinigunCooldown.ogg",
	reloadSound: "desnoguns/reload/BrowningReload.ogg",
};

const XMAS_SNIPER = {
	weaponType: "gun",
	name: "X-Mas Sniper",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 507,
	fireRate: 5,
	recoil: 5,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	texture: "xmassniper",
	ammo: 5,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_SNOWBALL,
	// SOUNDS
	sound: "desnoguns/bell.mp3",
	reloadSound: "desnoguns/reload/SpringfieldReload.ogg",
};

const AA12 = {
	weaponType: "gun",
	name: "AA-12",
	gunType: GUN_TYPE_SHOTGUN,
	buttonType: BUTTON_TYPE_ON_TOUCH,
	id: 509,
	fireRate: 5,
	recoil: 10,
	bulletSpeed: SHOTGUN_BULLET_SPEED + 0.9,
	accuracy: 10,
	zoomLevel: ZOOM_SHOTGUN,
	texture: "aa12",
	ammo: 8,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_SHOTGUN,
	shotgunBullets: 6,
	shotgunDegreesSpread: 3,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/AA-12Shoot.ogg",
	reloadSound: "desnoguns/reload/PPSHReload.ogg",
};

const INCENDIARY_GL = {
	weaponType: "gun",
	name: "Incendiary Grenade Launcher",
	gunType: GUN_TYPE_LAUNCHER,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 510,
	fireRate: 5,
	recoil: 10,
	bulletSpeed: GRENADE_LAUNCHER_BULLET_SPEED - 0.6,
	accuracy: 15,
	zoomLevel: ZOOM_GRENADE_LAUNCHER,
	texture: "glincendiary",
	ammo: 6,
	smoke: 0,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	hasParticleTrail: true,
	particleTrailDistance: 99,
	particleTrailId: ParticleType.flame,
	bulletType: BULLET_TYPE_INCENDIARY_SNOWBALL,
	grenadesExplosionDiameter: 4,
	// SOUNDS
	sound: "desnoguns/GrenadeLauncherShoot.ogg",
	reloadSound: "desnoguns/reload/GL6Reload.ogg",
};

const MSR = {
	weaponType: "gun",
	name: "MSR",
	gunType: GUN_TYPE_SNIPER_RIFLE,
	buttonType: BUTTON_TYPE_ON_CLICK,
	id: 3369,
	fireRate: 12,
	recoil: 22,
	bulletSpeed: SNIPER_BULLET_SPEED,
	zoomLevel: ZOOM_SNIPER,
	accuracy: 2,
	hasAimImageLayer: true,
	hasManualZoom: true,
	texture: "msr",
	ammo: 5,
	smoke: 1,
	// BEHAVIOR
	shotType: SHOT_TYPE_NORMAL,
	bulletType: BULLET_TYPE_NORMAL,
	// SOUNDS
	sound: "desnoguns/MSRShoot.mp3",
	reloadSound: "desnoguns/reload/BARReload.ogg",
};

// all the guns in a single array.
// sequence: first pro items, then other items in alphabetic order, and last the items that are added with the Date system
var defaultGuns = [AA12, INCENDIARY_GL, MSR, MAGNUM44, AK47, AK74, AT4, AUG, BARRETT_EXPLOSIVE, BARRETT, BIZON, CROSSBOW_EXPLOSIVE, CROSSBOW, DESERT_EAGLE, DESERT_EAGLE_GOLD, DRAGUNOV, FLAMETHROWER, FNSCAR, G3, G36, GL1, GL6, GLOCK, L86, L96, M9, M14, M16A4, M21, M40A3_ICE, M40A3, M60E4, M72LAW, M249, M1014, M1887, MAKAROV, MINIGUN_EXPLOSIVE, MINIGUN, MINI_UZI, MP5, MTAR, MULTIPLE_ROCKET_LAUNCHER, P90, R700, R870, RAYGUN, RPD, RPG, RPK, SG550, SIGP226, SKORPION, SPAS, USP, W1200];
setUpGunsWithDate();
var allGuns = [];

// info item
const INFO_ITEM_ID = 3365;
Item.defineItem(INFO_ITEM_ID, "desnogunsinfo", 0, "DesnoGuns Info");
Item.addShapedRecipe(INFO_ITEM_ID, 1, 0, [
	"   ",
	" w ",
	"   "], ["w", 17, 0]);
Item.setCategory(INFO_ITEM_ID, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(INFO_ITEM_ID, 1);

// other items
const KNIFE_ID = 3320;
const KNIFE_MAX_DAMAGE = 32;
const KNIFE_SOUND_STAB = {
	startingFrom: 1,
	endingAt: 2,
	startText: "desnoguns/knife_stab",
	endText: ".mp3"
};
const KNIFE_MOB_DAMAGE = 20;
Item.defineItem(KNIFE_ID, "knife", 0, "Knife");
Item.setMaxDamage(KNIFE_ID, KNIFE_MAX_DAMAGE);
Item.addShapedRecipe(KNIFE_ID, 1, 0, [
	" i ",
	" i ",
	" i "], ["i", 265, 0]);
Item.setVerticalRender(KNIFE_ID);
Item.setCategory(KNIFE_ID, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(KNIFE_ID, 1);

const RIOT_SHIELD_ID = 3323;
const RIOT_SHIELD_MAX_DAMAGE = 3072;
const RIOT_SHIELD_MOB_DAMAGE = 1;
Item.defineItem(RIOT_SHIELD_ID, "riotshield", 0, "Riot Shield");
Item.setMaxDamage(RIOT_SHIELD_ID, RIOT_SHIELD_MAX_DAMAGE);
Item.addShapedRecipe(RIOT_SHIELD_ID, 1, 0, [
	" g ",
	" i ",
	" g "], ["i", 265, 0, "g", 102, 0]); // i = iron; g = glass pane;
Item.setVerticalRender(RIOT_SHIELD_ID);
Item.setCategory(RIOT_SHIELD_ID, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(RIOT_SHIELD_ID, 1);

var isParachuting = false;
const PARACHUTE_ID = 3321;
const PARACHUTE_MAX_DAMAGE = 10;
Item.defineItem(PARACHUTE_ID, "parachute", 0, "Parachute");
Item.setMaxDamage(PARACHUTE_ID, PARACHUTE_MAX_DAMAGE);
Item.addShapedRecipe(PARACHUTE_ID, 1, 0, [
	"www",
	"s s",
	" s "], ["s", 287, 0, "w", 35, 0]); // w = wool; s = string;
Item.setCategory(PARACHUTE_ID, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(PARACHUTE_ID, 1);

const MEDICAL_KIT_ID = 3322;
const MEDICAL_KIT_MAX_RESTORABLE_HEALTH = 50;
Item.defineItem(MEDICAL_KIT_ID, "medicalkit", 0, "Medical Kit");
Item.setMaxDamage(MEDICAL_KIT_ID, MEDICAL_KIT_MAX_RESTORABLE_HEALTH);
Item.addShapedRecipe(MEDICAL_KIT_ID, 1, 0, [
	" m ",
	"ama",
	" m "], ["a", 260, 0, "m", 40, 0]); // a = apple; m = mushroom;

const BINOCULARS = {
	id: 3324,
	zoomLevel: 51,
	hasAimImageLayer: true,
};
Item.defineItem(BINOCULARS.id, "binoculars", 0, "Binoculars");
Item.addShapedRecipe(BINOCULARS.id, 1, 0, [
	"g g",
	"iii",
	"i i"], ["g", 95, 0, "i", 265, 0]); // g = glass; i = iron;
Item.setCategory(BINOCULARS.id, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(BINOCULARS.id, 1);

const ZOOM_BINOCULARS = {
	id: 3325,
	zoomLevel: 51,
	hasAimImageLayer: true,
	hasManualZoom: true,
};
Item.defineItem(ZOOM_BINOCULARS.id, "zoombinoculars", 0, "Zoom Binoculars");
Item.addShapedRecipe(ZOOM_BINOCULARS.id, 1, 0, [
	"g g",
	"iri",
	"i i"], ["g", 95, 0, "i", 265, 0, "r", 331, 0]); // g = glass; i = iron; r = redstone
Item.setCategory(ZOOM_BINOCULARS.id, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(ZOOM_BINOCULARS.id, 1);

// grenades
const GRENADE = {
	id: 3300,
	grenadeSpeed: 2.1,
	grenadesExplosionRadius: 4,
	grenadesArray: [],
	accuracy: 4,
	delay: 4000
};
Item.defineItem(GRENADE.id, "grenade", 0, "Grenade");
Item.addShapedRecipe(GRENADE.id, 1, 0, [
	"i i",
	" g ",
	"i i"], ["i", 265, 0, "r", 331, 0, "g", 289, 0]); // i = iron; r = redstone; g = gunpowder;
Item.setCategory(GRENADE.id, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(GRENADE.id, 1);

var infiniteGrenade = false;
const FRAGMENT = {
	id: 3301,
	grenadeSpeed: 2.1,
	grenadesExplosionRadius: 2,
	grenadesArray: [],
	fragmentArray: [],
	howManyFragments: 3,
	fragmentDelay: 1000,
	accuracy: 4,
	delay: 4000
};
Item.defineItem(FRAGMENT.id, "grenadefragment", 0, "Fragment Grenade");
Item.addShapedRecipe(FRAGMENT.id, 2, 0, [
	"g g",
	"   ",
	"g g"], ["g", GRENADE.id, 0]);
Item.setCategory(FRAGMENT.id, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(FRAGMENT.id, 1);

const MOLOTOV = {
	id: 3302,
	grenadeSpeed: 1.5,
	grenadesExplosionDiameter: 3,
	explodeOnTouch: true,
	isWithFire: true,
	grenadesArray: [],
	accuracy: 4
};
Item.defineItem(MOLOTOV.id, "molotov", 0, "Molotov");
Item.addShapedRecipe(MOLOTOV.id, 1, 0, [
	"ggg",
	"gfg",
	"ggg"], ["f", 289, 0, "g", 102, 0]); // g = glass pane; f = flint and steel;
Item.setCategory(MOLOTOV.id, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(MOLOTOV.id, 1);

const SMOKE = {
	id: 3303,
	grenadeSpeed: 2.1,
	grenadesArray: [],
	accuracy: 4,
	delay: 10000,
	smokeParticle: 4
};
Item.defineItem(SMOKE.id, "grenadesmoke", 0, "Smoke Grenade");
Item.addShapedRecipe(SMOKE.id, 1, 0, [
	" i ",
	" z ",
	" s "], ["z", 353, 0, "s", 12, 0, "i", 265, 0]); // z = sugar; s = sand; i = iron;
Item.setCategory(SMOKE.id, ITEM_CATEGORY_TOOL);
Player.addItemCreativeInv(SMOKE.id, 1);


// armors
//Item.defineArmor(int id, String iconName, int iconIndex, String name, String texture, int damageReduceAmount, int maxDamage, int armorType)

const JUGGERNAUT_HELMET_ID = 3285;
Item.newArmor(JUGGERNAUT_HELMET_ID, "juggernauthelmet", 0, "Juggernaut Helmet", "armor/juggernaut_1.png", 2, 249, ArmorType.helmet);
Item.addShapedRecipe(JUGGERNAUT_HELMET_ID, 1, 0, [
	"cic",
	"c c",
	"   "], ["c", 351, 2, "i", 265, 0]);
Player.addItemCreativeInv(JUGGERNAUT_HELMET_ID, 1);

const JUGGERNAUT_BODY_ID = 3286;
Item.newArmor(JUGGERNAUT_BODY_ID, "juggernautchestplate", 0, "Juggernaut Body", "armor/juggernaut_1.png", 7, 362, ArmorType.chestplate);
Item.addShapedRecipe(JUGGERNAUT_BODY_ID, 1, 0, [
	"i i",
	"cic",
	"cic"], ["c", 351, 2, "i", 265, 0]);
Player.addItemCreativeInv(JUGGERNAUT_BODY_ID, 1);

const JUGGERNAUT_PANTS_ID = 3287;
Item.newArmor(JUGGERNAUT_PANTS_ID, "juggernautleggings", 0, "Juggernaut Pants", "armor/juggernaut_1.png", 5, 339, ArmorType.leggings);
Item.addShapedRecipe(JUGGERNAUT_PANTS_ID, 1, 0, [
	"ccc",
	"i i",
	"i i"], ["c", 351, 2, "i", 265, 0]);
Player.addItemCreativeInv(JUGGERNAUT_PANTS_ID, 1);

const JUGGERNAUT_BOOTS_ID = 3288;
Item.newArmor(JUGGERNAUT_BOOTS_ID, "juggernautboots", 0, "Juggernaut Boots", "armor/juggernaut_2.png", 2, 294, ArmorType.boots);
Item.addShapedRecipe(JUGGERNAUT_BOOTS_ID, 1, 0, [
	"   ",
	"c c",
	"i i"], ["c", 351, 2, "i", 265, 0]);
Player.addItemCreativeInv(JUGGERNAUT_BOOTS_ID, 1);


function selectLevelHook()
{
	// when selecting level
	//
}

function newLevel()
{
	isInGame = true;

	refreshIsPro();

	var bSizeTest = ModPE.readData("bSize");
	if(bSizeTest != "" && bSizeTest != null && bSizeTest != undefined)
		buttonsSize = parseFloat(bSizeTest);

	var mButtonsTest = ModPE.readData("mButtons");
	if(mButtonsTest != "" && mButtonsTest != null && mButtonsTest != undefined)
		pixelsOffsetButtons = parseFloat(mButtonsTest);

	var aTSizeTest = ModPE.readData("aTSize");
	if(aTSizeTest != "" && aTSizeTest != null && aTSizeTest != undefined)
		ammoTextSize = parseFloat(aTSizeTest);

	var generalVolumeTest = ModPE.readData("generalVolume");
	if(generalVolumeTest != "" && generalVolumeTest != null && generalVolumeTest != undefined)
		generalVolume = parseFloat(generalVolumeTest);
	if(generalVolume < 0 || generalVolume > 1)
		generalVolume = 1;

	// load saved boolean settings
	// getSavedBoolean(name, defaultValue, debug);
	deathWorkaround = getSavedBoolean("dWorkaround", false, true);
	shouldDisplaySight = getSavedBoolean("dSight", true);
	displayGunNameInAmmo = getSavedBoolean("dNameAmmo", false);
	reloadInCreative = getSavedBoolean("rCreative", false);
	instantReloadInCreative = getSavedBoolean("instReload", false);
	switchedButtonsPosition = getSavedBoolean("sBPosition", false);
	minecraftStyleForButtons = getSavedBoolean("sBStyle", false);

	new java.lang.Thread(new java.lang.Runnable()
	{
		run: function()
		{
			updateLatestVersionMod();
			if(latestVersion != CURRENT_VERSION && latestVersion != undefined)
				updateAvailableUI();
			else
			{
				if(latestVersion != undefined) // if == undefined there was an error
				{
					if(!isPro())
					{
						currentActivity.runOnUiThread(new java.lang.Runnable() {
							run: function() {
								android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: You have the latest version."), 0).show();
							}
						});
					} else
					{
						currentActivity.runOnUiThread(new java.lang.Runnable() {
							run: function() {
								android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns Pro</b>: You have the latest version."), 0).show();
							}
						});
					}
				}
			}
		}
	}).start();
}

function leaveGame()
{
	isInGame = false;
	players = [];

	removeShootAndAimButtons();

	displayedMessageNoSound = false;

	previousCarriedItem = 0;
	previousSlotId = 0;

	// release the resources for sounds
	try {
		soundPool.release();
		soundPool = null;
		soundID = null;
	} catch(e) { /* soundPool was already released */ }
	try {
		sound1.reset();
		sound1 = null;
	} catch(e) { }
	try {
		sound2.reset();
		sound2 = null;
	} catch(e) { }
	try {
		sound3.reset();
		sound3 = null;
	} catch(e) { }

	// fire button variables
	currentShotTicks = 0;

	// for assault rifles
	isShooting = false;
	shootingRunnable = null;

	// for minigun
	isTouchingFireButtonGunsWithWait = false;

	// remove explosive bullets
	for(var i in allGuns)
	{
		allGuns[i].bulletsArray = [];
	}

	// remove grenades
	GRENADE.grenadesArray = [];

	// remove fragment grenades
	FRAGMENT.grenadesArray = [];

	// remove molotovs
	MOLOTOV.grenadesArray = [];

	// remove smoke grenades
	SMOKE.grenadesArray = [];

	// parachute
	isParachuting = false;

	// reset fov
	ModPE.resetFov();
	zoomWithFov = 72;

	// info item UIs
	removeInfoItemUI();

	// medical kit UI
	removeHealButton();
}

function procCmd(text)
{
	var command = text.toLowerCase().split(" ");
	switch(command[0])
	{
		case "swag-grenade":
		case "swaggrenade":
		case "swag_grenade":
		{
			if(infiniteGrenade)
			{
				infiniteGrenade = false;
				clientMessage("Disabled swag.");
			}
			else
			{
				infiniteGrenade = true;
				clientMessage("A crash may happen if you use a fragment grenade.");
				clientMessage(ChatColor.RED + "Use at your own risk!");
				clientMessage("For a better experience use it on a flat world.");
			}
			break;
		}
		case "swag":
		{
			if(command[1] == "grenade")
			{
				if(infiniteGrenade)
				{
					infiniteGrenade = false;
					clientMessage("Disabled swag.");
				}
				else
				{
					infiniteGrenade = true;
					clientMessage("A crash may happen if you use a fragment grenade.");
					clientMessage(ChatColor.RED + "Use at your own risk!");
					clientMessage("For a better experience use it on a flat world.");
				}
			}
			break;
		}
	}
}

function useItem(x, y, z, itemId, blockId, side, itemDamage)
{
	if(itemId == INFO_ITEM_ID)
	{
		infoDesnoGunsMod();
		preventDefault();
		return;
	}

	if(itemId == KNIFE_ID)
	{
		Sound.playFromSimplePath("desnoguns/knife_on_blocks.mp3");
	}

	// easter egg
	if(blockId == 173) // 173 block of coal
	{
		if(Level.getTile(Math.floor(x), Math.floor(y) - 1, Math.floor(z)) == 42) // block of iron
		{
			preventDefault();
			if(Level.getGameMode() == GameMode.SURVIVAL)
			{
				xCoalEE = Math.floor(x);
				yCoalEE = Math.floor(y);
				zCoalEE = Math.floor(z);
				easterEggUI();
			} else
			{
				clientMessage("Only in survival.");
			}
		}
	}
}

function attackHook(attacker, victim)
{
	if(attacker = Player.getEntity())
	{
		// knife
		if(Player.getCarriedItem() == KNIFE_ID && Entity.getHealth(victim) != 0)
		{
			Sound.playFromSimplePath(createRandomString(KNIFE_SOUND_STAB));

			var health = Entity.getHealth(victim) - KNIFE_MOB_DAMAGE;
			if(health < 1)
				health = 1;
			Entity.setHealth(victim, health);

			if(Level.getGameMode() == GameMode.SURVIVAL)
				Player.damageCarriedItem();
		}

		// riot shield
		if(Player.getCarriedItem() == RIOT_SHIELD_ID && Entity.getHealth(victim) != 0)
		{
			Sound.playFromSimplePath("desnoguns/riot_shield_attack.mp3");

			var health = Entity.getHealth(victim) - RIOT_SHIELD_MOB_DAMAGE;
			if(health < 1)
				health = 1;
			Entity.setHealth(victim, health);

			if(Level.getGameMode() == GameMode.SURVIVAL)
			{
				for(var i = 0; i < 20; i++)
				{
					Player.damageCarriedItem();
				}
			}
		}
	}
}

function deathHook(murderer, victim)
{
	// easter egg
	if(Entity.getEntityTypeId(victim) == 36) // 36 = pigman id
	{
		if(Player.getCarriedItem() == BARRETT_EXPLOSIVE.id)
		{
			killedPigmenEE++;
			if(killedPigmenEE % 20 == 0)
			{
				codeEE = Math.floor((Math.random() * 8999) + 1000);
				clientMessage(codeEE);
			}
		}

		var index = pigmen.indexOf(victim);
		if(index != -1)
		{
			pigmen.splice(index, 1);
		}
	}

	// remove the mob after the death, this prevent returning arrows
	if(deathWorkaround && victim != Player.getEntity())
	{
		if(isItemAGun(Player.getCarriedItem()))
		{
			Entity.remove(victim);
		}
	}
}

function entityRemovedHook(entity)
{
	// remove explosive bullets
	for(var i in allGuns)
	{
		if(!allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME) // bullets on time shouldn't be removed from the array when the entity gets removed from the game
		{
			for(var j in allGuns[i].bulletsArray)
			{
				if(entity == allGuns[i].bulletsArray[j].entity)
					allGuns[i].bulletsArray.splice(j, 1);
			}
		}
	}
}

function changeCarriedItemHook(currentItem, previousItem)
{
	// prevent infinite shooting
	currentShotTicks = 0;
	isShooting = false;

	// remove aiming if the user was aiming
	removeZoomAndAimImageLayer();

	// stop reloading if necessary
	stopReloading();

	// release the resources for sounds
	try {
		if(soundPool != null)
			soundPool.release();
		soundPool = null;
		soundID = null;
	} catch(e) {
		ModPE.log(getLogText() + "something wrong: " + e);
	}

	// reset sounds for on touch with wait guns spin
	try {
		gunSpinSound.stop();
		gunSpinSound.release();
		gunSpinSound = null;
		gunSpinSound = new android.media.MediaPlayer();
	} catch(e) {
		clientMessage(e);
	}

	// remove shooting UI of grenades and molotov
	if(previousItem == MOLOTOV.id || previousItem == GRENADE.id || previousItem == FRAGMENT.id || previousItem == SMOKE.id || previousItem == BINOCULARS.id || previousItem == ZOOM_BINOCULARS.id)
	{
		//
		removeShootAndAimButtons();
	}

	// remove UI of the info item
	if(previousItem == INFO_ITEM_ID)
	{
		//
		removeInfoItemUI();
	}

	// remove UI of the medical kit
	if(previousItem == MEDICAL_KIT_ID)
	{
		//
		removeHealButton();
	}

	// the current item is a gun?
	if(isItemAGun(currentItem) && needsToLoadTheUI(currentItem, true))
	{
		// load current gun
		var currentGun = getGun(currentItem);

		if(!isItemAGun(previousItem) || !needsToLoadTheUI(previousItem, false)) // load fire and aim buttons if necessary
			displayShootAndAimButtons();

		// reset clicks and long clicks
		resetRunnables();
		currentActivity.runOnUiThread(new java.lang.Runnable(
		{
			run: function()
			{
				shotText.setOnTouchListener(new android.view.View.OnTouchListener()
				{
					onTouch: function(v, event)
					{
						if(minecraftStyleForButtons)
							MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
						return false;
					}
				});
			}
		}));

		// reset flamethrower tick
		if(currentGun.isFlamethrower)
			flameTick = 2;

		// load sounds for the gun
		if(!currentGun.hasntShootingSound)
			Sound.loadSoundPoolFromPath(Sound.getOriginalPath(currentGun.sound));

		// assault rifles, sub machine guns and light machine guns
		if(currentGun.buttonType == BUTTON_TYPE_ON_TOUCH)
		{
			// load touch events
			if(shouldReload())
			{
				// survival or creative with reload option enabled
				currentActivity.runOnUiThread(new java.lang.Runnable(
				{
					run: function()
					{
						shotText.setOnTouchListener(new android.view.View.OnTouchListener()
						{
							onTouch: function(v, event)
							{
								if(minecraftStyleForButtons)
									MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
								onTouchWeaponShoot(event, currentGun, true);
								return false;
							}
						});
					}
				}));
			} else
			{
				// creative with reload option disabled
				currentActivity.runOnUiThread(new java.lang.Runnable(
				{
					run: function()
					{
						shotText.setOnTouchListener(new android.view.View.OnTouchListener()
						{
							onTouch: function(v, event)
							{
								if(minecraftStyleForButtons)
									MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
								onTouchWeaponShoot(event, currentGun, false);
								return false;
							}
						});
					}
				}));
			}
		}

		// single shot weapons
		if(currentGun.buttonType == BUTTON_TYPE_ON_CLICK)
		{
			// load click event
			onClickRunnable = (new java.lang.Runnable(
			{
				run: function()
				{
					onClickWeaponShoot(currentGun);
				}
			}));
		}

		// guns with warmup
		if(currentGun.buttonType == BUTTON_TYPE_ON_TOUCH_WITH_WAIT)
		{
			// load spin sound
			try {
				gunSpinSound.reset();
				gunSpinSound.setDataSource(Sound.getOriginalPath(currentGun.spinSound));
				gunSpinSound.setLooping(true);
				gunSpinSound.setVolume(generalVolume, generalVolume);
				gunSpinSound.prepareAsync();
			} catch(e) {
				try {
					// try again, maybe we had a weird sound error
					gunSpinSound.reset();
					gunSpinSound.setDataSource(Sound.getOriginalPath(currentGun.spinSound));
					gunSpinSound.setLooping(true);
					gunSpinSound.setVolume(generalVolume, generalVolume);
					gunSpinSound.prepareAsync();
				} catch(e) { /* sounds not installed */ }
			}

			// load touch events
			if(shouldReload())
			{
				// survival or creative with reload option enabled
				currentActivity.runOnUiThread(new java.lang.Runnable(
				{
					run: function()
					{
						shotText.setOnTouchListener(new android.view.View.OnTouchListener()
						{
							onTouch: function(v, event)
							{
								if(minecraftStyleForButtons)
									MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
								onTouchWithWaitWeaponShoot(event, currentGun, true);
								return false;
							}
						});
					}
				}));
			} else
			{
				// creative with reload option disabled
				currentActivity.runOnUiThread(new java.lang.Runnable(
				{
					run: function()
					{
						shotText.setOnTouchListener(new android.view.View.OnTouchListener()
						{
							onTouch: function(v, event)
							{
								if(minecraftStyleForButtons)
									MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
								onTouchWithWaitWeaponShoot(event, currentGun, false);
								return false;
							}
						});
					}
				}));
			}
		}

		// set ammo text
		updateAmmoText(currentGun)
	} else
	{
		if(isItemAGun(previousItem))
		{
			// the item before was weapon, now it isn't
			removeShootAndAimButtons();
		}
	}

	// medical kit
	if(currentItem == MEDICAL_KIT_ID)
	{
		//
		displayHealButton();
	}

	// grenade
	if(currentItem == GRENADE.id)
	{
		displayShootButton();

		// load click event
		onClickRunnable = (new java.lang.Runnable(
		{
			run: function()
			{
				shootGrenadeHand(GRENADE);
				if(Level.getGameMode() == GameMode.SURVIVAL)
					Player.decreaseByOneCarriedItem();
			}
		}));
		setAmmoText(" ");
	}

	// fragment
	if(currentItem == FRAGMENT.id)
	{
		displayShootButton();

		// load click event
		onClickRunnable = (new java.lang.Runnable(
		{
			run: function()
			{
				shootGrenadeHand(FRAGMENT);
				if(Level.getGameMode() == GameMode.SURVIVAL)
					Player.decreaseByOneCarriedItem();
			}
		}));
		setAmmoText(" ");
	}

	// molotov
	if(currentItem == MOLOTOV.id)
	{
		displayShootButton();

		// load click event
		onClickRunnable = (new java.lang.Runnable(
		{
			run: function()
			{
				shootGrenadeHand(MOLOTOV);
				if(Level.getGameMode() == GameMode.SURVIVAL)
					Player.decreaseByOneCarriedItem();
			}
		}));
		setAmmoText(" ");
	}

	// smoke grenade
	if(currentItem == SMOKE.id)
	{
		displayShootButton();

		// load click event
		onClickRunnable = (new java.lang.Runnable(
		{
			run: function()
			{
				shootGrenadeHand(SMOKE);
				if(Level.getGameMode() == GameMode.SURVIVAL)
					Player.decreaseByOneCarriedItem();
			}
		}));
		setAmmoText(" ");
	}

	if(currentItem == BINOCULARS.id || currentItem == ZOOM_BINOCULARS.id)
	{
		if(needsToLoadTheUI(currentItem, true))
			displayAimButton();
	}

	// DesnoGuns info
	if(currentItem == INFO_ITEM_ID)
	{
		//
		displayInfoItemUI();
	}

	// addons tabs items
	if(currentItem >= TABS_STARTING_ID && currentItem <= TABS_ENDING_ID)
	{
		if(loadedAddons.length > (currentItem - TABS_STARTING_ID))
			clientMessage("All the weapons after this item were added by \"" + loadedAddons[currentItem - TABS_STARTING_ID].name + "\"");
	}
}

function modTick()
{
	ModTickFunctions.checkChangedCarriedItem();

	ModTickFunctions.onTouchShooting();

	ModTickFunctions.bulletsControl();

	ModTickFunctions.molotov();

	ModTickFunctions.smokeGrenade();
	
	ModTickFunctions.parachute();

	ModTickFunctions.riotShield();
	
	ModTickFunctions.unstuckPigmenEE();

	/*if(DEBUG1)
	{
		ModPE.showTipMessage(Entity.getVelY(Player.getEntity()));
	}*/
}

var ModTickFunctions = {

	checkChangedCarriedItem: function()
	{
		if(Player.getCarriedItem() != previousCarriedItem)
			changeCarriedItemHook(Player.getCarriedItem(), previousCarriedItem);
		else
		{
			// switching between items with same id but different damage for example
			if(Player.getSelectedSlotId() != previousSlotId)
			{
				changeCarriedItemHook(previousCarriedItem, previousCarriedItem);
			}
		}
		previousCarriedItem = Player.getCarriedItem();
		previousSlotId = Player.getSelectedSlotId();
	},

	onTouchShooting: function()
	{
		if(isShooting && shootingRunnable != null)
		{
			shootingRunnable.run();
		} else
		{
			if(currentShotTicks != 0)
				currentShotTicks = 0;
		}
	},

	bulletsControl: function()
	{
		for(var i in allGuns)
		{
			for(var j in allGuns[i].bulletsArray)
			{
				if(allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH)
				{
					var arrow = allGuns[i].bulletsArray[j];
					var xArrow = Entity.getX(arrow.entity);
					var yArrow = Entity.getY(arrow.entity);
					var zArrow = Entity.getZ(arrow.entity);

					if(arrow.previousX == xArrow && arrow.previousY == yArrow && arrow.previousZ == zArrow && Entity.getVelY(arrow.entity) == 0)
					{
						// arrow touched the ground
						Level.explode(xArrow, yArrow, zArrow, allGuns[i].bulletsExplosionRadius);

						Entity.remove(arrow.entity);
						allGuns[i].bulletsArray.splice(j, 1);
					} else
					{
						if(xArrow == 0 && yArrow == 0 && zArrow == 0)
						{
							// arrow hit an entity, the previous position may be near the player, we need to check it
							if(!checkProximityOfPoints(arrow.previousX, arrow.previousY, arrow.previousZ, Player.getX(), Player.getY(), Player.getZ(), 5))
								Level.explode(arrow.previousX, arrow.previousY, arrow.previousZ, allGuns[i].bulletsExplosionRadius);

							allGuns[i].bulletsArray.splice(j, 1);
						} else
						{
							arrow.previousX = xArrow;
							arrow.previousY = yArrow;
							arrow.previousZ = zArrow;
						}
					}
				}

				if(allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME)
				{
					var arrow = allGuns[i].bulletsArray[j];
					var xArrow = Entity.getX(arrow.entity);
					var yArrow = Entity.getY(arrow.entity);
					var zArrow = Entity.getZ(arrow.entity);

					if(xArrow != 0 && yArrow != 0 && zArrow != 0)
					{
						// save the last position of the arrow, needed if the arrow is removed before it explodes
						arrow.previousX = xArrow;
						arrow.previousY = yArrow;
						arrow.previousZ = zArrow;
					}
				}

				if(allGuns[i].bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
				{
					var grenade = allGuns[i].bulletsArray[j];
					var xGrenade = Entity.getX(grenade.entity);
					var yGrenade = Entity.getY(grenade.entity);
					var zGrenade = Entity.getZ(grenade.entity);

					if(xGrenade == 0 && yGrenade == 0 && zGrenade == 0)
					{
						// the snowball has been removed

						// fire
						var xStarting = Math.floor(grenade.previousX) - Math.floor(allGuns[i].grenadesExplosionDiameter / 2);
						var yStarting = Math.floor(grenade.previousY) - Math.floor(allGuns[i].grenadesExplosionDiameter / 2);
						var zStarting = Math.floor(grenade.previousZ) - Math.floor(allGuns[i].grenadesExplosionDiameter / 2);
						for(var xExplosion = xStarting; xExplosion <= xStarting + allGuns[i].grenadesExplosionDiameter; xExplosion++)
						{
							for(var yExplosion = yStarting; yExplosion <= yStarting + allGuns[i].grenadesExplosionDiameter; yExplosion++)
							{
								for(var zExplosion = zStarting; zExplosion <= zStarting + allGuns[i].grenadesExplosionDiameter; zExplosion++)
								{
									var setFire = true;
									if(xExplosion == xStarting || xExplosion == xStarting + allGuns[i].grenadesExplosionDiameter || zExplosion == zStarting || zExplosion == zStarting + allGuns[i].grenadesExplosionDiameter)
									{
										setFire = java.util.Random().nextBoolean();
									}
									if(setFire && (Level.getTile(xExplosion, yExplosion, zExplosion) == 0 || Level.getTile(xExplosion, yExplosion, zExplosion) == 31))
									{
										Level.setTile(xExplosion, yExplosion, zExplosion, 51);
									}
								}
							}
						}

						Sound.playFromSimplePath("desnoguns/fire-explosion.mp3", grenade.previousX, grenade.previousY, grenade.previousZ);

						Entity.remove(grenade.entity);
						allGuns[i].bulletsArray.splice(j, 1);
					}else
					{
						grenade.previousX = xGrenade;
						grenade.previousY = yGrenade;
						grenade.previousZ = zGrenade;
					}
				}

				if(allGuns[i].hasParticleTrail)
				{
					// here the entity object may have been removed by bullet types
					if(allGuns[i].bulletsArray[j] != null)
					{
						var arrow = allGuns[i].bulletsArray[j];
						var xArrow = Entity.getX(arrow.entity);
						var yArrow = Entity.getY(arrow.entity);
						var zArrow = Entity.getZ(arrow.entity);

						if(arrow.previousX == xArrow && arrow.previousY == yArrow && arrow.previousZ == zArrow && Entity.getVelY(arrow.entity) == 0)
						{
							// arrow is on the ground
							if(!(allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH || allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME || allGuns[i].bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL))
							{
								// we can't remove the arrow if it is needed somewhere else in the code, for these bullet types the arrow is needed
								Entity.remove(arrow.entity);
								allGuns[i].bulletsArray.splice(j, 1);
							}
						} else
						{
							if(xArrow == 0 && yArrow == 0 && zArrow == 0)
							{
								// arrow hit an entity
								if(!(allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH || allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME || allGuns[i].bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL))
								{
									// we can't remove the entity object of the arrow if it is needed somewhere else in the code, for these bullet types it is needed
									allGuns[i].bulletsArray.splice(j, 1);
								}
							} else
							{
								arrow.previousX = xArrow;
								arrow.previousY = yArrow;
								arrow.previousZ = zArrow;

								var particleId;
								if(allGuns[i].particleTrailId != null)
									particleId = allGuns[i].particleTrailId;
								else
									particleId = 4;

								var particleDistance;
								if(allGuns[i].particleTrailDistance != null)
									particleDistance = allGuns[i].particleTrailDistance;
								else
									particleDistance = 3;

									Level.addParticle(allGuns[i].particleTrailId, xArrow, yArrow, zArrow, Entity.getVelX(arrow.entity) / particleDistance, Entity.getVelY(arrow.entity) / particleDistance, Entity.getVelZ(arrow.entity) / particleDistance, 1);
							}
						}
					}
				}
			}
		}
	},

	molotov: function()
	{
		for(var i in MOLOTOV.grenadesArray)
		{
			var grenade = MOLOTOV.grenadesArray[i];
			var xGrenade = Entity.getX(grenade.entity);
			var yGrenade = Entity.getY(grenade.entity);
			var zGrenade = Entity.getZ(grenade.entity);
			//ModPE.showTipMessage("x: " + Math.floor(xGrenade) + "y: " + Math.floor(yGrenade) + "z: " + Math.floor(zGrenade));

			if(xGrenade == 0 && yGrenade == 0 && zGrenade == 0)
			{
				// the entity has been removed

				// fire!!!!
				var xStarting = Math.floor(grenade.previousX) - Math.floor(MOLOTOV.grenadesExplosionDiameter / 2);
				var yStarting = Math.floor(grenade.previousY) - Math.floor(MOLOTOV.grenadesExplosionDiameter / 2);
				var zStarting = Math.floor(grenade.previousZ) - Math.floor(MOLOTOV.grenadesExplosionDiameter / 2);
				for(var xExplosion = xStarting; xExplosion <= xStarting + MOLOTOV.grenadesExplosionDiameter; xExplosion++)
				{
					for(var yExplosion = yStarting; yExplosion <= yStarting + MOLOTOV.grenadesExplosionDiameter; yExplosion++)
					{
						for(var zExplosion = zStarting; zExplosion <= zStarting + MOLOTOV.grenadesExplosionDiameter; zExplosion++)
						{
							var setFire = true;
							if(xExplosion == xStarting || xExplosion == xStarting + MOLOTOV.grenadesExplosionDiameter || zExplosion == zStarting || zExplosion == zStarting + MOLOTOV.grenadesExplosionDiameter)
							{
								setFire = java.util.Random().nextBoolean();
							}
							if(setFire && (Level.getTile(xExplosion, yExplosion, zExplosion) == 0 || Level.getTile(xExplosion, yExplosion, zExplosion) == 31))
							{
								Level.setTile(xExplosion, yExplosion, zExplosion, 51);
							}
						}
					}
				}

				Sound.playFromSimplePath("desnoguns/MolotovExplosion.mp3", grenade.previousX, grenade.previousY, grenade.previousZ);

				//clientMessage("x: " + Math.floor(grenade.previousX ) + "y: " + Math.floor(grenade.previousY) + "z: " + Math.floor(grenade.previousZ));
				Entity.remove(grenade.entity);
				MOLOTOV.grenadesArray.splice(i, 1);
			} else
			{
				Level.addParticle(ParticleType.flame, xGrenade, yGrenade, zGrenade, 0, 0, 0, 1);
				grenade.previousX = xGrenade;
				grenade.previousY = yGrenade;
				grenade.previousZ = zGrenade;
			}
		}
	},

	smokeGrenade: function()
	{
		for(var i in SMOKE.grenadesArray)
		{
			for(var j = 0; j < 9; j++)
			{
				if(SMOKE.grenadesArray[i] != null)
				{
					var entity = SMOKE.grenadesArray[i].entity;
					var speed;
					var randomOffset;

					var dir = getDirection(Math.floor(Math.random() * 360), Math.floor((Math.random() * 225) + 120)); // getDirection(randomYaw, randomPitch);

					var distance = Math.random() * 0.8 + 1.8;
					var x = Entity.getX(entity) + (dir.x * distance);
					var y = Entity.getY(entity) + (dir.y * (distance - 0.5)); // -0.5 to make it not a perfect sphere and add a "gravity" effect
					var z = Entity.getZ(entity) + (dir.z * distance);

					speed = Math.random() * 0.08 + 0.02;
					randomOffset = Math.random() - 0.5;
					Level.addParticle(SMOKE.smokeParticle, x + randomOffset, y + randomOffset, z + randomOffset, dir.x * speed, dir.y * speed * 0.8, dir.z * speed, 1);
					
					speed = Math.random() * 0.08 + 0.02;
					randomOffset = Math.random() - 0.5;
					Level.addParticle(SMOKE.smokeParticle, x + randomOffset, y + randomOffset, z + randomOffset, dir.x * speed, dir.y * speed * 0.8, dir.z * speed, 1);
				}
			}

			if(SMOKE.grenadesArray[i] != null)
			{
				SMOKE.grenadesArray[i].effectsTick++;
				if(SMOKE.grenadesArray[i].effectsTick >= 10)
				{
					for(var j in players)
					{
						if(checkProximity(SMOKE.grenadesArray[i].entity, players[j], 5))
						{
							Entity.addEffect(players[j], MobEffect.movementSlowdown, 100, 1, false, false);
							Entity.addEffect(players[j], MobEffect.weakness, 100, 1, false, false);
							Entity.addEffect(players[j], MobEffect.blindness, 40, 0, false, false);
						}
					}
					SMOKE.grenadesArray[i].effectsTick = 0;
				}
			}
		}
	},

	parachute: function()
	{
		if(Player.getCarriedItem() == PARACHUTE_ID)
		{
			// player will hit the ground soon
			if(isParachuting && Level.getTile(Math.floor(Player.getX()), Math.floor(Player.getY()) - 2, Math.floor(Player.getZ())) > 0)
			{
				if(Entity.getVelY(Player.getEntity()) == VEL_Y_OFFSET)
				{
					// STOP parachuting
					isParachuting = false;

					if(Level.getGameMode() == GameMode.SURVIVAL)
					{
						// Entity.removeEffect(entity, id) doesn't remove particles of the effect https://github.com/zhuowei/MCPELauncher/issues/241
						//Entity.removeEffect(Player.getEntity(), MobEffect.jump);
						Entity.removeAllEffects(Player.getEntity());
						Player.damageCarriedItem();
					}
				}
			}

			// player is falling, oh no! We have to help him.
			if(Entity.getVelY(Player.getEntity()) <= -0.5)
			{
				// START parachuting
				Sound.playFromSimplePath("desnoguns/benboncan_parachute.mp3");
				isParachuting = true;

				if(Level.getGameMode() == GameMode.SURVIVAL)
					Entity.addEffect(Player.getEntity(), MobEffect.jump, 999999, 254, false, false);
			}
		} else
		{
			if(isParachuting)
			{
				// STOP parachuting
				isParachuting = false;

				if(Level.getGameMode() == GameMode.SURVIVAL)
				{
					// Entity.removeEffect(entity, id) doesn't remove particles of the effect https://github.com/zhuowei/MCPELauncher/issues/241
					//Entity.removeEffect(Player.getEntity(), MobEffect.jump);
					Entity.removeAllEffects(Player.getEntity());

					//Player.damageCarriedItem(); // TODO fix: the item is not carried
					//Player.damageItemInInventory(item)
				}
			}
		}
		if(isParachuting)
		{
			// thanks to Anti for this line of code, it works better than making the player riding a chicken (that was my idea)
			Entity.setVelY(Player.getEntity(), -0.10);
		}
	},

	riotShield: function()
	{
		if(Player.getCarriedItem() == RIOT_SHIELD_ID)
		{
			Entity.addEffect(Player.getEntity(), MobEffect.damageResistance, 4, 3, false, false);
			Entity.addEffect(Player.getEntity(), MobEffect.movementSlowdown, 4, 0, false, false);

			if(Level.getGameMode() == GameMode.SURVIVAL)
				Player.damageCarriedItem();
		}
	},

	unstuckPigmenEE: function()
	{
		// Sin0psysS's spawning pattern code (with some changes by me)
		if(unstuck >= 1)
		{
			for(var i in pigmen)
			{
				if(Level.getTile(Math.floor(Entity.getX(pigmen[i])), Math.floor(Entity.getY(pigmen[i])), Math.floor(Entity.getZ(pigmen[i]))) != 0)
				{
					Entity.setPosition(pigmen[i], Entity.getX(pigmen[i]), Entity.getY(pigmen[i]) + 1, Entity.getZ(pigmen[i]));
				}
			}
			unstuck--;
		}
	}
};


//########################################################################################################################################################
// Added functions (No GUI and No render)
//########################################################################################################################################################

//########## LOAD ADDONS functions ##########
var loadedAddons = [];

function loadWeaponsFromAddons()
{
	var arrobject = [];
	net.zhuoweizhang.mcpelauncher.ScriptManager.callScriptMethod("loadWeaponsHook", arrobject);
}

function loadWeaponsCallback(weaponsArray, addonName, addonDescription)
{
	if(addonName == null)
		addonName = "No Name";
	if(addonDescription == null)
		addonDescription = "No Description";

	var result = canAddonBeLoaded(weaponsArray);
	if(result == "yes")
	{
		// everything is ok with the addon, add the guns in the game
		loadedAddons.push(new addonClass(weaponsArray, addonName, addonDescription));
	} else
	{
		addonErrorUI(addonName, result);
	}
}

function canAddonBeLoaded(weaponsArray)
{
	if(Array.isArray(weaponsArray))
	{
		for(var i in weaponsArray)
		{
			if(weaponsArray[i].weaponType == "gun")
			{
				var result = canGunBeLoaded(weaponsArray[i]);
				if(result != "yes")
				{
					return result;
				}
			}
		}

		return "yes";
	} else
	{
		return "The addon hasn't passed to the mod an array of weapons.";
	}
}

function canGunBeLoaded(gun)
{
	if(gun.name == null)
		return "You must set a name for every gun you add.";

	var defaultError = "Error in gun.name \"" + gun.name + "\": ";

	// the colon here fixes a bug with the code highlighting of sublime text:

	for(var i in defaultGuns)
	{
		if(defaultGuns[i].name == gun.name)
			return defaultError + "the name \"" + gun.name + "\" is already used in the DesnoGuns mod and can't be added.";
	}

	if(getGunTypeIdFromString(gun.gunType) == "error")
		return defaultError + gun.gunType + " is not a known gunType.";

	if(getButtonTypeIdFromString(gun.buttonType) == "error")
		return defaultError + gun.buttonType + " is not a known buttonType.";

	if(gun.id == null)
		return defaultError + "gun.id is not specified.";
	if(gun.id <= 512 || gun.id >= 4096)
		return defaultError + "gun.id must be > 512 and < 4096.";
	if(isItemAnIdTheModAlreadyUse(gun.id))
		return defaultError + "gun.id is already used in the mod.";

	if(gun.fireRate == null)
		return defaultError + "gun.fireRate is not specified.";
	if(gun.fireRate < 1)
		return defaultError + "gun.fireRate must be > or = 1.";

	if(gun.recoil == null)
		return defaultError + "gun.recoil is not specified.";
	if(gun.recoil <= 0)
		return defaultError + "gun.recoil must be > 0.";

	if(gun.bulletSpeed == null && getShotTypeIdFromString(gun.shotType) != SHOT_TYPE_FLAMETHROWER)
		return defaultError + "gun.bulletSpeed is not specified.";
	if(gun.bulletSpeed <= 0 && getShotTypeIdFromString(gun.shotType) != SHOT_TYPE_FLAMETHROWER)
		return defaultError + "gun.bulletSpeed must be > 0.";

	if(gun.zoomLevel == null)
		return defaultError + "gun.zoomLevel is not specified.";
	if(gun.zoomLevel <= 0 || gun.zoomLevel >= 68)
		return defaultError + "gun.zoomLevel must be > 0 and < 68.";

	if(gun.accuracy == null)
		return defaultError + "gun.accuracy is not specified.";
	if(gun.accuracy <= 0 || gun.accuracy >= 45)
		return defaultError + "gun.zoomLevel must be > 0 and < 45.";

	if(gun.texture == null)
		return defaultError + "gun.texture is not specified.";

	if(gun.ammo == null)
		return defaultError + "gun.ammo is not specified.";
	if(gun.ammo <= 0)
		return defaultError + "gun.ammo must be > 0.";

	if(gun.smoke != null && gun.smoke < 0)
		return defaultError + "gun.smoke must be > or = 0.";

	if(gun.recipeShape != null && gun.recipeShape.length != 3)
		return defaultError + "gun.recipeShape must be an array with 3 strings.";

	if(gun.recipeDescription != null && gun.recipeDescription.length % 3 != 0)
		return defaultError + "gun.recipeDescription doesn't seem to be well formatted.";

	// BEHAVIOR

	if(getShotTypeIdFromString(gun.shotType) == "error")
		return defaultError + gun.shotType + " is not a known shotType.";

	if(getBulletTypeIdFromString(gun.bulletType) == "error" && getShotTypeIdFromString(gun.shotType) != SHOT_TYPE_FLAMETHROWER)
		return defaultError + gun.bulletType + " is not a known bulletType.";

	if(getBulletTypeIdFromString(gun.bulletType) == BULLET_TYPE_CUSTOM_ENTITY && !(gun.customBulletId > 0))
		return defaultError + gun.customBulletId + " must be > 0 when using the \"custom\" bulletType.";

	if(gun.particleTrailDistance != null && gun.particleTrailDistance <= 0)
		return defaultError + "gun.particleTrailDistance must be > 0.";

	if(gun.particleTrailId != null && gun.particleTrailId <= 0)
		return defaultError + "gun.particleTrailId must be > 0.";

	if(getShotTypeIdFromString(gun.shotType) == SHOT_TYPE_SHOTGUN && !(gun.shotgunBullets > 0))
		return defaultError + "gun.shotgunBullets must be > 0.";

	if(getShotTypeIdFromString(gun.shotType) == SHOT_TYPE_SHOTGUN && !(gun.shotgunDegreesSpread > 0))
		return defaultError + "gun.shotgunDegreesSpread must be > 0.";

	if(getBulletTypeIdFromString(gun.bulletType) == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME && !(gun.bulletsExplosionDelay > 0))
		return defaultError + "gun.bulletsExplosionDelay must be > 0.";

	if(getBulletTypeIdFromString(gun.bulletType) == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME && !(gun.bulletsExplosionRadius > 0))
		return defaultError + "gun.bulletsExplosionRadius must be > 0.";

	if(getBulletTypeIdFromString(gun.bulletType) == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH && !(gun.bulletsExplosionRadius > 0))
		return defaultError + "gun.bulletsExplosionRadius must be > 0.";

	// SOUNDS

	if(!gun.hasntShootingSound)
	{
		if(gun.sound == null)
			return defaultError + "gun.sound is not specified.";
		if(!(Sound.isCustom(gun.sound) || Sound.isDefault(gun.sound)))
			return defaultError + "gun.sound must be a string starting with custom/ if a custom sound from a texture pack is used, or desnoguns/ if a pre-installed sound is used.";
	}
	
	if(gun.reloadSound == null)
		return defaultError + "gun.reloadSound is not specified.";
	if(!(Sound.isCustom(gun.reloadSound) || Sound.isDefault(gun.reloadSound)))
		return defaultError + "gun.reloadSound must be a string starting with custom/ if a custom sound from a texture pack is used, or desnoguns/ if a pre-installed sound is used.";

	if(getButtonTypeIdFromString(gun.buttonType) == BUTTON_TYPE_ON_TOUCH_WITH_WAIT)
	{
		if(gun.hasRandomWarmupSound)
			return defaultError + "gun.hasRandomWarmupSound is not yet supported.";
		if(gun.warmupSound == null)
			return defaultError + "gun.warmupSound is not specified.";
		if(!(Sound.isCustom(gun.warmupSound) || Sound.isDefault(gun.warmupSound)))
			return defaultError + "gun.warmupSound must be a string starting with custom/ if a custom sound from a texture pack is used, or desnoguns/ if a pre-installed sound is used.";

		if(gun.spinSound == null)
			return defaultError + "gun.spinSound is not specified.";
		if(!(Sound.isCustom(gun.spinSound) || Sound.isDefault(gun.spinSound)))
			return defaultError + "gun.spinSound must be a string starting with custom/ if a custom sound from a texture pack is used, or desnoguns/ if a pre-installed sound is used.";

		if(!gun.hasntCooldownSound)
		{
			if(gun.cooldownSound == null)
				return defaultError + "gun.cooldownSound is not specified.";
			if(!(Sound.isCustom(gun.cooldownSound) || Sound.isDefault(gun.cooldownSound)))
				return defaultError + "gun.cooldownSound must be a string starting with custom/ if a custom sound from a texture pack is used, or desnoguns/ if a pre-installed sound is used.";
		}
	}


	return "yes";
}

function getGunTypeIdFromString(gunType)
{
	if(typeof gunType == "string")
		gunType = gunType.toLowerCase();
	else
		return "error";

	if(gunType == "assault_rifle")
		return GUN_TYPE_ASSAULT_RIFLE;
	if(gunType == "sub_machine")
		return GUN_TYPE_SUB_MACHINE;
	if(gunType == "light_machine")
		return GUN_TYPE_LIGHT_MACHINE;
	if(gunType == "sniper_rifle")
		return GUN_TYPE_SNIPER_RIFLE;
	if(gunType == "shotgun")
		return GUN_TYPE_SHOTGUN;
	if(gunType == "machine_pistol")
		return GUN_TYPE_MACHINE_PISTOL;
	if(gunType == "handgun")
		return GUN_TYPE_HANDGUN;
	if(gunType == "launcher")
		return GUN_TYPE_LAUNCHER;
	if(gunType == "minigun")
		return GUN_TYPE_MINIGUN;

	return "error";
}

function getButtonTypeIdFromString(buttonType)
{
	if(typeof buttonType == "string")
		buttonType = buttonType.toLowerCase();
	else
		return "error";

	if(buttonType == "on_click")
		return BUTTON_TYPE_ON_CLICK;
	if(buttonType == "on_touch")
		return BUTTON_TYPE_ON_TOUCH;
	if(buttonType == "on_touch_with_wait")
		return BUTTON_TYPE_ON_TOUCH_WITH_WAIT;

	return "error";
}

function getShotTypeIdFromString(shotType)
{
	if(typeof shotType == "string")
		shotType = shotType.toLowerCase();
	else
		return "error";

	if(shotType == "normal")
		return SHOT_TYPE_NORMAL;
	if(shotType == "shotgun")
		return SHOT_TYPE_SHOTGUN;
	if(shotType == "flamethrower")
		return SHOT_TYPE_FLAMETHROWER;

	return "error";
}

function getBulletTypeIdFromString(bulletType)
{
	if(typeof bulletType == "string")
		bulletType = bulletType.toLowerCase();
	else
		return "error";

	if(bulletType == "normal")
		return BULLET_TYPE_NORMAL;
	if(bulletType == "normal_explosive_on_touch")
		return BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH;
	if(bulletType == "normal_explosive_on_time")
		return BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME;
	if(bulletType == "tnt")
		return BULLET_TYPE_TNT;
	if(bulletType == "snowball")
		return BULLET_TYPE_SNOWBALL;
	if(bulletType == "custom")
		return BULLET_TYPE_CUSTOM_ENTITY;

	return "error";
}

function addonClass(array, addonName, addonDescription)
{
	this.name = addonName;
	this.description = addonDescription;
	this.weaponsArray = array;
}
//########## LOAD ADDONS functions - END ##########


//########## LOADED ADDONS MANAGING functions ##########
function addLoadedAddonsInGame()
{
	// put a limit on the number of addons
	if(isPro())
	{
		//
		loadedAddons = limitLoadedAddonsArray(loadedAddons, 20);
	}
	else
	{
		if(loadedAddons.length > 2)
		{
			loadedAddons = limitLoadedAddonsArray(loadedAddons, 2);

			currentActivity.runOnUiThread(new java.lang.Runnable() {
				run: function() {
					android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: You can't have more than 2 addons enabled without the Pro Key."), 1).show();
				}
			});
		}
	}

	for(var i in loadedAddons)
	{
		// add tab
		addNewTab(i);

		// add guns
		for(var j in loadedAddons[i].weaponsArray)
		{
			var weapon = loadedAddons[i].weaponsArray[j];
			if(weapon.weaponType == "gun")
			{
				if(!isItemAGun(weapon.id)) // check if the id of this new gun isn't already present in the allGuns array
				{
					// this id isn't already used, we can add it safely to the game
					var gun = convertGunsStringsInIds(weapon);
					installSoundsOfGun(gun, loadedAddons[i].name);
					addNewGunFromAddon(gun, loadedAddons[i].name);
				} else
				{
					// the id of this new weapon is already used in another addon (the id was already checked for mod's ids)
					currentActivity.runOnUiThread(new java.lang.Runnable() {
						run: function() {
							android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: \"" + weapon.name + "\" of \"" + loadedAddons[i].name + "\" wasn't added because the id was already used by another addon."), 1).show();
						}
					});
				}
			}
		}
	}

	// show the user how many addons have been enabled
	if(loadedAddons.length > 0)
	{
		currentActivity.runOnUiThread(new java.lang.Runnable() {
			run: function() {
				android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: " + loadedAddons.length + " addon" + ((loadedAddons.length > 1) ? "s" : "") + " enabled!"), 0).show();
			}
		});
	}
}

function limitLoadedAddonsArray(array, limit)
{
	if(array.length > limit)
	{
		var newArray = [];
		for(var i = 0; i < limit; i++)
		{
			newArray[i] = array[i];
		}
		return newArray;
	} else
	{
		return array;
	}
}

function convertGunsStringsInIds(gun)
{
	// at this point we are already sure there aren't any errors in gunType and buttonType, they can be safely converted

	// the gun object is a reference to the object in the addon, we don't want to modify it directly
	var newGun = JSON.parse(JSON.stringify(gun)); // http://stackoverflow.com/questions/18359093/how-to-copy-javascript-object-to-new-variable-not-by-reference

	newGun.gunType = getGunTypeIdFromString(newGun.gunType);
	newGun.buttonType = getButtonTypeIdFromString(newGun.buttonType);
	newGun.shotType = getShotTypeIdFromString(newGun.shotType);
	newGun.bulletType = getBulletTypeIdFromString(newGun.bulletType);

	// remove pro features
	newGun.hasManualZoom = false;
	if(newGun.bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
		newGun.bulletType = BULLET_TYPE_NORMAL;

	return newGun;
}

function addNewTab(index)
{
	var id = (TABS_STARTING_ID + parseInt(index));

	Item.defineItem(id, "transparent", 0, loadedAddons[index].name + " Divider", 1);
	Item.setCategory(id, ITEM_CATEGORY_TOOL);
	Player.addItemCreativeInv(id, 1);
}

function installSoundsOfGun(gun, addonName)
{
	if((!gun.hasntShootingSound) && gun.sound != null)
		installSoundFromSimplePath(gun.sound, gun.name, addonName);

	if(gun.reloadSound != null)
		installSoundFromSimplePath(gun.reloadSound, gun.name, addonName);

	if(gun.warmupSound != null) // Random warmup sound not supported
		installSoundFromSimplePath(gun.warmupSound, gun.name, addonName);

	if(gun.spinSound != null)
		installSoundFromSimplePath(gun.spinSound, gun.name, addonName);

	if((!gun.hasntCooldownSound) && gun.cooldownSound != null)
		installSoundFromSimplePath(gun.cooldownSound, gun.name, addonName);
}

function installSoundFromSimplePath(simplePath, gunName, addonName)
{
	try
	{
		// sound in form: custom/path-in-texture-pack and it will be saved in ...sdcard/games/com.mojang/desnoguns-temp/path-in-texture-pack
		var pathInTexturePack = simplePath.substring(7);
		writeFileFromInputStream(ModPE.openInputStreamFromTexturePack(pathInTexturePack), Sound.getOriginalPath(simplePath));
	} catch(e)
	{
		texturePackNotIntsalledWarningUI("Seems that you haven't installed the texture pack of \"" + addonName + "\".<br><br>Please install the texture pack of the addon and <b>restart BlockLauncher</b>.<br><br>Error in " + gunName + ": the sound \"" + simplePath + "\" hasn't been found.");
		ModPE.log(getLogText() + "installSoundFromSimplePath(): Error: " + e);
	}
}

function addNewGunFromAddon(gun, addonName)
{
	// add gun to the main array
	allGuns.push(new gunClass(gun));

	// create gun
	if(gun.textureNumber > 0)
	{
		try
		{
			ModPE.setItem(gun.id, gun.texture, gun.textureNumber, gun.name, 1);
		} catch(e)
		{
			ModPE.setItem(gun.id, "skull_zombie", 0, gun.name, 1);
			texturePackNotIntsalledWarningUI("Seems that you haven't installed the texture pack of \"" + addonName + "\".<br><br>Please install the texture pack of the addon and <b>restart BlockLauncher</b>.<br><br>Error in " + gun.name + ": the texture \"" + gun.texture + "\" hasn't been found.");
		}	
	}
	else
	{
		try
		{
			ModPE.setItem(gun.id, gun.texture, 0, gun.name, 1);
		} catch(e)
		{
			ModPE.setItem(gun.id, "skull_zombie", 0, gun.name, 1);
			texturePackNotIntsalledWarningUI("Seems that you haven't installed the \"" + addonName + "\" texture pack.<br><br>Please install the texture pack of the addon and <b>restart BlockLauncher</b>.<br><br>Error in " + gun.name + ": the texture \"" + gun.texture + "\" hasn't been found.");
		}	
	}
	addGunCraftingRecipe(gun);
	Item.setMaxDamage(gun.id, gun.ammo);
	Item.setVerticalRender(gun.id);
	Item.setCategory(gun.id, ITEM_CATEGORY_TOOL);
	Player.addItemCreativeInv(gun.id, 1);
}
//########## LOADED ADDONS MANAGING functions - END ##########


//########## ADD GUNS TO GAME functions ##########
function addNewGun(gun)
{
	// add gun to the main array
	allGuns.push(new gunClass(gun));

	// create gun
	if(gun.textureNumber > 0)
		Item.defineItem(gun.id, gun.texture, gun.textureNumber, gun.name, 1);
	else
		Item.defineItem(gun.id, gun.texture, 0, gun.name, 1);
	addGunCraftingRecipe(gun);
	Item.setMaxDamage(gun.id, gun.ammo);
	Item.setVerticalRender(gun.id);
	Item.setCategory(gun.id, ITEM_CATEGORY_TOOL);
	Player.addItemCreativeInv(gun.id, 1);
}

function addGunCraftingRecipe(gun)
{
	if(gun.recipeShape == null || gun.recipeDescription == null)
		Item.addShapedRecipe(gun.id, 1, 0, getDefaultCraftingRecipeForGunType(gun.gunType), ["i", 265, 0, "r", 331, 0, "a", AMMO_ASSAULT_RIFLE_ID, 0, "b", AMMO_SUB_MACHINE_ID, 0, "c", AMMO_LIGHT_MACHINE_ID, 0, "d", AMMO_SNIPER_RIFLE_ID, 0, "e", AMMO_SHOTGUN_ID, 0, "f", AMMO_MACHINE_PISTOL_ID, 0, "g", AMMO_HANDGUN_ID, 0, "h", AMMO_LAUNCHER_ID, 0, "j", AMMO_MINIGUN_ID, 0]);
	else
		Item.addShapedRecipe(gun.id, 1, 0, gun.recipeShape, gun.recipeDescription);
}

function getDefaultCraftingRecipeForGunType(gunType)
{
	switch(gunType)
	{
		case GUN_TYPE_ASSAULT_RIFLE:
		{
			return [
				" a ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_SUB_MACHINE:
		{
			return [
				" b ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_LIGHT_MACHINE:
		{
			return [
				" c ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_SNIPER_RIFLE:
		{
			return [
				" d ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_SHOTGUN:
		{
			return [
				" e ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_MACHINE_PISTOL:
		{
			return [
				" f ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_HANDGUN:
		{
			return [
				" g ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_LAUNCHER:
		{
			return [
				" h ",
				"iri",
				"iri"];
		}
		case GUN_TYPE_MINIGUN:
		{
			return [
				" j ",
				"iri",
				"iri"];
		}
	}
}

function gunClass(gun)
{
	var gunObject = gun;
	gunObject.bulletsArray = [];

	return gunObject;
}
//########## ADD GUNS TO GAME functions - END ##########


//########## WEAPONS functions ##########
function isItemAGun(itemId)
{
	for(var i in allGuns)
	{
		if(allGuns[i].id == itemId)
			return true;
	}
	return false;
}

function isItemADefaultGun(itemId)
{
	for(var i in defaultGuns)
	{
		if(itemId == defaultGuns[i].id)
			return true;
	}
	return false;
}

function isItemAnIdTheModAlreadyUse(itemId)
{
	if(isItemADefaultGun(itemId))
		return true;


	if(itemId >= TABS_STARTING_ID && itemId <= TABS_ENDING_ID)
		return true;


	if(itemId == AMMO_ASSAULT_RIFLE_ID || itemId == AMMO_SUB_MACHINE_ID || itemId == AMMO_LIGHT_MACHINE_ID || itemId == AMMO_SNIPER_RIFLE_ID || itemId == AMMO_MACHINE_PISTOL_ID || itemId == AMMO_SHOTGUN_ID || itemId == AMMO_HANDGUN_ID || itemId == AMMO_LAUNCHER_ID || itemId == AMMO_MINIGUN_ID || itemId == AMMO_ARROW_EXPLOSIVE_ID)
		return true;


	if(itemId == INFO_ITEM_ID)
		return true;

	if(itemId == KNIFE_ID)
		return true;

	if(itemId == RIOT_SHIELD_ID)
		return true;

	if(itemId == PARACHUTE_ID)
		return true;

	if(itemId == MEDICAL_KIT_ID)
		return true;

	if(itemId == BINOCULARS.id)
		return true;

	if(itemId == ZOOM_BINOCULARS.id)
		return true;


	if(itemId == GRENADE.id)
		return true;

	if(itemId == FRAGMENT.id)
		return true;

	if(itemId == MOLOTOV.id)
		return true;

	if(itemId == SMOKE.id)
		return true;


	if(itemId == JUGGERNAUT_HELMET_ID)
		return true;

	if(itemId == JUGGERNAUT_BODY_ID)
		return true;

	if(itemId == JUGGERNAUT_PANTS_ID)
		return true;

	if(itemId == JUGGERNAUT_BOOTS_ID)
		return true;

	return false;
}

function shootGrenadeHand(grenadeObject)
{
	var yawAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * grenadeObject.accuracy;
	var pitchAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * grenadeObject.accuracy;
	var playerShootDir = getDirection(getYaw() + yawAccuracyValue, getPitch() + pitchAccuracyValue);

	var handShootDir = getDirection(getYaw() + 30, getPitch());

	if(grenadeObject.explodeOnTouch)
		var grenade = Level.spawnMob(getPlayerX() + (handShootDir.x * 2), getPlayerY() + (handShootDir.y * 2.5), getPlayerZ() + (handShootDir.z * 2), 81);
	else
	{
		var grenade = Level.spawnMob(getPlayerX() + (handShootDir.x * 2), getPlayerY() + (handShootDir.y * 2.5), getPlayerZ() + (handShootDir.z * 2), 11);
		Entity.setHealth(grenade, 99999);
	}
	setVelX(grenade, playerShootDir.x * grenadeObject.grenadeSpeed);
	setVelY(grenade, playerShootDir.y * grenadeObject.grenadeSpeed);
	setVelZ(grenade, playerShootDir.z * grenadeObject.grenadeSpeed);
	if(grenadeObject.id == GRENADE.id || grenadeObject.id == SMOKE.id)
	{
		Entity.setGrenadeRender(grenade);
		Entity.setMobSkin(grenade, "mob/grenade.png");
	}
	if(grenadeObject.id == FRAGMENT.id)
	{
		Entity.setGrenadeRender(grenade);
		Entity.setMobSkin(grenade, "mob/fraggrenade.png");
	}

	if(grenadeObject.isWithFire)
		Entity.setFireTicks(grenade, 1000);

	if(grenadeObject.id == SMOKE.id)
		grenadeObject.grenadesArray.push(new smokeGrenadeClass(grenade));
	else
		grenadeObject.grenadesArray.push(new entityClass(grenade));

	if(!grenadeObject.explodeOnTouch)
	{
		if(grenadeObject.id == FRAGMENT.id)
		{
			new android.os.Handler().postDelayed(new java.lang.Runnable(
			{
				run: function()
				{
					if(isInGame && FRAGMENT.grenadesArray.length > 0)
					{
						// push() put the object at the end so the first object ( [0] ) is the object that will explode
						var explosionX = Entity.getX(FRAGMENT.grenadesArray[0].entity);
						var explosionY = Entity.getY(FRAGMENT.grenadesArray[0].entity);
						var explosionZ = Entity.getZ(FRAGMENT.grenadesArray[0].entity);
						Entity.remove(FRAGMENT.grenadesArray[0].entity);
						FRAGMENT.grenadesArray.splice(0, 1);

						for(var i = 0; i < FRAGMENT.howManyFragments; i++)
						{
							var fragment = Level.spawnMob(explosionX + ((Math.random() * 2) - 1), explosionY + ((Math.random() * 2) - 1), explosionZ + ((Math.random() * 2) - 1), 11);
							Entity.setHealth(fragment, 99999);
							Entity.setGrenadeRender(fragment);
							Entity.setMobSkin(fragment, "mob/fraggrenade.png");
							FRAGMENT.fragmentArray.push(new entityClass(fragment));

							new android.os.Handler().postDelayed(new java.lang.Runnable(
							{
								run: function()
								{
									if(isInGame && FRAGMENT.fragmentArray.length > 0)
									{
										if(infiniteGrenade)
										{
											fragmentShit();
										} else
										{
											var fragmentX = Entity.getX(FRAGMENT.fragmentArray[0].entity);
											var fragmentY = Entity.getY(FRAGMENT.fragmentArray[0].entity);
											var fragmentZ = Entity.getZ(FRAGMENT.fragmentArray[0].entity);
											Entity.remove(FRAGMENT.fragmentArray[0].entity);
											FRAGMENT.fragmentArray.splice(0, 1);

											Level.explode(fragmentX, fragmentY, fragmentZ, FRAGMENT.grenadesExplosionRadius);
										}
									}
								}
							}), FRAGMENT.fragmentDelay);
						}

						Level.explode(explosionX, explosionY, explosionZ, FRAGMENT.grenadesExplosionRadius);
					}
				}
			}), grenadeObject.delay);
		}

		if(grenadeObject.id == GRENADE.id)
		{
			new android.os.Handler().postDelayed(new java.lang.Runnable(
			{
				run: function()
				{
					if(isInGame && GRENADE.grenadesArray.length > 0)
					{
						// push() put the object at the end so the first object ( [0] ) is the object that will explode
						var explosionX = Entity.getX(GRENADE.grenadesArray[0].entity);
						var explosionY = Entity.getY(GRENADE.grenadesArray[0].entity);
						var explosionZ = Entity.getZ(GRENADE.grenadesArray[0].entity);
						Entity.remove(GRENADE.grenadesArray[0].entity);
						GRENADE.grenadesArray.splice(0, 1);

						Level.explode(explosionX, explosionY, explosionZ, GRENADE.grenadesExplosionRadius);
					}
				}
			}), grenadeObject.delay);
		}

		if(grenadeObject.id == SMOKE.id)
		{
			new android.os.Handler().postDelayed(new java.lang.Runnable(
			{
				run: function()
				{
					if(isInGame && SMOKE.grenadesArray.length > 0)
					{
						var entity = SMOKE.grenadesArray[0].entity;
						SMOKE.grenadesArray.splice(0, 1);
						Entity.remove(entity);
					}
				}
			}), grenadeObject.delay);

			new android.os.Handler().postDelayed(new java.lang.Runnable(
			{
				run: function()
				{
					if(isInGame && SMOKE.grenadesArray.length > 0)
					{
						players = Server.getAllPlayers();
					}
				}
			}), 1);

			Sound.playFromSimplePath("desnoguns/smoke-grenade.mp3");
		}
	}
}

function fragmentShit()
{
	// different number of fragments and different delay

	var explosionX = Entity.getX(FRAGMENT.fragmentArray[0].entity);
	var explosionY = Entity.getY(FRAGMENT.fragmentArray[0].entity);
	var explosionZ = Entity.getZ(FRAGMENT.fragmentArray[0].entity);
	Entity.remove(FRAGMENT.fragmentArray[0].entity);
	FRAGMENT.fragmentArray.splice(0, 1);

	for(var i = 0; i < 2; i++)
	{
		var fragment = Level.spawnMob(explosionX + ((Math.random() * 2) - 1), explosionY + ((Math.random() * 2) - 1), explosionZ + ((Math.random() * 2) - 1), 11);
		Entity.setHealth(fragment, 99999);
		Entity.setGrenadeRender(fragment);
		Entity.setMobSkin(fragment, "mob/fraggrenade.png");
		FRAGMENT.fragmentArray.push(new entityClass(fragment));

		new android.os.Handler().postDelayed(new java.lang.Runnable(
		{
			run: function()
			{
				if(isInGame && FRAGMENT.fragmentArray.length > 0)
				{
					fragmentShit();
				}
			}
		}), FRAGMENT.fragmentDelay * 2);
	}

	Level.explode(explosionX, explosionY, explosionZ, FRAGMENT.grenadesExplosionRadius);
}

function smokeGrenadeClass(entity)
{
	var smokeObject = new entityClass(entity);
	smokeObject.effectsTick = 0;
	return smokeObject;
}

function getWeaponObject(id)
{
	if(isItemAGun(id))
		return getGun(id);

	if(id == BINOCULARS.id)
		return BINOCULARS;

	if(id == ZOOM_BINOCULARS.id)
		return ZOOM_BINOCULARS;
}

function getGun(id)
{
	var currentGun = -1;

	findTheGun:
	for(var i in allGuns)
	{
		if(id == allGuns[i].id)
		{
			currentGun = allGuns[i];
			break findTheGun;
		}
	}

	// gun not found
	if(currentGun == -1)
	{
		clientMessage("Error: gun not found in getGun(): " + id);
		currentGun = AK47;
	}

	return currentGun;
}

function getGunTypeName(gunType)
{
	switch(gunType)
	{
		case GUN_TYPE_ASSAULT_RIFLE:
		{
			return "Assault Rifle";
		}
		case GUN_TYPE_SUB_MACHINE:
		{
			return "Sub Machine";
		}
		case GUN_TYPE_LIGHT_MACHINE:
		{
			return "Light Machine";
		}
		case GUN_TYPE_SNIPER_RIFLE:
		{
			return "Sniper Rifle";
		}
		case GUN_TYPE_SHOTGUN:
		{
			return "Shotgun";
		}
		case GUN_TYPE_MACHINE_PISTOL:
		{
			return "Machine Pistol";
		}
		case GUN_TYPE_HANDGUN:
		{
			return "Handgun";
		}
		case GUN_TYPE_LAUNCHER:
		{
			return "Launcher";
		}
		case GUN_TYPE_MINIGUN:
		{
			return "Minigun";
		}
	}
}

function setUpGunsWithDate()
{
	var cal = java.util.Calendar.getInstance();
	var day = cal.get(java.util.Calendar.DAY_OF_MONTH);
	var month = cal.get(java.util.Calendar.MONTH);

	if((day > 6 && month == java.util.Calendar.JANUARY) || month == java.util.Calendar.FEBRUARY || (day <= 21 && month == java.util.Calendar.MARCH))
	{
		// winter after Xmas period
		XMAS_SNIPER.name = "Winter Sniper";
		XMAS_MINIGUN.name = "Winter Minigun";
		defaultGuns.push(XMAS_MINIGUN);
		defaultGuns.push(XMAS_SNIPER);
	}

	if(month == java.util.Calendar.DECEMBER || (day <= 6 && month == java.util.Calendar.JANUARY))
	{
		// Xmas period
		defaultGuns.push(XMAS_MINIGUN);
		defaultGuns.push(XMAS_SNIPER);
	}
}
//########## WEAPONS functions - END ##########


//########## SHOOT WITH GUNS functions ##########
function shoot(gun)
{
	if(gun.shotType == SHOT_TYPE_NORMAL)
	{
		shootSingleBullet(gun);
		return;
	}

	if(gun.shotType == SHOT_TYPE_SHOTGUN)
	{
		shootShotgun(gun);
		return;
	}

	if(gun.shotType == SHOT_TYPE_FLAMETHROWER)
	{
		shootFlamethrower(gun);
		return;
	}

	clientMessage("Something went wrong in shoot() with " + gun.name);
}

function shootSingleBullet(gun)
{
	// a single bullet

	var gunAccuracy = getDefaultAccuracy(gun);

	var yawAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * gunAccuracy;
	var pitchAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * gunAccuracy;

	var gunShootDir = getDirection(getYaw() + yawAccuracyValue, getPitch() + pitchAccuracyValue);

	var bullet = shootEntity(gun, getEntityIdForBulletType(gun), gunShootDir);

	if(gun.bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
		Entity.setFireTicks(bullet, 1000);

	if(gun.hasParticleTrail || gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH || gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME || gun.bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
		gun.bulletsArray.push(new entityClass(bullet));

	if(gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME)
	{
		Sound.playFromSimplePath("desnoguns/explosion-countdown.wav");

		new android.os.Handler().postDelayed(new java.lang.Runnable(
		{
			run: function()
			{
				if(isInGame && gun.bulletsArray.length > 0) // gun.bulletsArray.length > 0 fix a crash that happens when the player shoot an explosive bullet, exit the world and re-enter in the world
				{
					var explosionX = Entity.getX(gun.bulletsArray[0].entity);
					var explosionY = Entity.getY(gun.bulletsArray[0].entity);
					var explosionZ = Entity.getZ(gun.bulletsArray[0].entity);

					if(explosionX == 0 && explosionY == 0 && explosionZ == 0)
					{
						// arrow hit an entity
						Entity.remove(gun.bulletsArray[0].entity);
						Level.explode(gun.bulletsArray[0].previousX, gun.bulletsArray[0].previousY - 1, gun.bulletsArray[0].previousZ, gun.bulletsExplosionRadius); // y - 1 because usually the arrow is removed when it hits an entity and the explosion happens on a previous position that is not on the ground.
					} else
					{
						Entity.remove(gun.bulletsArray[0].entity);
						Level.explode(explosionX, explosionY, explosionZ, gun.bulletsExplosionRadius);
					}
					gun.bulletsArray.splice(0, 1);
				}
			}
		}), gun.bulletsExplosionDelay);
	}
}

function shootShotgun(gun)
{
	// multiple arrows

	for(var i = 0; i < gun.shotgunBullets; i++)
	{
		if(gun.shotgunWait > 0)
		{
			new android.os.Handler().postDelayed(new java.lang.Runnable(
			{
				run: function()
				{
					if(isInGame)
					{
						shootSingleShotgunBullet(gun);
					}
				}
			}), i * gun.shotgunWait);
		} else
		{
			shootSingleShotgunBullet(gun);
		}
	}

	if(gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME)
		Sound.playFromSimplePath("desnoguns/explosion-countdown.wav");
}

function shootSingleShotgunBullet(gun)
{
	// a single bullet but with yaw and pitch accuracy of the shotgun

	var gunAccuracy = getDefaultAccuracy(gun);

	var yawAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * gunAccuracy;
	var pitchAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * gunAccuracy;

	var yawAccuracyShotgun = ((Math.random() * gun.shotgunDegreesSpread) - (gun.shotgunDegreesSpread / 2));
	var pitchAccuracyShotgun = ((Math.random() * gun.shotgunDegreesSpread) - (gun.shotgunDegreesSpread / 2));

	var gunShootDir = getDirection(getYaw() + yawAccuracyValue + yawAccuracyShotgun, getPitch() + pitchAccuracyValue + pitchAccuracyShotgun);

	var bullet = shootEntity(gun, getEntityIdForBulletType(gun), gunShootDir);

	if(gun.bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
		Entity.setFireTicks(bullet, 1000);

	if(gun.hasParticleTrail || gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH || gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME || gun.bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
		gun.bulletsArray.push(new entityClass(bullet));

	if(gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME)
	{
		new android.os.Handler().postDelayed(new java.lang.Runnable(
		{
			run: function()
			{
				if(isInGame && gun.bulletsArray.length > 0) // gun.bulletsArray.length > 0 fix a crash that happens when the player shoot an explosive bullet, exit the world and re-enter in the world
				{
					var explosionX = Entity.getX(gun.bulletsArray[0].entity);
					var explosionY = Entity.getY(gun.bulletsArray[0].entity);
					var explosionZ = Entity.getZ(gun.bulletsArray[0].entity);

					if(explosionX == 0 && explosionY == 0 && explosionZ == 0)
					{
						// arrow hit an entity
						Entity.remove(gun.bulletsArray[0].entity);
						Level.explode(gun.bulletsArray[0].previousX, gun.bulletsArray[0].previousY - 1, gun.bulletsArray[0].previousZ, gun.bulletsExplosionRadius); // y - 1 because usually the arrow is removed when it hits an entity and the explosion happens on a previous position that is not on the ground.
					} else
					{
						Entity.remove(gun.bulletsArray[0].entity);
						Level.explode(explosionX, explosionY, explosionZ, gun.bulletsExplosionRadius);
					}
					gun.bulletsArray.splice(0, 1);
				}
			}
		}), gun.bulletsExplosionDelay);
	}
}

function shootFlamethrower(gun)
{
	// fire

	var gunAccuracy = getDefaultAccuracy(gun);

	var yawAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * gun.accuracy;
	var pitchAccuracyValue = ((Math.random() * RANDOMNESS) - (RANDOMNESS / 2)) * gun.accuracy;
	var playerShootDir = getDirection(getYaw() + yawAccuracyValue, getPitch() + pitchAccuracyValue);

	var flameShootDir = getDirection(getYaw() + 45, getPitch());

	var xDir;
	var yDir;
	var zDir;
	var tile;

	// particles near the player
	xDir = Player.getX() + (playerShootDir.x * 0.75) + flameShootDir.x;
	yDir = Player.getY() + (playerShootDir.y * 0.75) - 0.3;
	zDir = Player.getZ() + (playerShootDir.z * 0.75) + flameShootDir.z;

	Level.addParticle(ParticleType.flame, xDir + Math.random() - 0.5, yDir, zDir + Math.random() - 0.5, playerShootDir.x * 0.05, playerShootDir.y * 0.05, playerShootDir.z * 0.05, 1);
	Level.addParticle(ParticleType.flame, xDir - Math.random() + 0.5, yDir, zDir - Math.random() + 0.5, playerShootDir.x * 0.05, playerShootDir.y * 0.05, playerShootDir.z * 0.05, 1);


	// first flame tick
	xDir = Player.getX() + (playerShootDir.x * flameTick) + flameShootDir.x;
	yDir = Player.getY() + (playerShootDir.y * flameTick) - 0.3;
	zDir = Player.getZ() + (playerShootDir.z * flameTick) + flameShootDir.z;

	tile = Level.getTile(Math.floor(xDir), Math.floor(yDir), Math.floor(zDir));

	if(tile == 0 || tile == 31) // 31 grass
	{
		if(flameTick > 5)
		{
			Level.setTile(Math.floor(xDir), Math.floor(yDir), Math.floor(zDir), 51);
		} else
		{
			Level.setTile(Math.floor(xDir), Math.floor(yDir), Math.floor(zDir), 51);
		}
	}

	Level.addParticle(ParticleType.flame, xDir + Math.random() - 0.5, yDir, zDir + Math.random() - 0.5, playerShootDir.x * 0.05, playerShootDir.y * 0.05, playerShootDir.z * 0.05, 1);
	Level.addParticle(ParticleType.flame, xDir - Math.random() + 0.5, yDir, zDir - Math.random() + 0.5, playerShootDir.x * 0.05, playerShootDir.y * 0.05, playerShootDir.z * 0.05, 1);


	flameTick += 0.5;


	// second flame tick
	xDir = Player.getX() + (playerShootDir.x * flameTick) + flameShootDir.x;
	yDir = Player.getY() + (playerShootDir.y * flameTick) - 0.3;
	zDir = Player.getZ() + (playerShootDir.z * flameTick) + flameShootDir.z;

	tile = Level.getTile(Math.floor(xDir), Math.floor(yDir), Math.floor(zDir));

	if(tile == 0 || tile == 31) // 31 grass
		Level.setTile(Math.floor(xDir), Math.floor(yDir), Math.floor(zDir), 51);

	Level.addParticle(ParticleType.flame, xDir + Math.random() - 0.5, yDir, zDir + Math.random() - 0.5, playerShootDir.x * 0.05, playerShootDir.y * 0.05, playerShootDir.z * 0.05, 1);
	Level.addParticle(ParticleType.flame, xDir - Math.random() + 0.5, yDir, zDir - Math.random() + 0.5, playerShootDir.x * 0.05, playerShootDir.y * 0.05, playerShootDir.z * 0.05, 1);


	flameTick += 0.5;

	if(flameTick > 10)
		flameTick = 2;
}

function shootEntity(gun, entityId, playerDir, arrowDir)
{
	if(arrowDir == null)
		arrowDir = playerDir;

	var bullet = Level.spawnMob(getPlayerX() + (playerDir.x * 2), getPlayerY() + (playerDir.y * 2.5), getPlayerZ() + (playerDir.z * 2), entityId);
	setVelX(bullet, arrowDir.x * gun.bulletSpeed);
	setVelY(bullet, arrowDir.y * gun.bulletSpeed);
	setVelZ(bullet, arrowDir.z * gun.bulletSpeed);

	return bullet;
}

function getDefaultAccuracy(gun)
{
	if(isAiming)
	{
		return gun.accuracy - 1;
	} else
	{
		if(gun.gunType == GUN_TYPE_SNIPER_RIFLE)
			return gun.accuracy + 25;
		else
			return gun.accuracy;
	}
}

function getEntityIdForBulletType(gun)
{
	if(gun.bulletType == BULLET_TYPE_NORMAL || gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH || gun.bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME)
		return 80;

	if(gun.bulletType == BULLET_TYPE_TNT)
		return 65;

	if(gun.bulletType == BULLET_TYPE_SNOWBALL || gun.bulletType == BULLET_TYPE_INCENDIARY_SNOWBALL)
		return 81;

	if(gun.bulletType == BULLET_TYPE_CUSTOM_ENTITY)
		return gun.customBulletId;
}

function showCloudParticle(amount)
{
	if(amount > 0)
	{
		new android.os.Handler().postDelayed(new java.lang.Runnable(
		{
			run: function()
			{
				if(isInGame)
				{
					var gunDir = getDirection(getYaw() + 30, getPitch());
					for(var i = 0; i < amount; i++)
						Level.addParticle(4, getPlayerX() + (gunDir.x * 1.5), getPlayerY() + (gunDir.y * 1.5), getPlayerZ() + (gunDir.z * 1.5), 0, 0, 0, 1);
				}
			}
		}), 250);
	}
}
//########## SHOOT WITH GUNS functions - END ##########


//########## ON CLICK GUNS functions ##########
function onClickWeaponShoot(gun)
{
	if(latestShotTime == null || java.lang.System.currentTimeMillis() > (latestShotTime + (gun.fireRate * 50)))
	{
		if(shouldReload())
		{
			// survival or creative with reload option enabled
			onClickShootWithReload(gun);
		} else
		{
			// creative with reload option disabled
			onClickShootWithoutReload(gun);
		}
	}
}

function onClickShootWithReload(gun)
{
	if(Player.getCarriedItemData() >= gun.ammo)
	{
		Sound.playFromSimplePath("desnoguns/EmptyGun.ogg");
		ModPE.showTipMessage("Press the ammo text to reload.");
	} else
	{
		stopReloading();
		Sound.playLoadedSoundPool();
		shoot(gun);
		damageCarriedGun(gun);
		latestShotTime = java.lang.System.currentTimeMillis();
		showCloudParticle(gun.smoke);
		Recoil.makeRecoil(gun);
	}
}

function onClickShootWithoutReload(gun)
{
	Sound.playLoadedSoundPool();
	shoot(gun);
	latestShotTime = java.lang.System.currentTimeMillis();
	showCloudParticle(gun.smoke);
	Recoil.makeRecoil(gun);
}
//########## ON CLICK GUNS functions - END ##########


//########## ON TOUCH GUNS functions ##########
function onTouchWeaponShoot(event, gun, reload)
{
	var action = event.getActionMasked();
	if(action == android.view.MotionEvent.ACTION_CANCEL || action == android.view.MotionEvent.ACTION_UP)
	{
		isShooting = false;
		showCloudParticle(gun.smoke);
	} else
	{
		if(!isShooting)
		{
			isShooting = true;
			currentShotTicks = gun.fireRate;
			if(reload)
				onTouchShootingRunnableWithReload(gun);
			else
				onTouchShootingRunnableWithoutReload(gun);
		}
	}
}

function onTouchShootingRunnableWithReload(gun)
{
	shootingRunnable = (new java.lang.Runnable(
	{
		run: function()
		{
			if(currentShotTicks == gun.fireRate)
			{
				if(Player.getCarriedItemData() >= gun.ammo)
				{
					Sound.playFromSimplePath("desnoguns/EmptyGun.ogg");
					ModPE.showTipMessage("Press the ammo text to reload.");
				} else
				{
					stopReloading();
					currentShotTicks = 0;
					Sound.playLoadedSoundPool(GUNS_ON_TOUCH_SHOOT_VOLUME);
					shoot(gun);
					damageCarriedGun(gun);
					Recoil.makeRecoil(gun);
				}
			}
			currentShotTicks++;
		}
	}));
}

function onTouchShootingRunnableWithoutReload(gun)
{
	shootingRunnable = (new java.lang.Runnable(
	{
		run: function()
		{
			if(currentShotTicks == gun.fireRate)
			{
				currentShotTicks = 0;
				Sound.playLoadedSoundPool(GUNS_ON_TOUCH_SHOOT_VOLUME);
				shoot(gun);
				Recoil.makeRecoil(gun);
			}
			currentShotTicks++;
		}
	}));
}
//########## ON TOUCH GUNS functions - END ##########


//########## ON TOUCH WITH WAIT GUNS functions ##########
function onTouchWithWaitWeaponShoot(event, gun, reload)
{
	var action = event.getActionMasked();
	if(action == android.view.MotionEvent.ACTION_CANCEL || action == android.view.MotionEvent.ACTION_UP)
	{
		onTouchWithWaitWeaponButtonReleased(gun);
	} else
	{
		if(!isShooting && !isTouchingFireButtonGunsWithWait)
		{
			try
			{
				var warmupSoundString;
				if(gun.hasRandomWarmupSound)
					warmupSoundString = createRandomString(gun.warmupSound);
				else
					warmupSoundString = gun.warmupSound;

				isTouchingFireButtonGunsWithWait = true;
				gunWarmupSound.reset();
				gunWarmupSound.setDataSource(Sound.getOriginalPath(warmupSoundString));
				gunWarmupSound.setVolume(generalVolume, generalVolume);
				gunWarmupSound.prepare();
				gunWarmupSound.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
				{
					onCompletion: function(mp)
					{
						if(isTouchingFireButtonGunsWithWait)
						{
							isShooting = true;
							gunSpinSound.start();
							currentShotTicks = gun.fireRate;
							if(reload)
								onTouchWithWaitShootingRunnableWithReload(gun);
							else
								onTouchWithWaitShootingRunnableWithoutReload(gun);
						}
					}
				});
				gunWarmupSound.start();
			} catch(e) {
				ModPE.log(getLogText() + "Error in onTouchWithWaitWeaponShoot(): " + e);
				clientMessage("The minigun needs sounds to work properly.");
			}
		}
	}
}

function onTouchWithWaitWeaponButtonReleased(gun)
{
	if(gun.isFlamethrower)
		flameTick = 2;

	isTouchingFireButtonGunsWithWait = false;
	if(gunWarmupSound.isPlaying())
		gunWarmupSound.stop();
	if(isShooting)
		showCloudParticle(gun.smoke);
	isShooting = false;
	try {
		gunSpinSound.stop();
		gunSpinSound.prepareAsync();
	} catch(e) {
		// sometimes an error happens also if you have sounds installed correctly and I didn't find why
		clientMessage("A wild error appeared. See log.");
		ModPE.log(getLogText() + "Error in onTouchWithWaitWeaponButtonReleased(): " + e);
		try {
			gunSpinSound.reset();
			gunSpinSound.setDataSource(Sound.getOriginalPath(gun.spinSound));
			gunSpinSound.setLooping(true);
			gunSpinSound.setVolume(generalVolume, generalVolume);
			gunSpinSound.prepareAsync();
		} catch(e) { /* sounds not installed */ }
	}

	if(!gun.hasntCooldownSound)
		Sound.playFromSimplePath(gun.cooldownSound);
}

function onTouchWithWaitShootingRunnableWithReload(gun)
{
	shootingRunnable = (new java.lang.Runnable(
	{
		run: function()
		{
			if(currentShotTicks == gun.fireRate)
			{
				if(Player.getCarriedItemData() >= gun.ammo)
				{
					Sound.playFromSimplePath("desnoguns/EmptyGun.ogg");
					ModPE.showTipMessage("Press the ammo text to reload.");
				} else
				{
					stopReloading();
					currentShotTicks = 0;
					if(!gun.hasntShootingSound)
						Sound.playLoadedSoundPool(GUNS_ON_TOUCH_WITH_WAIT_SHOOT_VOLUME);
					shoot(gun);
					damageCarriedGun(gun);
					Recoil.makeRecoil(gun);
				}
			}
			currentShotTicks++;
		}
	}));
}

function onTouchWithWaitShootingRunnableWithoutReload(gun)
{
	shootingRunnable = (new java.lang.Runnable(
	{
		run: function()
		{
			if(currentShotTicks == gun.fireRate)
			{
				currentShotTicks = 0;
				if(!gun.hasntShootingSound)
					Sound.playLoadedSoundPool(GUNS_ON_TOUCH_WITH_WAIT_SHOOT_VOLUME);
				shoot(gun);
				Recoil.makeRecoil(gun);
			}
			currentShotTicks++;
		}
	}));
}
//########## ON TOUCH WITH WAIT GUNS functions - END ##########


//########## SOUND functions ##########
var sound1;
var sound2;
var sound3;

var Sound = {

	isDefault: function(simplePath)
	{
		//
		return simplePath.substring(0, 10) == "desnoguns/";
	},

	isCustom: function(simplePath)
	{
		//
		return simplePath.substring(0, 7) == "custom/";
	},

	getOriginalPath: function(simplePath)
	{
		if(Sound.isDefault(simplePath))
		{
			// sound in form: desnoguns/path-in-default-desnoguns-folder
			var pathInDefaultDesnoGunsFolder = simplePath.substring(10); // keep only after desnoguns/
			return sdcard + "/games/com.mojang/desnoguns-sounds/" + pathInDefaultDesnoGunsFolder;
		}

		if(Sound.isCustom(simplePath))
		{
			// sound in form: custom/path-in-texture-pack and it has been saved in ...com.mojang/desnoguns-temp/path-in-texture-pack
			var pathInTexturePack = simplePath.substring(7); // keep only after custom/
			return sdcard + "/games/com.mojang/desnoguns-temp/" + pathInTexturePack;
		}

		throw getLogText() + "problem in path: " + simplePath;
	},

	playFromSimplePath: function(simplePath, x, y, z)
	{
		//
		Sound.playFromPath(Sound.getOriginalPath(simplePath), x, y, z);
	},

	playFromPath: function(path, x, y, z)
	{
		var volume = 1.0;

		// change volume based on distance from source
		if(!(x == null || y == null || z == null))
		{
			var distance = Math.sqrt( Math.pow(x - Player.getX(), 2) + Math.pow(y - Player.getY(), 2) + Math.pow(z - Player.getZ(), 2) );
			if(distance > MAX_LOGARITHMIC_VOLUME)
				volume = 0.0;
			else
			{
				volume = 1 - (Math.log(distance) / Math.log(MAX_LOGARITHMIC_VOLUME));
			}
		}

		// apply general volume
		volume = volume * generalVolume;

		// play sound
		try
		{
			if(sound1 == null)
			{
				if(DEBUG1)
					clientMessage("sound 1");

				if(sound1 == null)
					sound1 = new android.media.MediaPlayer();
				sound1.reset();
				sound1.setDataSource(path);
				sound1.setVolume(volume, volume);
				sound1.prepare();
				sound1.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
				{
					onCompletion: function(mp)
					{
						if(DEBUG1)
							clientMessage("sound 1 finish");
						sound1.release();
						sound1 = null;
					}
				});
				sound1.start();
				return 1; // END
			}
			if(sound2 == null)
			{
				if(DEBUG1)
					clientMessage("sound 2");

				if(sound2 == null)
					sound2 = new android.media.MediaPlayer();
				sound2.reset();
				sound2.setDataSource(path);
				sound2.setVolume(volume, volume);
				sound2.prepare();
				sound2.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
				{
					onCompletion: function(mp)
					{
						if(DEBUG1)
							clientMessage("sound 2 finish");
						sound2.release();
						sound2 = null;
					}
				});
				sound2.start();
				return 2; // END
			}
			if(sound3 == null)
			{
				if(DEBUG1)
					clientMessage("sound 3");

				if(sound3 == null)
					sound3 = new android.media.MediaPlayer();
				sound3.reset();
				sound3.setDataSource(path);
				sound3.setVolume(volume, volume);
				sound3.prepare();
				sound3.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
				{
					onCompletion: function(mp)
					{
						if(DEBUG1)
							clientMessage("sound 3 finish");
						sound3.release();
						sound3 = null;
					}
				});
				sound3.start();
				return 3; // END
			} else
			{
				if(DEBUG1)
					clientMessage("sound 1 all");

				if(sound1 == null)
					sound1 = new android.media.MediaPlayer();
				sound1.reset();
				sound1.setDataSource(path);
				sound1.setVolume(volume, volume);
				sound1.prepare();
				sound1.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
				{
					onCompletion: function(mp)
					{
						if(DEBUG1)
							clientMessage("sound 1 all finish");
						sound1.release();
						sound1 = null;
					}
				});
				sound1.start();
				return 1; // END
			}
		} catch(err)
		{
			if(!displayedMessageNoSound)
			{
				ModPE.showTipMessage(getLogText() + "Sounds not installed!");
				displayedMessageNoSound = true;
				ModPE.log(getLogText() + "Error in playFromPath: " + err);
			}
		}
	},

	loadSoundPoolFromPath: function(path)
	{
		try
		{
			soundPool = new android.media.SoundPool(5, android.media.AudioManager.STREAM_MUSIC, 0);
			soundID = soundPool.load(path, 1);
		} catch(err)
		{
			ModPE.showTipMessage(getLogText() + "Sounds not installed!");
			ModPE.log(getLogText() + "Error in loadSoundPoolFromPath: " + err);
		}
	},

	playLoadedSoundPool: function(volume)
	{
		if(volume == null)
			volume = 1.0;

		try
		{
			volume = volume * generalVolume;
			soundPool.play(soundID, volume, volume, 1, 0, 1.0);
		} catch(e) { /* probably sounds not installed error */ }
	}
};
//########## SOUND functions - END ##########


//########## SHOOT UI functions ##########
function displayShootAndAimButtons()
{
	displayShootButton();

	displayAimButton();
}

function displayShootButton()
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try
			{
				// display a little cross in the center of the screen
				displaySight();


				// fire button
				if(minecraftStyleForButtons)
				{
					shotText = MinecraftButton(buttonsSize, false);
					shotText.setText("Fire");
				} else
				{
					shotText = defaultColoredMinecraftButton("fire", "#FFDE0000");
				}
				shotText.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function(v)
					{
						if(onClickRunnable != null)
							onClickRunnable.run();
						return false;
					}
				});
				shotText.setOnLongClickListener(new android.view.View.OnLongClickListener()
				{
					onLongClick: function(v)
					{
						return false;
					}
				});
				shotText.setSoundEffectsEnabled(false);

				popupShot = new android.widget.PopupWindow(shotText, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
				popupShot.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupShot.setOutsideTouchable(false);
				popupShot.setSplitTouchEnabled(true);
				popupShot.showAtLocation(currentActivity.getWindow().getDecorView(), (switchedButtonsPosition ? android.view.Gravity.RIGHT : android.view.Gravity.LEFT) | android.view.Gravity.CENTER, 0, pixelsOffsetButtons);


				// ammo text (reload)
				ammoText = basicMinecraftTextView("null", ammoTextSize);
				ammoText.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function(v)
					{
						reloadAmmo(getGun(Player.getCarriedItem()));
						return false;
					}
				});
				ammoText.setGravity(android.view.Gravity.CENTER);
				ammoText.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				ammoText.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));

				popupAmmo = new android.widget.PopupWindow(ammoText, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
				popupAmmo.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupAmmo.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.CENTER | android.view.Gravity.BOTTOM, 0, 64 * deviceDensity);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	}));
}

function displaySight()
{
	if(shouldDisplaySight) // display sight preference
	{
		currentActivity.runOnUiThread(new java.lang.Runnable(
		{
			run: function()
			{
				try
				{
					try {
						popupSightImage.dismiss();
					} catch(e) {}

					var sightImageView = new android.widget.ImageView(currentActivity);
					sightImageView.setImageBitmap(sightPngScaled);

					popupSightImage = new android.widget.PopupWindow(sightImageView, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
					popupSightImage.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
					popupSightImage.setOutsideTouchable(false);
					popupSightImage.setFocusable(false);
					popupSightImage.setTouchable(false);
					popupSightImage.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.CENTER | android.view.Gravity.CENTER, -convertDpToPixel(1), -convertDpToPixel(1));
				} catch(err)
				{
					clientMessage("Error: " + err);
				}
			}
		}));
	}
}

function displayAimButton()
{
	// weapon must be an object with zoomLevel and optional hasAimImageLayer

	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try
			{
				var aimText;
				if(minecraftStyleForButtons)
				{
					aimText = MinecraftButton(buttonsSize);
					aimText.setText("Aim");
				} else
				{
					aimText = defaultColoredMinecraftButton("aim", "#FFFFFFFF");
				}
				aimText.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function(v)
					{
						onAimClick();
						return false;
					}
				});
				aimText.setSoundEffectsEnabled(true);

				popupAim = new android.widget.PopupWindow(aimText, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
				popupAim.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupAim.showAtLocation(currentActivity.getWindow().getDecorView(), (switchedButtonsPosition ? android.view.Gravity.LEFT : android.view.Gravity.RIGHT) | android.view.Gravity.CENTER, 0, pixelsOffsetButtons);
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	}));
}

function removeShootAndAimButtons()
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try {
				popupShot.dismiss();
			} catch(e) {}
			try {
				popupSettingsImage.dismiss();
			} catch(e) {}
			try {
				popupSightImage.dismiss();
			} catch(e) {}
			try {
				popupAmmo.dismiss();
			} catch(e) {}
			try {
				popupAim.dismiss();
			} catch(e) {}
			try {
				popupAiming.dismiss();
			} catch(e) {}
			try {
				popupZoom.dismiss();
			} catch(e) {}
		}
	}));
	currentShotTicks = 0;
}

function resetRunnables()
{
	if(shootingRunnable != null)
	{
		isShooting = false;
		shootingRunnable = null;
	}
	if(onClickRunnable != null)
		onClickRunnable = null;
}
//########## SHOOT UI functions - END ##########


//########## AIM functions ##########
function onAimClick()
{
	var weapon = getWeaponObject(Player.getCarriedItem());

	if(!isDisplayingAimingAnimation) // if is currently zooming in or out don't do anything
	{
		if(!isAiming)
		{
			isAiming = true;
			isDisplayingAimingAnimation = true;

			zoomInGradually(weapon.zoomLevel - 1); // total steps = weapon.zoomLevel - 1; total time: (weapon.zoomLevel - 1) * 12

			// last step of decreasing FOV
			new android.os.Handler().postDelayed(new java.lang.Runnable( // executed 1 time. Total: executed a total of weapon.zoomLevel times
			{
				run: function()
				{
					zoomWithFov--;
					ModPE.setFov(zoomWithFov);

					if(weapon.id == Player.getCarriedItem()) // check if the player hasn't changed is item while zooming
					{
						if(weapon.hasAimImageLayer)
							showAimImageLayerFromWeapon(weapon);

						isDisplayingAimingAnimation = false;
					} else
					{
						isDisplayingAimingAnimation = false;
						
						removeZoomAndAimImageLayer();
					}
				}
			}), weapon.zoomLevel * 12);
		} else
		{
			if(weapon.hasAimImageLayer && isItemAGun(weapon.id)) // restore the sight after aiming with an image layer (the image layer removed the sight)
				displaySight();

			removeZoomAndAimImageLayer();
		}
	}
}

function zoomInGradually(amount)
{
	for(var ms = 1; ms <= amount; ms++)
	{
		new android.os.Handler().postDelayed(new java.lang.Runnable(
		{
			run: function()
			{
				zoomWithFov--;
				ModPE.setFov(zoomWithFov);
			}
		}), ms * 12);
	}
}

function removeZoomAndAimImageLayer()
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			if(!isDisplayingAimingAnimation) // if is currently zooming in or out don't do anything
			{
				if(isAiming)
				{
					isAiming = false;
					isDisplayingAimingAnimation = true;
					var removeAiming = zoomWithFov;

					for(var ms = 1; ms < (72 - removeAiming); ms++)
					{
						new android.os.Handler().postDelayed(new java.lang.Runnable(
						{
							run: function()
							{
								zoomWithFov++;
								ModPE.setFov(zoomWithFov);
							}
						}), ms * 12);
					}

					new android.os.Handler().postDelayed(new java.lang.Runnable(
					{
						run: function()
						{
							zoomWithFov++;
							ModPE.setFov(zoomWithFov);

							isDisplayingAimingAnimation = false;
						}
					}), (72 - removeAiming) * 12);
				}
				try {
					popupAiming.dismiss();
				} catch(e) {}
				try {
					popupZoom.dismiss();
				} catch(e) {}
			}
		}
	}));
}
//########## AIM functions - END ##########


//########## IMAGE LAYER FOR AIM functions ##########
function showAimImageLayerFromWeapon(weapon)
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try
			{
				// remove buttons (we don't want the layer to stay on the top of them)
				removeShootAndAimButtons();

				// display actual layer
				displayAimImageLayerPopup(getAimImageFromId(weapon.id));


				if(weapon.hasManualZoom)
					displayManualZoom(weapon);

				if(isItemAGun(weapon.id))
				{
					// display both fire and aim buttons
					displayShootAndAimButtons();

					// restore ammo text
					updateAmmoText(weapon);

					// remove sight image, we already have the image layer but we need to restore it later
					try {
						popupSightImage.dismiss();
					} catch(e) {}

					if(weapon.buttonType == BUTTON_TYPE_ON_TOUCH)
					{
						// load touch events
						if(shouldReload())
						{
							// survival or creative with reload option enabled
							currentActivity.runOnUiThread(new java.lang.Runnable(
							{
								run: function()
								{
									shotText.setOnTouchListener(new android.view.View.OnTouchListener()
									{
										onTouch: function(v, event)
										{
											if(minecraftStyleForButtons)
												MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
											onTouchWeaponShoot(event, weapon, true);
											return false;
										}
									});
								}
							}));
						} else
						{
							// creative with reload option disabled
							currentActivity.runOnUiThread(new java.lang.Runnable(
							{
								run: function()
								{
									shotText.setOnTouchListener(new android.view.View.OnTouchListener()
									{
										onTouch: function(v, event)
										{
											if(minecraftStyleForButtons)
												MinecraftButtonLibrary.onTouch(v, event, false); // make touch effect
											onTouchWeaponShoot(event, weapon, false);
											return false;
										}
									});
								}
							}));
						}
					}
				} else
				{
					// is not a gun, we don't want to display the fire button and related things (sight, ammo)
					displayAimButton();
				}
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	}));
}

function displayAimImageLayerPopup(image)
{
	// must be called in a runOnUiThread

	var backgroundAimImageView = new android.widget.ImageView(currentActivity);
	backgroundAimImageView.setImageBitmap(image);
	backgroundAimImageView.setScaleType(android.widget.ImageView.ScaleType.CENTER_CROP);
	backgroundAimImageView.setLayoutParams(new android.widget.LinearLayout.LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT));

	popupAiming = new android.widget.PopupWindow();
	popupAiming.setContentView(backgroundAimImageView);
	popupAiming.setOutsideTouchable(false);
	popupAiming.setFocusable(false);
	popupAiming.setTouchable(false);
	popupAiming.setAnimationStyle(android.R.style.Animation_Translucent); // Animation_Activity, Animation_Dialog, Animation_Translucent, 
	popupAiming.setWidth(getDisplayWidth());
	popupAiming.setHeight(getDisplayHeight());
	popupAiming.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.CENTER | android.view.Gravity.CENTER, 0, 0);
}

function getAimImageFromId(id)
{
	switch(id)
	{
		case BARRETT.id:
		case BARRETT_EXPLOSIVE.id:
		{
			return barrettUIDecoded;
		}
		case DRAGUNOV.id:
		{
			return dragunovUIDecoded;
		}
		case M21.id:
		{
			return m21UIDecoded;
		}
		case M40A3.id:
		case M40A3_ICE.id:
		{
			return m40a3UIDecoded;
		}
		case R700.id:
		{
			return r700UIDecoded;
		}
		case CROSSBOW.id:
		case CROSSBOW_EXPLOSIVE.id:
		{
			return crossbowUIDecoded;
		}
		case MSR.id:
		{
			return msrUIDecoded;
		}

		case BINOCULARS.id:
		case ZOOM_BINOCULARS.id:
		{
			return binocularsUIDecoded;
		}

		default:
		{
			return barrettUIDecoded;
		}
	}
}

function displayManualZoom(weapon)
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try
			{
				var zoomChooser = new android.widget.SeekBar(currentActivity);
				zoomChooser.setMax(68);
				zoomChooser.setProgress(72 - zoomWithFov);
				zoomChooser.setOnSeekBarChangeListener(new android.widget.SeekBar.OnSeekBarChangeListener()
				{
					onProgressChanged: function()
					{
						zoomWithFov = 72 - zoomChooser.getProgress();
						weapon.zoomLevel = zoomChooser.getProgress();
						if(zoomWithFov == 72)
							ModPE.resetFov();
						else
							ModPE.setFov(zoomWithFov);
					}
				});

				popupZoom = new android.widget.PopupWindow();
				popupZoom.setContentView(zoomChooser);
				popupZoom.setWidth(Math.floor(getDisplayWidth() * 0.4));
				popupZoom.setHeight(android.widget.RelativeLayout.LayoutParams.WRAP_CONTENT);
				popupZoom.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupZoom.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.TOP | android.view.Gravity.CENTER, 0, convertDpToPixel(8));
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	}));
}
//########## IMAGE LAYER FOR AIM functions - END ##########


//########## RECOIL functions ##########
var timedRecoilVar;

var Recoil = {

	makeRecoil: function(gun)
	{
		if(gun.fireRate < 2)
		{
			if(gun.buttonType == BUTTON_TYPE_ON_CLICK)
			{
				Recoil.makeLessTimedRecoil(gun);
				return;
			} else
			{
				Recoil.makeInstantRecoil(gun);
				return;
			}
		}

		if(gun.fireRate >= 2 && gun.fireRate <= 5)
		{
			Recoil.makeLessTimedRecoil(gun);
			return;
		}

		// fire rate > 5
		if(gun.recoil >= 15)
		{
			Recoil.makeTimedRecoil(gun);
			return;
		} else
		{
			Recoil.makeLessTimedRecoil(gun);
			return;
		}
	},

	makeInstantRecoil: function(gun)
	{
		var recoilDegree = gun.recoil / RECOIL;
		Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - recoilDegree);
	},

	makeLessTimedRecoil: function(gun)
	{
		var recoilDegree = gun.recoil / RECOIL;
		var timedRecoilVar = recoilDegree / 4;
		currentActivity.runOnUiThread(new java.lang.Runnable(
		{
			run: function()
			{
				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar));
					}
				}), 1 * 6);

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar * 0.9));
					}
				}), 2 * 6);

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar * 0.8));
					}
				}), 3 * 6);

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar * 0.7));
					}
				}), 4 * 6);
			}
		}));
	},

	makeTimedRecoil: function(gun)
	{
		var recoilDegree = gun.recoil / RECOIL;
		var timedRecoilVar = recoilDegree / 8;
		currentActivity.runOnUiThread(new java.lang.Runnable(
		{
			run: function()
			{
				for(var ms = 1; ms <= 5; ms++)
				{
					new android.os.Handler().postDelayed(new java.lang.Runnable(
					{
						run: function()
						{
							Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - timedRecoilVar);
						}
					}), ms * 7);
				}

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar * 0.9));
					}
				}), 6 * 7);

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar * 0.75));
					}
				}), 7 * 7);

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						Entity.setRot(Player.getEntity(), Entity.getYaw(Player.getEntity()), Entity.getPitch(Player.getEntity()) - (timedRecoilVar * 0.5));
					}
				}), 8 * 7);
			}
		}));
	}
};
//########## RECOIL functions - END ##########


//########## RELOAD GUN functions ##########
function reloadAmmo(gun)
{
	if(Level.getGameMode() == GameMode.SURVIVAL)
	{
		// gamemode survival
		var slot = Player.getSlotOfItem(getAmmoId(gun));
		if(Player.getCarriedItemData() != 0)
		{
			if(slot == -1)
				clientMessage("You don't have one " + Item.getName(getAmmoId(gun), 0, false) + " in your inventory.");
			else
			{
				try
				{
					isReloading = true;
					reloadingGun = gun;

					reloadSound.reset();
					reloadSound.setDataSource(Sound.getOriginalPath(gun.reloadSound));
					reloadSound.setVolume(generalVolume, generalVolume);
					reloadSound.prepare();
					reloadSound.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
					{
						onCompletion: function(mp)
						{
							isReloading = false;

							// let's do again a check to see if the player hasn't changed his carried item
							if(Player.getCarriedItem() == reloadingGun.id)
							{
								var ammoSlot = Player.getSlotOfItem(getAmmoId(reloadingGun));
								if(ammoSlot == -1)
									clientMessage("You don't have one " + Item.getName(getAmmoId(gun), 0, false) + " ammo in your inventory.");
								else
								{
									Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount(), 0);
									setAmmoTextFromGun(gun);
									Player.removeItemsFromInventory(ammoSlot, 1);
								}
							}

							// reset sound
							reloadSound.release();
							reloadSound = null;
							reloadSound = new android.media.MediaPlayer();
						}
					});
					reloadSound.start();
					ModPE.showTipMessage("Reloading");
				} catch(e)
				{
					ModPE.showTipMessage("Sounds not installed.");
					ModPE.log(getLogText() + "error in reloadAmmo: " + e);
				}
			}
		}
	}

	if(Level.getGameMode() == GameMode.CREATIVE)
	{
		// gamemode creative
		if(Player.getCarriedItemData() != 0)
		{
			if(instantReloadInCreative)
			{
				// instant reload enabled
				isReloading = false;

				// let's do a re-check to see if the player hasn't changed his carried item
				if(Player.getCarriedItem() == gun.id)
				{
					Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount(), 0);
					setAmmoTextFromGun(gun);
				}
			} else
			{
				// instant reload NOT enabled
				try
				{
					isReloading = true;
					reloadingGun = gun;

					reloadSound.reset();
					reloadSound.setDataSource(Sound.getOriginalPath(gun.reloadSound));
					reloadSound.setVolume(generalVolume, generalVolume);
					reloadSound.prepare();
					reloadSound.setOnCompletionListener(new android.media.MediaPlayer.OnCompletionListener()
					{
						onCompletion: function(mp)
						{
							isReloading = false;

							// let's do a re-check to see if the player hasn't changed his carried item
							if(Player.getCarriedItem() == reloadingGun.id)
							{
								Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount(), 0);
								setAmmoTextFromGun(reloadingGun);
							}

							// reset sound
							reloadSound.release();
							reloadSound = null;
							reloadSound = new android.media.MediaPlayer();
						}
					});
					reloadSound.start();
					ModPE.showTipMessage("Reloading");
				} catch(e)
				{
					ModPE.showTipMessage("Sounds not installed.");
					ModPE.log(getLogText() + "error in reloadAmmo: " + e);

					// since we are in creative we should reload the ammo, also if sounds aren't installed.
					isReloading = false;

					// let's do a re-check to see if the player hasn't changed his carried item
					if(Player.getCarriedItem() == reloadingGun.id)
					{
						Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount(), 0);
						setAmmoTextFromGun(reloadingGun);
					}
				}
			}
		}
	}
}

function stopReloading()
{
	if(isReloading)
	{
		try {
			reloadSound.stop();
		} catch(e) {
			ModPE.log(getLogText() + "error while stopping reloadSound: " + e);
		}
		isReloading = false;
		ModPE.showTipMessage("Ammo reload interrupted.");
	}
}

function updateAmmoText(currentGun)
{
	if(shouldReload())
		setAmmoTextFromGun(currentGun);
	else
		setAmmoText(" ");
}

function setAmmoTextFromGun(gun)
{
	if(displayGunNameInAmmo)
		setAmmoText(gun.name + " " + (gun.ammo - Player.getCarriedItemData()) + "/" + gun.ammo);
	else
		setAmmoText((gun.ammo - Player.getCarriedItemData()) + "/" + gun.ammo);
}

function setAmmoText(text)
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try
			{
				ammoText.setText(text);
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	}));
}

function damageCarriedGun(gun)
{
	var maxDamage = gun.ammo;
	if(Player.getCarriedItemData() < maxDamage)
		Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount(), Player.getCarriedItemData() + 1);
	setAmmoTextFromGun(gun);
}

function getAmmoId(gun)
{
	if(gun.ammunitionID != null)
	{
		return gun.ammunitionID;
	}
	else
	{
		switch(gun.gunType)
		{
			case GUN_TYPE_ASSAULT_RIFLE:
			{
				return AMMO_ASSAULT_RIFLE_ID;
			}
			case GUN_TYPE_SUB_MACHINE:
			{
				return AMMO_SUB_MACHINE_ID;
			}
			case GUN_TYPE_LIGHT_MACHINE:
			{
				return AMMO_LIGHT_MACHINE_ID;
			}
			case GUN_TYPE_SNIPER_RIFLE:
			{
				return AMMO_SNIPER_RIFLE_ID;
			}
			case GUN_TYPE_SHOTGUN:
			{
				return AMMO_SHOTGUN_ID;
			}
			case GUN_TYPE_MACHINE_PISTOL:
			{
				return AMMO_MACHINE_PISTOL_ID;
			}
			case GUN_TYPE_HANDGUN:
			{
				return AMMO_HANDGUN_ID;
			}
			case GUN_TYPE_LAUNCHER:
			{
				return AMMO_LAUNCHER_ID;
			}
			case GUN_TYPE_MINIGUN:
			{
				return AMMO_MINIGUN_ID;
			}
			default:
			{
				clientMessage("Error in getAmmoId() with gunType " + gun.gunType);
			}
		}
	}
}

function shouldReload()
{
	// reload in survival, or in creative with reload option enabled
	return reloadInCreative || Level.getGameMode() == GameMode.SURVIVAL;
}
//########## RELOAD GUN functions - END ##########


//########## MED KIT functions ##########
function displayHealButton()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var healthText;
				if(minecraftStyleForButtons)
				{
					healthText = MinecraftButton(buttonsSize);
					healthText.setText("Heal");
				} else
				{
					healthText = defaultColoredMinecraftButton("heal", "#FF00DE00");
				}
				healthText.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function(v)
					{
						if(Player.getCarriedItem() == MEDICAL_KIT_ID) // one more check before removing items
						{
							var currentHealth = Entity.getHealth(Player.getEntity());
							if(currentHealth >= 20)
							{
								ModPE.showTipMessage("Your health is full.");
								return;
							}
							var healthToBeRestored = 20 - currentHealth;
							var healthMedicalKitCanRestore = MEDICAL_KIT_MAX_RESTORABLE_HEALTH - Player.getCarriedItemData() + 1;
							if(healthToBeRestored > healthMedicalKitCanRestore)
							{
								Player.setHealth(currentHealth + healthMedicalKitCanRestore);
								for(var i = 0; i < healthMedicalKitCanRestore; i++)
									Player.damageCarriedItem();
								ModPE.showTipMessage("Medical Kit broke before restoring all health.");
							} else
							{
								Player.setHealth(20);
								for(var i = 0; i < healthToBeRestored; i++)
									Player.damageCarriedItem();
								ModPE.showTipMessage("Restored " + (healthToBeRestored / 2) + " hearts.");
							}
						} else
						{
							popupHealth.dismiss();
						}
					}
				});
				healthText.setSoundEffectsEnabled(true);

				popupHealth = new android.widget.PopupWindow(healthText, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
				popupHealth.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupHealth.setOutsideTouchable(false);
				popupHealth.setSplitTouchEnabled(true);
				popupHealth.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.LEFT | android.view.Gravity.CENTER, 0, pixelsOffsetButtons);
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function removeHealButton()
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try {
				popupHealth.dismiss();
			} catch(e) {}
		}
	}));
}
//########## MED KIT functions - END ##########


//########## INFO ITEM functions ##########
function displayInfoItemUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var settingsImage = new android.widget.ImageView(currentActivity);
				settingsImage.setImageBitmap(settingsPngScaled);
				settingsImage.setOnClickListener(new android.view.View.OnClickListener({
					onClick: function()
					{
						infoDesnoGunsMod();
					}
				}));

				popupSettingsImage = new android.widget.PopupWindow(settingsImage, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
				popupSettingsImage.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupSettingsImage.setOutsideTouchable(false);
				popupSettingsImage.setSplitTouchEnabled(true);
				popupSettingsImage.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.LEFT | android.view.Gravity.CENTER, convertDpToPixel(4), 0);



				var tipText = basicMinecraftTextView(getRandomTip(), 14);
				tipText.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function(v)
					{
						v.setText(new android.text.Html.fromHtml(getRandomTip()));
						return false;
					}
				});
				tipText.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
				tipText.setLinkTextColor(android.graphics.Color.parseColor("#FFFBFF97"));
				tipText.setGravity(android.view.Gravity.LEFT);
				tipText.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				tipText.setPadding(convertDpToPixel(4), convertDpToPixel(4), convertDpToPixel(4), convertDpToPixel(4));
				tipText.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));

				popupTip = new android.widget.PopupWindow(tipText, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
				popupTip.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT));
				popupTip.showAtLocation(currentActivity.getWindow().getDecorView(), android.view.Gravity.RIGHT | android.view.Gravity.CENTER, 0, -64 * deviceDensity);
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function getRandomTip()
{
	var cal = java.util.Calendar.getInstance();
	var day = cal.get(java.util.Calendar.DAY_OF_MONTH);
	var month = cal.get(java.util.Calendar.MONTH);
	if(day == 3 && month == java.util.Calendar.JULY)
	{
		return "Happy birthday, Desno365!";
	}
	if(day == 31 && month == java.util.Calendar.OCTOBER)
	{
		return "Keep calm and scary on.";
	}
	if(day == 25 && month == java.util.Calendar.DECEMBER)
	{
		return "Merry Christmas!";
	}
	if(day == 1 && month == java.util.Calendar.JANUARY)
	{
		return "Happy new year!";
	}

	var random = Math.floor((Math.random() * 21) + 1);
	switch(random)
	{
		case 1:
		{
			return "Guns have better accuracy while aiming.";
		}
		case 2:
		{
			return "Follow @Desno365 on Twitter";
		}
		case 3:
		{
			return "Sniper Rifles have a really bad accuracy if you don't aim.";
		}
		case 4:
		{
			return "Many textures and sounds are made by @jamioflan, a big thanks to him!";
		}
		case 5:
		{
			return "Place a Block of Coal on top of a Block of Iron, then touch it.";
		}
		case 6:
		{
			return "The damage of a bullet to a mob depends on his speed.";
		}
		case 7:
		{
			return "This mod causes addiction, use with caution.";
		}
		case 8:
		{
			return "In Minecraft the damage of an arrow depends on his speed.";
		}
		case 9:
		{
			return "This mod is compatible with the Portal 2 Mod by Desno365!";
		}
		case 10:
		{
			return ".shootDatCreeper()";
		}
		case 11:
		{
			return "Press the settings icon on the left, there are awesome things inside.";
		}
		case 12:
		{
			return "Do a 360 noscope.";
		}
		case 13:
		{
			return "author.name = \"Dennis Motta\"; author.nickname = \"Desno365\";";
		}
		case 14:
		{
			return "@AntiModPe made the grenade renders and helped me with some other things. Thanks Anti!";
		}
		case 15:
		{
			return "A good sniper calculates the gravity before shooting.";
		}
		case 16:
		{
			if(isPro())
				return "Pro Key installed. Thanks for your support! :)";
			else
				return "Consider getting the Pro Key, you will support the development and unlock features.";
		}
		case 17:
		{
			return "You're running the " + CURRENT_VERSION + " version of the DesnoGuns mod!";
		}
		case 18:
		{
			return "There is an option in Settings that enables reload in creative.";
		}
		case 19:
		{
			return "Did you find the Easter Egg? No? You'll find a tip on one of these splash texts.";
		}
		case 20:
		{
			return "/swag grenade";
		}
		case 21:
		{
			if(isPro())
				return "If you like the pro features consider leaving a good review on the Play Store: <a href=\"https://play.google.com/store/apps/details?id=com.desno365.desnogunsmodkey\">DesnoGuns Mod Pro Key</a>.";
			else
				return "Get notifications when a new version of one of my mod is released, download the <a href=\"https://play.google.com/store/apps/details?id=com.desno365.mods\">Desno365's Mods app</a> now!";
		}
	}
}

function removeInfoItemUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable(
	{
		run: function()
		{
			try {
				popupSettingsImage.dismiss();
			} catch(e) {}
			try {
				popupTip.dismiss();
			} catch(e) {}
		}
	}));
}
//########## INFO ITEM functions - END ##########


//########## INTERNET functions ##########
function updateLatestVersionMod()
{
	try
	{
		// download content
		var url = new java.net.URL("https://raw.githubusercontent.com/Desno365/MCPE-scripts/master/desnogunsMOD-version");
		var connection = url.openConnection();
 
		// get content
		inputStream = connection.getInputStream();
 
		// read result
		var loadedVersion = "";
		var bufferedVersionReader = new java.io.BufferedReader(new java.io.InputStreamReader(inputStream));
		var rowVersion = "";
		while((rowVersion = bufferedVersionReader.readLine()) != null)
		{
			loadedVersion += rowVersion;
		}
		latestVersion = loadedVersion.split(" ")[0];
 
		// close what needs to be closed
		bufferedVersionReader.close();
		inputStream.close();
	} catch(err)
	{
		clientMessage("DesnoGuns Mod: Can't check for updates, please check your Internet connection.");
		ModPE.log(getLogText() + "updateLatestVersionMod(): caught an error: " + err);
	}
}
//########## INTERNET functions - END ##########


//########## DIRECTION functions ##########
function vector3d(x, y, z)
{
	this.x = x;
	this.y = y;
	this.z = z;
}

function getDirection(yaw, pitch)
{
	var direction = new vector3d(0, 0, 0);
	direction.y = -Math.sin(java.lang.Math.toRadians(pitch));
	direction.x = -Math.sin(java.lang.Math.toRadians(yaw)) * Math.cos(java.lang.Math.toRadians(pitch));
	direction.z = Math.cos(java.lang.Math.toRadians(yaw)) * Math.cos(java.lang.Math.toRadians(pitch));
	return direction;
}
//########## DIRECTION functions - END ##########


//########## PLAYER functions ##########
Player.damageCarriedItem = function()
{
	var maxDamage;
	if(Player.getCarriedItem() == KNIFE_ID)
		maxDamage = KNIFE_MAX_DAMAGE;
	if(Player.getCarriedItem() == PARACHUTE_ID)
		maxDamage = PARACHUTE_MAX_DAMAGE;
	if(Player.getCarriedItem() == MEDICAL_KIT_ID)
		maxDamage = MEDICAL_KIT_MAX_RESTORABLE_HEALTH;
	if(Player.getCarriedItem() == RIOT_SHIELD_ID)
		maxDamage = RIOT_SHIELD_MAX_DAMAGE;

	if(Player.getCarriedItemData() < maxDamage)
		Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount(), Player.getCarriedItemData() + 1);
	else
	{
		Level.playSoundEnt(Player.getEntity(), "random.break", 100, 0);
		if(Player.getCarriedItemCount() == 1)
			Player.clearInventorySlot(Player.getSelectedSlotId());
		else
			Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount() - 1, 0);
	}
}

Player.decreaseByOneCarriedItem = function()
{
	if(Player.getCarriedItemCount() == 1)
		Player.clearInventorySlot(Player.getSelectedSlotId());
	else
		Entity.setCarriedItem(Player.getEntity(), Player.getCarriedItem(), Player.getCarriedItemCount() - 1, 0);
}

Player.getSlotOfItem = function(item, count)
{
	if(count == null || count <= 0)
		count = 1;
	// return -1 if the item wasn't found otherwise returns the inventory slot
	for(var i = 0; i <= 255; i++)
	{
		if(Player.getInventorySlot(i) == item)
		{
			if(Player.getInventorySlotCount(i) >= count)
			{
				return i;
			}
		}
	}
	return -1;
}

Player.removeItemsFromInventory = function(slot, count)
{
	if(Player.getInventorySlotCount(slot) > count)
	{
		var id = Player.getInventorySlot(slot);
		var countBefore = Player.getInventorySlotCount(slot);

		Player.clearInventorySlot(slot);
		Player.addItemInventory(id, countBefore - count);
	} else
	{
		Player.clearInventorySlot(slot);
	}
}
//########## PLAYER functions - END ##########


//########## FILE functions ##########
function deleteFile(path)
{
	var file = new java.io.File(path);

	if(file.isDirectory())
	{
		var directoryFiles = file.listFiles();
		for(var i in directoryFiles)
		{
			deleteFile(directoryFiles[i].getAbsolutePath());
		}
		file.delete();
	}

	if(file.isFile())
		file.delete();
}

function doesFileExist(path)
{
	var file = new java.io.File(path);
	return file.exists();
}

function isFileEmpty(path)
{
	var file = new java.io.File(path);
	if(file.length() > 0)
		return false;
	else
		return true;
}

function writeFileFromByteArray(byteArray, path)
{
	// create file and parent directories
	var file = new java.io.File(path);
	if(file.exists())
		file.delete();
	file.getParentFile().mkdirs();
	file.createNewFile();

	// write to file
	var stream = new java.io.FileOutputStream(file);
	stream.write(byteArray);
	stream.close();

	byteArray = null;
}

function writeFileFromInputStream(inputStream, path)
{
	// create file and parent directories
	var file = new java.io.File(path);
	if(file.exists())
		file.delete();
	file.getParentFile().mkdirs();
	file.createNewFile();

	// write to file
	var outputStream = new java.io.FileOutputStream(file);
	var read = 0;
	var bytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 1024);
	while ((read = inputStream.read(bytes)) != -1) {
		outputStream.write(bytes, 0, read);
	}
	outputStream.close();
}
//########## FILE functions - END ##########


//########## IMAGE functions ##########
function createImages()
{
	var settingsPngDecoded = decodeImageFromBase64(settingsPng);
	settingsPngScaled = scaleImageToSize(settingsPngDecoded, settingsPngDecoded.getHeight() * deviceDensity * 0.3, settingsPngDecoded.getHeight() * deviceDensity * 0.3, true);
	settingsPng = null;

	var sightPngDecoded = decodeImageFromBase64(sightPng);
	sightPngScaled = scaleImageToDensity(sightPngDecoded);
	sightPng = null;

	var backgroundDarkDirtDecoded = decodeImageFromBase64(backgroundDarkDirtPng);
	backgroundDarkDirtScaled = scaleImageToDensity(backgroundDarkDirtDecoded);
	background = new android.graphics.drawable.BitmapDrawable(backgroundDarkDirtScaled);
	background.setTileModeXY(android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);
	backgroundDarkDirtPng = null;

	var playStorePngDecoded = decodeImageFromBase64(playStorePng);
	playStorePngScaled = scaleImageToSize(playStorePngDecoded, playStorePngDecoded.getWidth() * (deviceDensity / 3), playStorePngDecoded.getHeight() * (deviceDensity / 3), true);
	playStorePng = null;

	binocularsUIDecoded = decodeImageFromBase64(binocularsUI);
	binocularsUI = null;

	barrettUIDecoded = decodeImageFromBase64(barrettUI);
	barrettUI = null;
	dragunovUIDecoded = decodeImageFromBase64(dragunovUI);
	dragunovUI = null;
	m21UIDecoded = decodeImageFromBase64(m21UI);
	m21UI = null;
	m40a3UIDecoded = decodeImageFromBase64(m40a3UI);
	m40a3UI = null;
	r700UIDecoded = decodeImageFromBase64(r700UI);
	r700UI = null;
	crossbowUIDecoded = decodeImageFromBase64(crossbowUI);
	crossbowUI = null;
	msrUIDecoded = decodeImageFromBase64(msrUI);
	msrUI = null;
}

function decodeImageFromBase64(base64String)
{
	if(base64String != null)
	{
		var byteArray = android.util.Base64.decode(base64String, 0);
		return android.graphics.BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
	}else
	{
		throw getLogText() + "decodeImageFromBase64 has found a null string.";
	}
}

function scaleImageToDensity(image)
{
	//
	return scaleImageToSize(image, Math.round(image.getWidth() * deviceDensity), Math.round(image.getHeight() * deviceDensity));
}

function scaleImageToSize(image, width, height, filter)
{
	if(filter == null)
		filter = false;
	return android.graphics.Bitmap.createScaledBitmap(image, Math.round(width), Math.round(height), filter);
}
//########## IMAGE functions - END ##########


//########## MISC functions ##########
function createRandomString(randomObject)
{
	// randomObjectExample = { startingFrom:1, endingAt:4, startText:"ignite_flamethrower", endText:".ogg" }
	var random = Math.floor((Math.random() * (randomObject.endingAt - randomObject.startingFrom + 1)) + randomObject.startingFrom);
	return(randomObject.startText + random + randomObject.endText);
}

function cloneObject(obj)
{
	var copy;

	// Handle the 3 simple types, and null or undefined
	if (null == obj || "object" != typeof obj) return obj;

	// Handle Array
	if(obj instanceof Array)
	{
		copy = [];
		for (var i = 0, len = obj.length; i < len; i++) {
			copy[i] = cloneObject(obj[i]);
		}
		return copy;
	}

	// Handle Object
	if(obj instanceof Object)
	{
		copy = {};
		for (var attr in obj)
		{
			if (obj.hasOwnProperty(attr))
				copy[attr] = cloneObject(obj[attr]);
		}
		return copy;
	}

	return obj;
}

function entityClass(entity)
{
	this.entity = entity;
	this.previousX = 0;
	this.previousY = 0;
	this.previousZ = 0;
}

function checkProximity(entity1, entity2, distanceXZ, distanceY)
{
	if(distanceY == null)
		distanceY = distanceXZ;

	if(!(Math.abs(Entity.getX(entity1) - Entity.getX(entity2)) <= distanceXZ))
		return false;
	if(!(Math.abs(Entity.getY(entity1) - Entity.getY(entity2)) <= distanceY))
		return false;
	if(!(Math.abs(Entity.getZ(entity1) - Entity.getZ(entity2)) <= distanceXZ))
		return false;

	return true;
}

function checkProximityOfPoints(x1, y1, z1, x2, y2, z2, distanceXZ, distanceY)
{
	if(distanceY == null)
		distanceY = distanceXZ;

	if(!(Math.abs(x1 - x2) <= distanceXZ))
		return false;
	if(!(Math.abs(y1 - y2) <= distanceY))
		return false;
	if(!(Math.abs(z1 - z2) <= distanceXZ))
		return false;

	return true;
}

function stringToBoolean(string)
{
	if(typeof string != "string")
		string = String(string);
	switch(string.toLowerCase())
	{
		case "true":
		case "yes":
		case "1":
			return true;
		case "false":
		case "no":
		case "0":
		case null:
			return false;
		default:
			return Boolean(string);
	}
}

function getSavedBoolean(name, defaultValue, debug)
{
	var savedDataTest = ModPE.readData(name);
	debug = debug || false;

	// debug code
	if(DEBUG1 && debug)
	{
		var debugTest;

		if(typeof savedDataTest == "boolean")
		{
			clientMessage(name + " is bool");
			debugTest = savedDataTest;
		} else
		{
			if(typeof savedDataTest == "string")
			{
				clientMessage(name + " is string");
				debugTest = stringToBoolean(savedDataTest);
			} else
			{
				clientMessage(name + " is " + typeof savedDataTest);
			}
		}

		clientMessage(name + ": " + debugTest);

		if(typeof debugTest == "boolean")
		{
			if(debugTest)
				clientMessage(name + " true");
			else
				clientMessage(name + " false");
		} else
		{
			clientMessage(name + ", something really wrong here!");
		}
	}

	// return the saved boolean
	if(typeof savedDataTest == "boolean")
	{
		return savedDataTest;
	} else
	{
		if(typeof savedDataTest == "string")
		{
			if(savedDataTest != "" && savedDataTest != null && savedDataTest != undefined)
			{
				return stringToBoolean(savedDataTest);
			} else
			{
				// this setting has never been saved.
				if(typeof defaultValue == "boolean")
					return defaultValue;
				else
					return stringToBoolean(defaultValue)
			}
		} else
		{
			clientMessage("Error in getSavedBoolean() for " + name);
			return false;
		}
	}
}

function getLogText()
{
	//
	return(TAG + ": ");
}

function getDisplayHeight()
{
	if(android.os.Build.VERSION.SDK_INT >= 17)
	{
		var uiFlags = currentActivity.getWindow().getDecorView().getSystemUiVisibility();
		if((uiFlags & android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0)
		{
			var metrics = new android.util.DisplayMetrics();
			currentActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
			var realHeight = metrics.heightPixels;
			var realWidth = metrics.widthPixels;
			if(realHeight > realWidth) // fix auto-rotation disabled bug
			{
				var x = realHeight;
				realHeight = realWidth;
				realWidth = x;
			}
			return realHeight;
		} else
		{
			return displayHeight;
		}
	} else
	{
		return displayHeight;
	}
}

function getDisplayWidth()
{
	if(android.os.Build.VERSION.SDK_INT >= 17)
	{
		var uiFlags = currentActivity.getWindow().getDecorView().getSystemUiVisibility();
		if((uiFlags & android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0)
		{
			var metrics = new android.util.DisplayMetrics();
			currentActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
			var realHeight = metrics.heightPixels;
			var realWidth = metrics.widthPixels;
			if(realHeight > realWidth) // fix auto-rotation disabled bug
			{
				var x = realHeight;
				realHeight = realWidth;
				realWidth = x;
			}
			return realWidth;
		} else
		{
			return displayWidth;
		}
	} else
	{
		return displayWidth;
	}
}

function dumpFlagStateToLog(uiFlags)
{
	// https://developer.android.com/samples/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.html
	if ((uiFlags & android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
		clientMessage("SYSTEM_UI_FLAG_LOW_PROFILE is set");
	} else {
		clientMessage("SYSTEM_UI_FLAG_LOW_PROFILE is unset");
	}

	if ((uiFlags & android.view.View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
		clientMessage("SYSTEM_UI_FLAG_FULLSCREEN is set");
	} else {
		clientMessage("SYSTEM_UI_FLAG_FULLSCREEN is unset");
	}

	if ((uiFlags & android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
		clientMessage("SYSTEM_UI_FLAG_HIDE_NAVIGATION is set");
	} else {
		clientMessage("SYSTEM_UI_FLAG_HIDE_NAVIGATION is unset");
	}

	if ((uiFlags & android.view.View.SYSTEM_UI_FLAG_IMMERSIVE) != 0) {
		clientMessage("SYSTEM_UI_FLAG_IMMERSIVE is set");
	} else {
		clientMessage("SYSTEM_UI_FLAG_IMMERSIVE is unset");
	}

	if ((uiFlags & android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0) {
		clientMessage("SYSTEM_UI_FLAG_IMMERSIVE_STICKY is set");
	} else {
		clientMessage("SYSTEM_UI_FLAG_IMMERSIVE_STICKY is unset");
	}
}
//########## MISC functions - END ##########


//########## UTILS OF UIs functions ##########
function convertDpToPixel(dp)
{
	//
	return Math.round(dp * deviceDensity);
}

function basicMinecraftTextView(text, textSize) // TextView with just the Minecraft font
{
	var lineSpacing = convertDpToPixel(4);

	var textview = new android.widget.TextView(currentActivity);
	textview.setText(new android.text.Html.fromHtml(text));
	if(textSize != null)
		textview.setTextSize(textSize);
	textview.setTypeface(MinecraftButtonLibrary.ProcessedResources.font);
	textview.setPaintFlags(textview.getPaintFlags() | android.graphics.Paint.SUBPIXEL_TEXT_FLAG);
	textview.setLineSpacing(lineSpacing, 1);
	if(android.os.Build.VERSION.SDK_INT > 19) // KITKAT
		textview.setShadowLayer(1, Math.round((textview.getLineHeight() - lineSpacing) / 8), Math.round((textview.getLineHeight() - lineSpacing) / 8), android.graphics.Color.parseColor("#FF333333"));
	else
		textview.setShadowLayer(0.001, Math.round((textview.getLineHeight() - lineSpacing) / 8), Math.round((textview.getLineHeight() - lineSpacing) / 8), android.graphics.Color.parseColor("#FF333333"));

	return textview;
}

function defaultColoredMinecraftButton(text, colorString)
{
	var padding = convertDpToPixel(4);

	var bg = android.graphics.drawable.GradientDrawable();
	bg.setColor(android.graphics.Color.TRANSPARENT);
	bg.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
	bg.setStroke(convertDpToPixel(1), android.graphics.Color.parseColor(colorString));

	var coloredButton = basicMinecraftTextView(text, buttonsSize);
	coloredButton.setGravity(android.view.Gravity.CENTER);
	coloredButton.setTextColor(android.graphics.Color.parseColor(colorString));
	coloredButton.setBackgroundDrawable(bg);
	coloredButton.setPadding(padding, padding, padding, padding);
	coloredButton.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));

	return coloredButton;
}
//########## UTILS OF UIs functions - END ##########


//########################################################################################################################################################
// Utils of popup's UI functions
//########################################################################################################################################################

const MARGIN_HORIZONTAL_BIG = 16;
const MARGIN_HORIZONTAL_SMALL = 4;

function setMarginsLinearLayout(view, left, top, right, bottom)
{
	var originalParams = view.getLayoutParams();
	var newParams = new android.widget.LinearLayout.LayoutParams(originalParams);
	newParams.setMargins(convertDpToPixel(left), convertDpToPixel(top), convertDpToPixel(right), convertDpToPixel(bottom));
	view.setLayoutParams(newParams);
}

function dividerText()
{
	var dividerText = new android.widget.TextView(currentActivity);
	dividerText.setText(" ");
	return dividerText;
}

function progressBarForInformation(value, max, invert, text)
{
	if(value > max)
		value = max;
	if(invert)
		value = max - value;
	var progressBar = new android.widget.ProgressBar(currentActivity, null, android.R.attr.progressBarStyleHorizontal);
	progressBar.setIndeterminate(false);
	progressBar.setMax(max);
	progressBar.setProgress(Math.round(value));
	progressBar.setLayoutParams(new android.widget.LinearLayout.LayoutParams(0, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 3));

	var text1 = new android.widget.TextView(currentActivity);
	text1.setText(text);
	text1.setGravity(android.view.Gravity.LEFT);
	text1.setTextSize(12);
	text1.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
	text1.setPadding(Math.floor(8 * deviceDensity), 0, Math.floor(8 * deviceDensity), 0);
	text1.setLayoutParams(new android.widget.LinearLayout.LayoutParams(0, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 1));

	var layoutH = new android.widget.LinearLayout(currentActivity);
	layoutH.setOrientation(android.widget.LinearLayout.HORIZONTAL);

	layoutH.addView(progressBar);
	layoutH.addView(text1);
	return layoutH;
}

function defaultContentTextView(text) // TextView for contents (basicMinecraftTextView with little changes)
{
	var textview = basicMinecraftTextView(text, 12);
	textview.setTextColor(android.graphics.Color.parseColor(MinecraftButtonLibrary.defaultButtonTextColor));

	return textview;
}

function defaultSubTitle(subtitle) // TextView with Minecraft background
{
	var padding = convertDpToPixel(8);

	var bg = android.graphics.drawable.GradientDrawable();
	bg.setColor(android.graphics.Color.parseColor("#FF736A6F"));
	bg.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
	bg.setStroke(convertDpToPixel(2), android.graphics.Color.parseColor("#FF93898B"));

	var title = basicMinecraftTextView(subtitle, 16);
	title.setTextColor(android.graphics.Color.WHITE);
	title.setBackgroundDrawable(bg);
	title.setPadding(padding, padding, padding, padding);

	return title;
}

function defaultLayout(title)
{
	var layout = new android.widget.LinearLayout(currentActivity);
	layout.setOrientation(android.widget.LinearLayout.VERTICAL);
	var padding = convertDpToPixel(8);
	layout.setPadding(padding, padding, padding, padding);
	layout.setBackgroundDrawable(background);

	var titleTextView = basicMinecraftTextView(title, 18);
	titleTextView.setTextColor(android.graphics.Color.WHITE);
	titleTextView.setGravity(android.view.Gravity.CENTER);
	layout.addView(titleTextView);
	setMarginsLinearLayout(titleTextView, 0, 4, 0, 4);

	var divider = new android.view.View(currentActivity);
	divider.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.parseColor("#958681")));
	divider.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, convertDpToPixel(1)));
	layout.addView(divider);
	setMarginsLinearLayout(divider, 0, 8, 0, 8);

	return layout;
}

function defaultPopup(layout)
{
	var scroll = new android.widget.ScrollView(currentActivity);
	scroll.addView(layout);

	var popup = new android.app.Dialog(currentActivity);
	popup.requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
	popup.setContentView(scroll);
	return popup;
}

function showImmersivePopup(popup)
{
	//Set the dialog to not focusable (makes navigation ignore us adding the window)
	// http://stackoverflow.com/questions/22794049/how-to-maintain-the-immersive-mode-in-dialogs
	popup.getWindow().setFlags(android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

	//Show the dialog!
	popup.show();

	//Set the dialog to immersive
	popup.getWindow().getDecorView().setSystemUiVisibility(currentActivity.getWindow().getDecorView().getSystemUiVisibility());

	//Clear the not focusable flag from the window
	popup.getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}


//########################################################################################################################################################
// Popup's UI functions
//########################################################################################################################################################

function infoDesnoGunsMod()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				if(isPro())
					layout = defaultLayout("DesnoGuns Mod Pro");
				else
					layout = defaultLayout("DesnoGuns Mod");

				var text = defaultContentTextView("Welcome to the DesnoGuns Mod by Desno365!");
				layout.addView(text);
				setMarginsLinearLayout(text, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				var informationButton = MinecraftButton();
				informationButton.setText("Information");
				informationButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationUI();
						popup.dismiss();
					}
				});
				layout.addView(informationButton);
				setMarginsLinearLayout(informationButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var settingsButton = MinecraftButton();
				settingsButton.setText("Settings");
				settingsButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						settingsUI();
						popup.dismiss();
					}
				});
				layout.addView(settingsButton);
				setMarginsLinearLayout(settingsButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var addonsButton = MinecraftButton();
				addonsButton.setText("Addons");
				addonsButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						addonsUI();
						popup.dismiss();
					}
				});
				layout.addView(addonsButton);
				setMarginsLinearLayout(addonsButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);


				var updatesButton = MinecraftButton();
				updatesButton.setText("Check for updates");
				updatesButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						if(latestVersion == CURRENT_VERSION || latestVersion == undefined || latestVersion == " ")
						{
							latestVersionUI();
							popup.dismiss();
						} else
						{
							updateAvailableUI();
						}
					}
				});
				layout.addView(updatesButton);
				setMarginsLinearLayout(updatesButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var modThreadButton = MinecraftButton();
				modThreadButton.setText("Visit the mod thread");
				modThreadButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						visitThread();
						popup.dismiss();
					}
				});
				layout.addView(modThreadButton);
				setMarginsLinearLayout(modThreadButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var supportButton = MinecraftButton();
				supportButton.setText("Support the developer");
				supportButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						supportUI();
						popup.dismiss();
					}
				});
				layout.addView(supportButton);
				setMarginsLinearLayout(supportButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);


				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function informationUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Information");

				var button = MinecraftButton();
				button.setText("View guns specifications");
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecifications();
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText("Other items");
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationOtherItems();
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						infoDesnoGunsMod();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function informationGunsSpecifications()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Guns specifications");

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_ASSAULT_RIFLE));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_ASSAULT_RIFLE);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_SUB_MACHINE));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_SUB_MACHINE);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_LIGHT_MACHINE));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_LIGHT_MACHINE);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_SNIPER_RIFLE));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_SNIPER_RIFLE);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_SHOTGUN));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_SHOTGUN);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_MACHINE_PISTOL));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_MACHINE_PISTOL);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_HANDGUN));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_HANDGUN);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_LAUNCHER));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_LAUNCHER);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText(getGunTypeName(GUN_TYPE_MINIGUN));
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecificationsForGunType(GUN_TYPE_MINIGUN);
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationUI();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function informationGunsSpecificationsForGunType(gunType)
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout(getGunTypeName(gunType) + " specifications");

				for(var i in allGuns)
				{
					if(allGuns[i].gunType == gunType)
					{
						var text = new android.widget.TextView(currentActivity);
						text.setText(new android.text.Html.fromHtml("<b>" + allGuns[i].name + "</b> (ID: " + allGuns[i].id + ")"));
						text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
						layout.addView(text);

						var text = new android.widget.TextView(currentActivity);
						text.setText(getGunTypeName(allGuns[i].gunType));
						text.setGravity(android.view.Gravity.RIGHT);
						text.setTextSize(12);
						text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
						layout.addView(text);


						if(allGuns[i].ammo != 1)
						{
							var text = new android.widget.TextView(currentActivity);
							text.setText("Fire rate");
							text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
							layout.addView(text);

							if(allGuns[i].fireRate == 1 && allGuns[i].buttonType == BUTTON_TYPE_ON_CLICK)
							{
								var text = new android.widget.TextView(currentActivity);
								text.setText("Shoot one bullet every time the user clicks the \"fire\" button");
								text.setTextSize(12);
								text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
								layout.addView(text);
							} else
							{
								layout.addView(progressBarForInformation(allGuns[i].fireRate, 20, true, (allGuns[i].fireRate / 20) + " second(s)"));
							}
						}


						var text = new android.widget.TextView(currentActivity);
						text.setText("Recoil");
						text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
						layout.addView(text);

						layout.addView(progressBarForInformation(allGuns[i].recoil, 30, true, (61 - (allGuns[i].recoil * 2)) + "/60"));


						var text = new android.widget.TextView(currentActivity);
						text.setText("Accuracy");
						text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
						layout.addView(text);

						layout.addView(progressBarForInformation(allGuns[i].accuracy, SPAS.accuracy, true, (51 - allGuns[i].accuracy) + "/50"));


						var text = new android.widget.TextView(currentActivity);
						text.setText("Zoom level (when aiming)");
						text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
						layout.addView(text);

						layout.addView(progressBarForInformation(allGuns[i].zoomLevel, ZOOM_SNIPER, false, allGuns[i].zoomLevel + " FOV"));


						if(allGuns[i].shotType != SHOT_TYPE_FLAMETHROWER)
						{
							var text = new android.widget.TextView(currentActivity);
							text.setText("Bullet speed");
							text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
							layout.addView(text);

							layout.addView(progressBarForInformation(allGuns[i].bulletSpeed * 10, 100, false, allGuns[i].bulletSpeed * 10 + "/100"));
						}
						

						var text = new android.widget.TextView(currentActivity);
						text.setText("Ammo");
						text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
						layout.addView(text);

						layout.addView(progressBarForInformation(allGuns[i].ammo, 125, false, allGuns[i].ammo.toString()));


						if(allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TOUCH || allGuns[i].bulletType == BULLET_TYPE_NORMAL_EXPLOSIVE_ON_TIME)
						{
							var text = new android.widget.TextView(currentActivity);
							text.setText("Explosion radius");
							text.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
							layout.addView(text);

							layout.addView(progressBarForInformation(allGuns[i].bulletsExplosionRadius, 5, false, allGuns[i].bulletsExplosionRadius + "/10"));;
						}

						layout.addView(dividerText());
					}
				}


				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationGunsSpecifications();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function informationOtherItems()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Other items");

				var textview = defaultContentTextView("<i>Knife</i>: ID: " + KNIFE_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Parachute</i>: ID: " + PARACHUTE_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Medical Kit</i>: ID: " + MEDICAL_KIT_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Riot Shield</i>: ID: " + RIOT_SHIELD_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Grenade</i>: ID: " + GRENADE.id);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Fragment Grenade</i>: ID: " + FRAGMENT.id);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Molotov</i>: ID: " + MOLOTOV.id);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Smoke Grenade</i>: ID: " + SMOKE.id);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Assault Rifle Ammo</i>: ID: " + AMMO_ASSAULT_RIFLE_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Sub Machine Ammo</i>: ID: " + AMMO_SUB_MACHINE_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Light Machine Ammo</i>: ID: " + AMMO_LIGHT_MACHINE_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Sniper Rifle Ammo</i>: ID: " + AMMO_SNIPER_RIFLE_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Shotgun Ammo</i>: ID: " + AMMO_SHOTGUN_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Machine Pistol Ammo</i>: ID: " + AMMO_MACHINE_PISTOL_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Handgun Ammo</i>: ID: " + AMMO_HANDGUN_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Launcher Ammo</i>: ID: " + AMMO_LAUNCHER_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Minigun Ammo</i>: ID: " + AMMO_MINIGUN_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Juggernaut Helmet</i>: ID: " + JUGGERNAUT_HELMET_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Juggernaut Body</i>: ID: " + JUGGERNAUT_BODY_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Juggernaut Pants</i>: ID: " + JUGGERNAUT_PANTS_ID);
				layout.addView(textview);

				layout.addView(dividerText());

				var textview = defaultContentTextView("<i>Juggernaut Boots</i>: ID: " + JUGGERNAUT_BOOTS_ID);
				layout.addView(textview);

				layout.addView(dividerText());


				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						informationUI();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function settingsUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Settings");

				var padding = convertDpToPixel(8);


				var title = defaultSubTitle("Buttons");
				layout.addView(title);

				layout.addView(dividerText());

				var sizeText = new android.widget.TextView(currentActivity);
				sizeText.setText("Select the preferred size of the \"fire\" and \"aim\" buttons (default is " + BUTTONS_SIZE_DEFAULT + ")");
				sizeText.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				sizeText.setPadding(padding, 0, padding, 0);
				layout.addView(sizeText);

				var sizeChooser = new android.widget.SeekBar(currentActivity);
				sizeChooser.setMax(30);
				sizeChooser.setProgress(buttonsSize - 10);
				sizeChooser.setOnSeekBarChangeListener(new android.widget.SeekBar.OnSeekBarChangeListener()
				{
					onProgressChanged: function()
					{
						buttonsSize = sizeChooser.getProgress() + 10;
						sizeText1.setText("Size: " + buttonsSize + "/" + (sizeChooser.getMax() + 10));
					},
					onStopTrackingTouch: function()
					{
						ModPE.saveData("bSize", buttonsSize);
					}
				});
				sizeChooser.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(sizeChooser);

				var sizeText1 = new android.widget.TextView(currentActivity);
				sizeText1.setText("Size: " + buttonsSize + "/" + (sizeChooser.getMax() + 10));
				sizeText1.setTextColor(android.graphics.Color.parseColor("#FFC0C0C0"));
				sizeText1.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(sizeText1);

				layout.addView(dividerText());



				var moveButtonsText = new android.widget.TextView(currentActivity);
				moveButtonsText.setText("Select the y positions of the \"fire\" and \"aim\" buttons (default is 0)");
				moveButtonsText.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				moveButtonsText.setPadding(padding, 0, padding, 0);
				layout.addView(moveButtonsText);

				var maxY = (getDisplayHeight() / 4 * 3);
				if(maxY % 2 != 0)
					maxY--;
				var moveButtonsChooser = new android.widget.SeekBar(currentActivity);
				moveButtonsChooser.setMax(maxY);
				moveButtonsChooser.setProgress((-pixelsOffsetButtons) + (maxY / 2));
				moveButtonsChooser.setOnSeekBarChangeListener(new android.widget.SeekBar.OnSeekBarChangeListener()
				{
					onProgressChanged: function()
					{
						pixelsOffsetButtons = -(moveButtonsChooser.getProgress() - (maxY / 2));
						moveButtonsText1.setText("Y position: " + (-pixelsOffsetButtons) + " pixels");
					},
					onStopTrackingTouch: function()
					{
						ModPE.saveData("mButtons", pixelsOffsetButtons);
					}
				});
				moveButtonsChooser.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(moveButtonsChooser);

				var moveButtonsText1 = new android.widget.TextView(currentActivity);
				moveButtonsText1.setText("Y position: " + (-pixelsOffsetButtons) + " pixels");
				moveButtonsText1.setTextColor(android.graphics.Color.parseColor("#FFC0C0C0"));
				moveButtonsText1.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(moveButtonsText1);

				layout.addView(dividerText());



				var sizeText2 = new android.widget.TextView(currentActivity);
				sizeText2.setText("Select the preferred size of the ammo text (default is " + AMMO_TEXT_SIZE_DEFAULT + ")");
				sizeText2.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				sizeText2.setPadding(padding, 0, padding, 0);
				layout.addView(sizeText2);

				var sizeChooser1 = new android.widget.SeekBar(currentActivity);
				sizeChooser1.setMax(20);
				sizeChooser1.setProgress(ammoTextSize - 8);
				sizeChooser1.setOnSeekBarChangeListener(new android.widget.SeekBar.OnSeekBarChangeListener()
				{
					onProgressChanged: function()
					{
						ammoTextSize = sizeChooser1.getProgress() + 8;
						sizeText3.setText("Size: " + ammoTextSize + "/" + (sizeChooser1.getMax() + 8));
					},
					onStopTrackingTouch: function()
					{
						ModPE.saveData("aTSize", ammoTextSize);
					}
				});
				sizeChooser1.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(sizeChooser1);

				var sizeText3 = new android.widget.TextView(currentActivity);
				sizeText3.setText("Size: " + ammoTextSize + "/" + (sizeChooser1.getMax() + 8));
				sizeText3.setTextColor(android.graphics.Color.parseColor("#FFC0C0C0"));
				sizeText3.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(sizeText3);

				layout.addView(dividerText());



				var switchButtonsPosition = new android.widget.Switch(currentActivity);
				switchButtonsPosition.setChecked(switchedButtonsPosition);
				switchButtonsPosition.setText("Switch position of the \"fire\" and \"aim\" buttons");
				switchButtonsPosition.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchButtonsPosition.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						switchedButtonsPosition = !switchedButtonsPosition;
						ModPE.saveData("sBPosition", switchedButtonsPosition);
					}
				});
				switchButtonsPosition.setPadding(padding, 0, padding, 0);
				layout.addView(switchButtonsPosition);

				layout.addView(dividerText());



				var switchButtonsStyle = new android.widget.Switch(currentActivity);
				switchButtonsStyle.setChecked(minecraftStyleForButtons);
				switchButtonsStyle.setText("Enable Minecraft style for the \"fire\" and \"aim\" buttons");
				switchButtonsStyle.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchButtonsStyle.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						minecraftStyleForButtons = !minecraftStyleForButtons;
						ModPE.saveData("sBStyle", minecraftStyleForButtons);
					}
				});
				switchButtonsStyle.setPadding(padding, 0, padding, 0);
				layout.addView(switchButtonsStyle);

				layout.addView(dividerText());




				var title = defaultSubTitle("UI");
				layout.addView(title);

				layout.addView(dividerText());

				var switchSight = new android.widget.Switch(currentActivity);
				switchSight.setChecked(shouldDisplaySight);
				switchSight.setText("Display a little cross in the center of the screen");
				switchSight.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchSight.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						shouldDisplaySight = !shouldDisplaySight;
						ModPE.saveData("dSight", shouldDisplaySight);
						try {
							popupSightImage.dismiss();
						} catch(e) {}
					}
				});
				switchSight.setPadding(padding, 0, padding, 0);
				layout.addView(switchSight);

				layout.addView(dividerText());



				var switchGunName = new android.widget.Switch(currentActivity);
				switchGunName.setChecked(displayGunNameInAmmo);
				switchGunName.setText("Display gun's name in the ammo text");
				switchGunName.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchGunName.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						displayGunNameInAmmo = !displayGunNameInAmmo;
						ModPE.saveData("dNameAmmo", displayGunNameInAmmo);
					}
				});
				switchGunName.setPadding(padding, 0, padding, 0);
				layout.addView(switchGunName);

				layout.addView(dividerText());



				var title = defaultSubTitle("Audio");
				layout.addView(title);

				layout.addView(dividerText());

				var audioText = new android.widget.TextView(currentActivity);
				audioText.setText("Set the sound volume of the mod (default is " + 10 + ")");
				audioText.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				audioText.setPadding(padding, 0, padding, 0);
				layout.addView(audioText);

				var audioChooser = new android.widget.SeekBar(currentActivity);
				audioChooser.setMax(10);
				audioChooser.setProgress(generalVolume * 10);
				audioChooser.setOnSeekBarChangeListener(new android.widget.SeekBar.OnSeekBarChangeListener()
				{
					onProgressChanged: function()
					{
						generalVolume = audioChooser.getProgress() / 10;
						audioText1.setText("Volume: " + audioChooser.getProgress() + "/" + audioChooser.getMax());
					},
					onStopTrackingTouch: function()
					{
						ModPE.saveData("generalVolume", generalVolume);
					}
				});
				audioChooser.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(audioChooser);

				var audioText1 = new android.widget.TextView(currentActivity);
				audioText1.setText("Volume: " + (generalVolume * 10) + "/" + audioChooser.getMax());
				audioText1.setTextColor(android.graphics.Color.parseColor("#FFC0C0C0"));
				audioText1.setPadding(padding * 2, 0, padding * 2, 0);
				layout.addView(audioText1);

				layout.addView(dividerText());



				var title = defaultSubTitle("Other");
				layout.addView(title);

				layout.addView(dividerText());

				var switchWorkaround = new android.widget.Switch(currentActivity);
				switchWorkaround.setChecked(deathWorkaround);
				switchWorkaround.setText("Enable workaround to prevent returning arrows while shooting (experimental)");
				switchWorkaround.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchWorkaround.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						deathWorkaround = !deathWorkaround;
						ModPE.saveData("dWorkaround", deathWorkaround);

						if(DEBUG1)
						{
							var dWorkaroundTest = ModPE.readData("dWorkaround");
							if(typeof dWorkaroundTest == "boolean")
							{
								clientMessage("bool");
							} else
							{
								if(typeof dWorkaroundTest == "string")
								{
									clientMessage("string");
								} else
								{
									clientMessage(typeof dWorkaroundTest);
								}
							}
							clientMessage(dWorkaroundTest);

							if(deathWorkaround)
								clientMessage("workaround");
							else
								clientMessage("keep everything");
						}
					}
				});
				switchWorkaround.setPadding(padding, 0, padding, 0);
				layout.addView(switchWorkaround);

				layout.addView(dividerText());



				var switchReloadCreative = new android.widget.Switch(currentActivity);
				switchReloadCreative.setChecked(reloadInCreative);
				switchReloadCreative.setText("Reload in creative");
				switchReloadCreative.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchReloadCreative.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						reloadInCreative = !reloadInCreative;
						ModPE.saveData("rCreative", reloadInCreative);

						switchInstantReloadCreative.setEnabled(reloadInCreative);
					}
				});
				switchReloadCreative.setPadding(padding, 0, padding, 0);
				layout.addView(switchReloadCreative);

				layout.addView(dividerText());



				var switchInstantReloadCreative = new android.widget.Switch(currentActivity);
				switchInstantReloadCreative.setChecked(instantReloadInCreative);
				switchInstantReloadCreative.setText("Instant reload in creative (disable reload sounds)");
				switchInstantReloadCreative.setTextColor(android.graphics.Color.parseColor("#FFFFFFFF"));
				switchInstantReloadCreative.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener()
				{
					onCheckedChanged: function()
					{
						instantReloadInCreative = !instantReloadInCreative;
						ModPE.saveData("instReload", instantReloadInCreative);
					}
				});
				switchInstantReloadCreative.setEnabled(reloadInCreative);
				switchInstantReloadCreative.setPadding(padding, 0, padding, 0);
				layout.addView(switchInstantReloadCreative);

				layout.addView(dividerText());



				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						infoDesnoGunsMod();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function addonsUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Addons");

				var button = MinecraftButton();
				button.setText("Install an addon");
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						var intentBrowser = new android.content.Intent(currentActivity);
						intentBrowser.setAction(android.content.Intent.ACTION_VIEW);
						intentBrowser.setData(android.net.Uri.parse("http://desno365.github.io/minecraft/desnoguns-mod/addons/"));
						currentActivity.startActivity(intentBrowser);
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button = MinecraftButton();
				button.setText("Enabled addons");
				button.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						enabledAddonsUI();
						popup.dismiss();
					}
				});
				layout.addView(button);
				setMarginsLinearLayout(button, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						infoDesnoGunsMod();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function enabledAddonsUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Enabled addons");

				var padding = convertDpToPixel(8);


				for(var i in loadedAddons)
				{
					var title = defaultSubTitle(loadedAddons[i].name);
					layout.addView(title);

					layout.addView(dividerText());

					var textview = defaultContentTextView(loadedAddons[i].description + "<br><br>");
					textview.setPadding(padding, 0, padding, 0);
					layout.addView(textview);
				}

				// 0 addons enabled
				if(loadedAddons.length == 0)
				{
					layout.addView(dividerText());

					var textview = defaultContentTextView("You don't have any addon enabled.");
					layout.addView(textview);

					layout.addView(dividerText());
				}

				// one or more addons enabled without the pro version
				if(loadedAddons.length >= 1 && (!isPro()))
				{
					var textview = defaultContentTextView("Buy the <a href=\"https://play.google.com/store/apps/details?id=com.desno365.desnogunsmodkey\">DesnoGuns Mod Pro Key</a> to enable more than 2 addons simultaneously.<br>");
					textview.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
					textview.setTextSize(11);
					layout.addView(textview);
				}


				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						addonsUI();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function latestVersionUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("You're up to date");

				var updatesText = defaultContentTextView("You have the latest version of the mod.<br><br>" +
					"Download the Desno365's Mods app to receive notifications of new updates and get the latest news about my mods.");
				layout.addView(updatesText);
				setMarginsLinearLayout(updatesText, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				var playStoreView = new android.widget.ImageView(currentActivity);
				playStoreView.setImageBitmap(playStorePngScaled);
				playStoreView.setOnClickListener(new android.view.View.OnClickListener(
				{
					onClick: function()
					{
						try
						{
							//play store installed
							currentActivity.startActivity(new android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse("market://details?id=" + "com.desno365.mods")));
						} catch (err)
						{
							//play store not installed
							currentActivity.startActivity(new android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse("http://play.google.com/store/apps/details?id=" + "com.desno365.mods")));
						}
					}
				}));
				layout.addView(playStoreView);
				setMarginsLinearLayout(playStoreView, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);



				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						infoDesnoGunsMod();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function updateAvailableUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("DesnoGuns Mod: new version");

				var updatesText = defaultContentTextView("New version available, you have the " + CURRENT_VERSION + " version and the latest version is " + latestVersion + ".<br>" +
					"You can find a download link on Desno365's website (press the button to visit it).");
				layout.addView(updatesText);
				setMarginsLinearLayout(updatesText, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var threadButton = MinecraftButton();
				threadButton.setText("Visit website");
				threadButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						var intentBrowser = new android.content.Intent(currentActivity);
						intentBrowser.setAction(android.content.Intent.ACTION_VIEW);
						intentBrowser.setData(android.net.Uri.parse("http://desno365.github.io/minecraft/desnoguns-mod/"));
						currentActivity.startActivity(intentBrowser);
						popup.dismiss();
					}
				});
				layout.addView(threadButton);
				setMarginsLinearLayout(threadButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				popup.setCanceledOnTouchOutside(false);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function visitThread()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var intentBrowser = new android.content.Intent(currentActivity);
				intentBrowser.setAction(android.content.Intent.ACTION_VIEW);
				intentBrowser.setData(android.net.Uri.parse("http://www.minecraftforum.net/forums/minecraft-pocket-edition/mcpe-mods-tools/2299721-mod-desnoguns-mod-more-than-50-weapons-r001-by"));
				currentActivity.startActivity(intentBrowser);
			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

function supportUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("Support me");

				var text = defaultContentTextView("This mod was brought to you with love by Desno365 :)<br>Thank you for playing with it.");
				layout.addView(text);
				setMarginsLinearLayout(text, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button1 = MinecraftButton();
				button1.setText("Follow me on Twitter");
				button1.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						android.widget.Toast.makeText(currentActivity, "Opening the URL...", 0).show();
						var intentBrowser = new android.content.Intent(currentActivity);
						intentBrowser.setAction(android.content.Intent.ACTION_VIEW);
						intentBrowser.setData(android.net.Uri.parse("https://twitter.com/desno365"));
						currentActivity.startActivity(intentBrowser);
						popup.dismiss();
					}
				});
				layout.addView(button1);
				setMarginsLinearLayout(button1, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var button2 = MinecraftButton();
				button2.setText("Subscribe to my YouTube channel");
				button2.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						android.widget.Toast.makeText(currentActivity, "Opening the URL...", 0).show();
						var intentBrowser = new android.content.Intent(currentActivity);
						intentBrowser.setAction(android.content.Intent.ACTION_VIEW);
						intentBrowser.setData(android.net.Uri.parse("http://www.youtube.com/channel/UCJQL47nQnsijcaN_7pMsjCQ/videos"));
						currentActivity.startActivity(intentBrowser);
						popup.dismiss();
					}
				});
				layout.addView(button2);
				if(!isPro())
					setMarginsLinearLayout(button2, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);
				else
					setMarginsLinearLayout(button2, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);

				if(!isPro())
				{
					var button3 = MinecraftButton();
					button3.setText("Get the Pro Key");
					button3.setOnClickListener(new android.view.View.OnClickListener()
					{
						onClick: function()
						{
							openProKeyOnPlayStore();
							popup.dismiss();
						}
					});
					layout.addView(button3);
					setMarginsLinearLayout(button3, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_BIG);
				}

				var backButton = MinecraftButton();
				backButton.setText("Back");
				backButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						infoDesnoGunsMod();
						popup.dismiss();
					}
				});
				layout.addView(backButton);
				setMarginsLinearLayout(backButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);
				setMarginsLinearLayout(exitButton, 0, MARGIN_HORIZONTAL_SMALL, 0, MARGIN_HORIZONTAL_SMALL);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
				clientMessage("Maybe GUI is not supported for your device.");
			}
		}
	});
}

function easterEggUI()
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout;
				layout = defaultLayout("I don't want to explode!");

				layout.addView(dividerText());

				var layoutH = new android.widget.LinearLayout(currentActivity);
				layoutH.setOrientation(android.widget.LinearLayout.HORIZONTAL);

				var inputText1 = new android.widget.EditText(currentActivity);
				inputText1.setHint("The code");
				if(codeEE == null || codeEE == undefined)
					inputText1.setEnabled(false);
				layoutH.addView(inputText1);

				var button1 = MinecraftButton();
				button1.setText("Ok");
				button1.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						if(codeEE != null && codeEE != undefined)
						{
							if(String(codeEE) == String(inputText1.getText()))
							{
								Level.setTime(1000);
								Level.setTime(10000);
								Level.setTime(1000);
								Level.setTile(xCoalEE, yCoalEE, zCoalEE, 0);
								Level.setTile(xCoalEE, yCoalEE - 1, zCoalEE, 0);

								popup.dismiss();
								easterEgg = true;
								unstuck = -1;
								pigmen = [];
								codeEE = null;

								BARRETT_EXPLOSIVE.buttonType = BUTTON_TYPE_ON_TOUCH;
								BARRETT_EXPLOSIVE.ammo = 500;
								BARRETT_EXPLOSIVE.recoil = 3;
								BARRETT_EXPLOSIVE.fireRate = 3;
								Item.setMaxDamage(BARRETT_EXPLOSIVE.id, 500);

								if(Player.getCarriedItem() == BARRETT_EXPLOSIVE.id)
									changeCarriedItemHook(BARRETT_EXPLOSIVE.id, BARRETT.id);

								currentActivity.runOnUiThread(new java.lang.Runnable(
								{
									run: function()
									{
										for(var ms = 0; ms < 60; ms++)
										{
											new android.os.Handler().postDelayed(new java.lang.Runnable(
											{
												run: function()
												{
													ModPE.showTipMessage("§" + currentColorEE.toString(16) + "Easter Egg enabled!");
													if(currentColorEE == 15)
														currentColorEE = 0;
													else
														currentColorEE++;
												}
											}), ms * 250 + 1);
										}
									}
								}));
							} else
							{
								currentActivity.runOnUiThread(new java.lang.Runnable()
								{
									run: function()
									{
										android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("Wrong code!"), android.widget.Toast.LENGTH_SHORT).show();
									}
								});
							}
						}
					}
				});
				if(codeEE == null || codeEE == undefined)
					button1.setEnabled(false);
				layoutH.addView(button1);

				layout.addView(layoutH);

				layout.addView(dividerText());

				var button2 = MinecraftButton();
				button2.setText("Start wave");
				button2.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						Level.setTime(10000);
						for(var i = 0; i <= 20; i++)
						{
							var playerDir = getDirection(getYaw(), getPitch());

							pigmen.push(Level.spawnMob(getPlayerX() + (playerDir.x * 10), getPlayerY(), getPlayerZ() + (playerDir.z * 10), 36));

							// enable Sin0psysS's spawning pattern code (with some changes by me)
							unstuck = 20;
						}
						clientMessage("Barrett Explosive.");
						popup.dismiss();
					}
				});
				layout.addView(button2);

				layout.addView(dividerText());

				var exitButton = MinecraftButton();
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);


				var popup = defaultPopup(layout);
				showImmersivePopup(popup);

			} catch(err)
			{
				clientMessage("Error: " + err);
			}
		}
	});
}

// No Minecraft Layout because this UI can be showed at startup
function addonErrorUI(addonName, error)
{
	currentActivity.runOnUiThread(new java.lang.Runnable()
	{
		run: function()
		{
			try
			{
				var layout = new android.widget.LinearLayout(currentActivity);
				var padding = convertDpToPixel(8);
				layout.setPadding(padding, padding, padding, padding);
				layout.setOrientation(android.widget.LinearLayout.VERTICAL);

				var scroll = new android.widget.ScrollView(currentActivity);
				scroll.addView(layout);

				var popup = new android.app.Dialog(currentActivity);
				popup.setContentView(scroll);
				popup.setTitle(new android.text.Html.fromHtml("DesnoGuns addon error!"));
				popup.setCanceledOnTouchOutside(false);

				var text = new android.widget.TextView(currentActivity);
				text.setText(new android.text.Html.fromHtml("An error has been found in \"" + addonName + "\". The addon has been disabled.<br><br>" +
					error));
				layout.addView(text);

				layout.addView(dividerText());

				var exitButton = new android.widget.Button(currentActivity);
				exitButton.setText("Close");
				exitButton.setOnClickListener(new android.view.View.OnClickListener()
				{
					onClick: function()
					{
						popup.dismiss();
					}
				});
				layout.addView(exitButton);


				showImmersivePopup(popup);

			} catch(err)
			{
				print("Error: " + err);
			}
		}
	});
}

// No Minecraft Layout because this UI can be showed at startup
function texturePackNotIntsalledWarningUI(customMessage)
{
	if(!textureUiShowed)
	{
		textureUiShowed = true;

		// custom message (used with addons)
		if(customMessage == null)
			customMessage = "Seems that you haven't installed the DesnoGuns texture pack.<br><br>Please install the texture pack of the mod and <b>restart BlockLauncher</b> to enjoy all the features of the DesnoGuns Mod.";

		currentActivity.runOnUiThread(new java.lang.Runnable()
		{
			run: function()
			{
				try
				{
					var layout = new android.widget.LinearLayout(currentActivity);
					var padding = convertDpToPixel(8);
					layout.setPadding(padding, padding, padding, padding);
					layout.setOrientation(android.widget.LinearLayout.VERTICAL);

					var scroll = new android.widget.ScrollView(currentActivity);
					scroll.addView(layout);

					var popup = new android.app.Dialog(currentActivity);
					popup.setContentView(scroll);
					popup.setTitle(new android.text.Html.fromHtml("Texture Pack not installed"));
					popup.setCanceledOnTouchOutside(false);

					var text = new android.widget.TextView(currentActivity);
					text.setText(new android.text.Html.fromHtml(customMessage));
					layout.addView(text);

					layout.addView(dividerText());

					var exitButton = new android.widget.Button(currentActivity);
					exitButton.setText("OK");
					exitButton.setOnClickListener(new android.view.View.OnClickListener()
					{
						onClick: function()
						{
							textureUiShowed = false;
							popup.dismiss();
						}
					});
					layout.addView(exitButton);


					showImmersivePopup(popup);

				} catch(err)
				{
					print("Error: " + err);
				}
			}
		});
	}
}


//########################################################################################################################################################
// Renders
//########################################################################################################################################################

// grenade render by Anti, thanks!
function addGrenadeRenderType(renderer)
{
	var model = renderer.getModel();

	var head = model.getPart("head");
	var body = model.getPart("body");
	var rArm = model.getPart("rightArm");
	var lArm = model.getPart("leftArm");
	var rLeg = model.getPart("rightLeg");
	var lLeg = model.getPart("leftLeg");

	head.clear();

	body.clear();
	body.setTextureOffset(0, 0);
	body.addBox(-1, 20, -1.5, 2, 4, 3);
	body.setTextureOffset(8, 0);
	body.addBox(-0.5, 20.5, 1.5, 1, 1, 1);

	rArm.clear();

	lArm.clear();

	rLeg.clear();

	lLeg.clear();
}
var grenadeRenderType = Renderer.createHumanoidRenderer();
addGrenadeRenderType(grenadeRenderType);
const GRENADE_RENDER_COLLISION_SIZE_XZ = 1/16 * 4;
const GRENADE_RENDER_COLLISION_SIZE_Y = 1/16 * 4;

Entity.setGrenadeRender = function(entity)
{
	Entity.setRenderType(entity, grenadeRenderType.renderType);
	Entity.setCollisionSize(entity, GRENADE_RENDER_COLLISION_SIZE_XZ, GRENADE_RENDER_COLLISION_SIZE_Y);
}


//########################################################################################################################################################
// Sounds installation
//########################################################################################################################################################

var SoundsInstaller = {
	sounds:
	{
		version: 5,
		soundArray: [
			// { fileName: "", file: "" },
			// { fileName: "", fileDirectory: "", file: "" },

			{
				fileName: "44Magnum.mp3"
			},
			{
				fileName: "AA-12Shoot.ogg"
			},
			{
				fileName: "AK47Shoot.ogg"
			},
			{
				fileName: "AK74Shoot.ogg"
			},
			{
				fileName: "AT4_and_M72LAW_and_Panzerfaust3Shoot.ogg"
			},
			{
				fileName: "BarrettShoot.ogg"
			},
			{
				fileName: "bell.mp3"
			},
			{
				fileName: "benboncan_parachute.mp3"
			},
			{
				fileName: "CrossbowShoot.wav"
			},
			{
				fileName: "DesertEagleShoot.ogg"
			},
			{
				fileName: "DragunovShoot.ogg"
			},
			{
				fileName: "EmptyGun.ogg"
			},
			{
				fileName: "explosion-countdown.wav"
			},
			{
				fileName: "fire-explosion.mp3"
			},
			{
				fileName: "flamethrower.mp3"
			},
			{
				fileName: "FNSCAR_and_AUG_and_MTARShoot.ogg"
			},
			{
				fileName: "G36Shoot.ogg"
			},
			{
				fileName: "GlockShoot.ogg"
			},
			{
				fileName: "GrenadeLauncherShoot.ogg"
			},
			{
				fileName: "ignite_flamethrower1.ogg"
			},
			{
				fileName: "ignite_flamethrower2.ogg"
			},
			{
				fileName: "ignite_flamethrower3.ogg"
			},
			{
				fileName: "knife_on_blocks.mp3"
			},
			{
				fileName: "knife_stab1.mp3"
			},
			{
				fileName: "knife_stab2.mp3"
			},
			{
				fileName: "L96Shoot.ogg"
			},
			{
				fileName: "LICENSE.txt"
			},
			{
				fileName: "M9Shoot.ogg"
			},
			{
				fileName: "M14_and_M16A4Shoot.ogg"
			},
			{
				fileName: "M21Shoot.ogg"
			},
			{
				fileName: "M249_and_L86Shoot.ogg"
			},
			{
				fileName: "M1014Shoot.ogg"
			},
			{
				fileName: "M1887Shoot.ogg"
			},
			{
				fileName: "MakarovShoot.ogg"
			},
			{
				fileName: "MinigunCooldown.ogg"
			},
			{
				fileName: "MinigunSpin.ogg"
			},
			{
				fileName: "MinigunWarmup.ogg"
			},
			{
				fileName: "MiniUziShoot.ogg"
			},
			{
				fileName: "MolotovExplosion.mp3"
			},
			{
				fileName: "MP5Shoot.ogg"
			},
			{
				fileName: "MSRShoot.mp3"
			},
			{
				fileName: "MultipleRocketLauncherShoot.mp3"
			},
			{
				fileName: "P90_and_Bizon_and_G3Shoot_and_Minigun.ogg"
			},
			{
				fileName: "R700_and_M40A3Shoot.ogg"
			},
			{
				fileName: "R870Shoot.ogg"
			},
			{
				fileName: "RayGunShoot.mp3"
			},
			{
				fileName: "BARReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "BazookaReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "BrenReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "BrowningReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "ColtReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "CrossbowReload.wav",
				fileDirectory: "reload"
			},
			{
				fileName: "DP28Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "GL6Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "GrenadeLauncherReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "Kar98kReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "LeeEnfieldReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "LugerReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "M1CarbineReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "M1014Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "M1887Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "MG42Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "MP40Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "MP44Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "PPSHReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "RayGunReload.mp3",
				fileDirectory: "reload"
			},
			{
				fileName: "Reload1.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "SpringfieldReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "StenReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "ThompsonReload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "TT33Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "W1200Reload.ogg",
				fileDirectory: "reload"
			},
			{
				fileName: "riot_shield_attack.mp3"
			},
			{
				fileName: "RPD_and_M60E4_and_RPKShoot.ogg"
			},
			{
				fileName: "RPGShoot.ogg"
			},
			{
				fileName: "SG550Shoot.ogg"
			},
			{
				fileName: "SIGP226Shoot.ogg"
			},
			{
				fileName: "smoke-grenade.mp3"
			},
			{
				fileName: "SkorpionShoot.ogg"
			},
			{
				fileName: "USPShoot.ogg"
			},
			{
				fileName: "W1200_and_SPASShoot.ogg"
			}
		]
	},

	progressDialog: null,

	versionFileName: "version.txt",


	checkAtStartup: function()
	{
		ModPE.log(getLogText() + "checkAtStartup(): started check.");

		if(SoundsInstaller.needsInstallation())
		{
			if(DEBUG1)
				print("DesnoGuns Debug: checkAtStartup(): sounds NOT correctly installed!");

			SoundsInstaller.installUI();
		} else
		{
			if(DEBUG1)
				print("DesnoGuns Debug: checkAtStartup(): sounds correctly installed.");
			ModPE.log(getLogText() + "checkAtStartup(): sounds correctly installed.");
		}
	},


	needsInstallation: function()
	{
		if(doesFileExist(sdcard + "/games/com.mojang/desnoguns-sounds/" + SoundsInstaller.versionFileName))
		{
			var versionOfSounds = SoundsInstaller.getInstalledVersion();
			ModPE.log(getLogText() + "needsInstallation(): version file found, version: " + versionOfSounds);

			// check version
			if(versionOfSounds == SoundsInstaller.sounds.version)
			{
				ModPE.log(getLogText() + "needsInstallation(): version of the file matches saved version.");
				return !SoundsInstaller.areSoundsPresent();
			} else
			{
				ModPE.log(getLogText() + "needsInstallation(): version of the file is different than saved version.");
				return true;
			}
		} else
		{
			ModPE.log(getLogText() + "needsInstallation(): version file not found.");
			return true;
		}
	},

	getInstalledVersion: function()
	{
		var versionFile = new java.io.File(sdcard + "/games/com.mojang/desnoguns-sounds/" + SoundsInstaller.versionFileName);
		if(versionFile.exists())
		{
			var loadedVersion = "";
			var streamVersionInput = new java.io.FileInputStream(versionFile);
			var bufferedVersionReader = new java.io.BufferedReader(new java.io.InputStreamReader(streamVersionInput));
			var rowVersion = "";
			while((rowVersion = bufferedVersionReader.readLine()) != null)
			{
				loadedVersion += rowVersion;
			}
			var loadedVersion = loadedVersion.split(" ");
			bufferedVersionReader.close();

			ModPE.log(getLogText() + "getInstalledVersion(): text on the version file: " + loadedVersion);
			return parseInt(loadedVersion);
		} else
		{
			print("Bug found: remember that getInstalledVersion() should be used only when version file exists.");
			return -1;
		}
	},

	areSoundsPresent: function()
	{
		var arrayOfMissingSounds = SoundsInstaller.checkMissingSounds();

		if(arrayOfMissingSounds.length == 0)
		{
			// yeah, all sounds needed have been found
			ModPE.log(getLogText() + "areSoundsPresent(): all sounds present.");
			return true;
		} else
		{
			// not correctly installed :(
			ModPE.log(getLogText() + "areSoundsPresent(): some sounds are missing.");
			ModPE.log(getLogText() + "areSoundsPresent(): missing: " + arrayOfMissingSounds.toString());
			return false;
		}
	},

	checkMissingSounds: function()
	{
		var tmpPath = sdcard + "/games/com.mojang/desnoguns-sounds/";
		var arrayOfErrors = [];
		for(var i in SoundsInstaller.sounds.soundArray)
		{
			if(SoundsInstaller.sounds.soundArray[i].fileDirectory == undefined || SoundsInstaller.sounds.soundArray[i].fileDirectory == null)
			{
				// file is inside the general sound folder
				if(!doesFileExist(tmpPath + SoundsInstaller.sounds.soundArray[i].fileName))
				{
					if(arrayOfErrors.indexOf(SoundsInstaller.sounds.soundArray[i].fileName) == -1)
						arrayOfErrors.push(SoundsInstaller.sounds.soundArray[i].fileName);
				} else
				{
					// file exists, maybe is empty?
					if(isFileEmpty(tmpPath + SoundsInstaller.sounds.soundArray[i].fileName))
					{
						if(arrayOfErrors.indexOf(SoundsInstaller.sounds.soundArray[i].fileName) == -1)
							arrayOfErrors.push(SoundsInstaller.sounds.soundArray[i].fileName);
					}
				}
			} else
			{
				// file is inside another folder
				if(!doesFileExist(tmpPath + SoundsInstaller.sounds.soundArray[i].fileDirectory + "/" + SoundsInstaller.sounds.soundArray[i].fileName))
				{
					if(arrayOfErrors.indexOf(SoundsInstaller.sounds.soundArray[i].fileName) == -1)
						arrayOfErrors.push(SoundsInstaller.sounds.soundArray[i].fileName);
				} else
				{
					// file exists, maybe is empty?
					if(isFileEmpty(tmpPath + SoundsInstaller.sounds.soundArray[i].fileDirectory + "/" + SoundsInstaller.sounds.soundArray[i].fileName))
					{
						if(arrayOfErrors.indexOf(SoundsInstaller.sounds.soundArray[i].fileName) == -1)
							arrayOfErrors.push(SoundsInstaller.sounds.soundArray[i].fileName);
					}
				}
			}
		}

		return arrayOfErrors;
	},


	installUI: function()
	{
		currentActivity.runOnUiThread(new java.lang.Runnable()
		{
			run: function()
			{
				try
				{
					ModPE.log(getLogText() + "installUI(): displayed installation for sounds UI.");

					var layout = new android.widget.LinearLayout(currentActivity);
					layout.setOrientation(android.widget.LinearLayout.VERTICAL);
					var padding = convertDpToPixel(8);
					layout.setPadding(padding, padding, padding, padding);

					var scroll = new android.widget.ScrollView(currentActivity);
					scroll.addView(layout);

					var popup = new android.app.AlertDialog.Builder(currentActivity);
					popup.setView(scroll);
					popup.setTitle("Download sounds?");

					var helpTextInstallation = new android.widget.TextView(currentActivity);
					helpTextInstallation.setText(android.text.Html.fromHtml("<br>Would you like to download and install DesnoGuns Mod sounds now?<br><br>" +
						"A total of approximately 3 MB of data will be saved on your internal storage.<br>" +
						"The installation may take up to one minute.<br><br>" +
						"An active Internet connection is needed to install sounds.<br>"));
					layout.addView(helpTextInstallation);

					popup.setPositiveButton("Install", new android.content.DialogInterface.OnClickListener() {
						onClick: function(viewarg)
						{
							SoundsInstaller.installingUI();
							ModPE.log(getLogText() + "installUI(): Sounds installation started by the user.");
						}
					});

					popup.setNegativeButton("Cancel", new android.content.DialogInterface.OnClickListener() {
						onClick: function(viewarg)
						{
							currentActivity.runOnUiThread(new java.lang.Runnable() {
								run: function() {
									android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: Some features may not work properly without sounds."), android.widget.Toast.LENGTH_LONG).show();
								}
							});
							ModPE.log(getLogText() + "installUI(): Sounds installation canceled.");
						}
					});


					var alertDialog = popup.create();
					alertDialog.setCanceledOnTouchOutside(false);
					alertDialog.show();

				} catch(err)
				{
					print("Error: " + err);
				}
			}
		});
	},

	installingUI: function()
	{
		currentActivity.runOnUiThread(new java.lang.Runnable()
		{
			run: function()
			{
				try
				{
					SoundsInstaller.progressDialog = new android.app.ProgressDialog(currentActivity);
					SoundsInstaller.progressDialog.setTitle("Downloading...");
					SoundsInstaller.progressDialog.setMessage("Downloading sound 1 of " + SoundsInstaller.sounds.soundArray.length);
					SoundsInstaller.progressDialog.setProgressStyle(android.app.ProgressDialog.STYLE_HORIZONTAL);
					SoundsInstaller.progressDialog.setCancelable(false);
					SoundsInstaller.progressDialog.setCanceledOnTouchOutside(false);
					SoundsInstaller.progressDialog.setIndeterminate(false);
					SoundsInstaller.progressDialog.setMax(SoundsInstaller.sounds.soundArray.length);
					SoundsInstaller.progressDialog.setProgress(0);
					SoundsInstaller.progressDialog.show();

					SoundsInstaller.install();
				} catch(err)
				{
					print("Error: " + err);
				}
			}
		});
	},

	install: function()
	{
		new java.lang.Thread(new java.lang.Runnable()
		{
			run: function()
			{
				var githubUrl = "https://raw.githubusercontent.com/Desno365/DesnoGuns-Mod/master/sounds/version" + SoundsInstaller.sounds.version + "/desnoguns-sounds/";
				var tmpPath = sdcard + "/games/com.mojang/desnoguns-sounds/";

				deleteFile(tmpPath); //delete previous files if present

				for(var i in SoundsInstaller.sounds.soundArray)
				{
					// display progress
					currentActivity.runOnUiThread(new java.lang.Runnable()
					{
						run: function()
						{
							var progress = parseInt(i) + 1;
							SoundsInstaller.progressDialog.setMessage("Downloading sound " + (progress) + " of " + SoundsInstaller.sounds.soundArray.length);
							SoundsInstaller.progressDialog.setProgress(progress);

						}
					});

					// save file on the sdcard
					if(SoundsInstaller.sounds.soundArray[i].fileDirectory == undefined || SoundsInstaller.sounds.soundArray[i].fileDirectory == null)
					{
						// file is inside the general sound folder
						//writeFileFromByteArray(android.util.Base64.decode(SoundsInstaller.sounds.soundArray[i].file, 0), tmpPath + SoundsInstaller.sounds.soundArray[i].fileName);
						SoundsInstaller.downloadFile(githubUrl + SoundsInstaller.sounds.soundArray[i].fileName, tmpPath + SoundsInstaller.sounds.soundArray[i].fileName);
					} else
					{
						// file is inside another folder
						//writeFileFromByteArray(android.util.Base64.decode(SoundsInstaller.sounds.soundArray[i].file, 0), tmpPath + SoundsInstaller.sounds.soundArray[i].fileDirectory + "/" + SoundsInstaller.sounds.soundArray[i].fileName);
						SoundsInstaller.downloadFile(githubUrl + SoundsInstaller.sounds.soundArray[i].fileDirectory + "/" + SoundsInstaller.sounds.soundArray[i].fileName, tmpPath + SoundsInstaller.sounds.soundArray[i].fileDirectory + "/" + SoundsInstaller.sounds.soundArray[i].fileName);
					}
				}

				var nomediaFile = new java.io.File(sdcard + "/games/com.mojang/desnoguns-sounds/.nomedia");
				if(!nomediaFile.exists())
					nomediaFile.createNewFile();

				// put file version
				SoundsInstaller.saveFileWithVersion();

				// END INSTALLATION
				SoundsInstaller.onFinishInstallation();
			}
		}).start();
	},


	downloadFile: function(url, savePath)
	{
		try
		{
			// download content
			var url = new java.net.URL(url);
			var connection = url.openConnection();
	 
			// get content
			inputStream = connection.getInputStream();

			// write content
			writeFileFromInputStream(inputStream, savePath);
		} catch(err)
		{
			ModPE.log(getLogText() + "downloadFile(): caught an error: " + err);
		}
	},


	onFinishInstallation: function()
	{
		SoundsInstaller.progressDialog.dismiss();

		ModPE.log(getLogText() + "Finished sounds installation. Re-checking sounds...");

		var notSuccess = SoundsInstaller.needsInstallation();
		if(notSuccess)
		{
			currentActivity.runOnUiThread(new java.lang.Runnable() {
				run: function() {
					android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: An error has happened during installation, so sounds aren't correctly installed, please check your Internet connection and try again."), android.widget.Toast.LENGTH_LONG).show();
				}
			});
			ModPE.log(getLogText() + "Sounds HAVEN'T been correctly installed!");
		} else
		{
			currentActivity.runOnUiThread(new java.lang.Runnable() {
				run: function() {
					android.widget.Toast.makeText(currentActivity, new android.text.Html.fromHtml("<b>DesnoGuns</b>: Congratulations, sounds are now correctly installed. You can start playing with the <i>DesnoGuns Mod</i>."), android.widget.Toast.LENGTH_LONG).show();
				}
			});
			ModPE.log(getLogText() + "Sounds have been correctly installed. Very good.");
		}

		removeSoundsInstaller();
	},

	saveFileWithVersion: function()
	{
		var versionSaveFile = new java.io.File(sdcard + "/games/com.mojang/desnoguns-sounds/" + SoundsInstaller.versionFileName);
		if(versionSaveFile.exists())
			versionSaveFile.delete();
		versionSaveFile.createNewFile();

		var streamOutputVersion = new java.io.FileOutputStream(versionSaveFile);
		var streamWriterVersion = new java.io.OutputStreamWriter(streamOutputVersion);

		streamWriterVersion.append(SoundsInstaller.sounds.version + " Hi! Did you enjoy playing with the DesnoGuns Mod? I hope you did. -Desno365");
		streamWriterVersion.close();
		streamOutputVersion.close();
	}
};

function removeSoundsInstaller()
{
	// this should allow the GC to free the memory that all the sounds were using
	SoundsInstaller = null;
}


//########################################################################################################################################################
// Things to do at startup
//########################################################################################################################################################

// add mod guns
for(var i in defaultGuns)
	addNewGun(defaultGuns[i]);

// check sounds
SoundsInstaller.checkAtStartup();

// create images from base64
new java.lang.Thread(new java.lang.Runnable()
{
	run: function()
	{
		try
		{
			createImages();
		} catch(e)
		{
			print("Error " + e);
		}
	}
}).start();

// load weapons from addons
currentActivity.runOnUiThread(new java.lang.Runnable(
{
	run: function()
	{
		new android.os.Handler().postDelayed(new java.lang.Runnable(
		{
			run: function()
			{
				// remove old sounds of addons
				deleteFile(sdcard + "/games/com.mojang/desnoguns-temp/");

				// load the weapons saved in addons in the memory
				loadWeaponsFromAddons();

				new android.os.Handler().postDelayed(new java.lang.Runnable(
				{
					run: function()
					{
						// add loaded weapons to the game
						addLoadedAddonsInGame();
					}
				}), 250); // this time should be enough for the mod to load all the addons 
			}
		}), 750); // this time should be enough for BlockLuancher to load all the other scripts
	}
}));


//########################################################################################################################################################
// MINECRAFT BUTTON LIBRARY
//########################################################################################################################################################

// Library version: 1.2.5
// Made by Dennis Motta, also known as Desno365
// https://github.com/Desno365/Minecraft-Button-Library

/*
	The MIT License (MIT)

	Copyright (c) 2015 Dennis Motta 

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

var MinecraftButtonLibrary = {};

// Customization
// These are the default values of the library, you can change them to make the buttons look how you want to.
MinecraftButtonLibrary.defaultButtonPadding = 8;
MinecraftButtonLibrary.defaultButtonTextSize = 16;
MinecraftButtonLibrary.defaultButtonTextLineSpacing = 4;
MinecraftButtonLibrary.defaultButtonTextColor = "#FFDDDDDD";
MinecraftButtonLibrary.defaultButtonTextPressedColor = "#FFFBFF97";
MinecraftButtonLibrary.defaultButtonTextShadowColor = "#FF292929";

// Variables
MinecraftButtonLibrary.Resources = {};
MinecraftButtonLibrary.ProcessedResources = {};

MinecraftButtonLibrary.context = com.mojang.minecraftpe.MainActivity.currentMainActivity.get();
MinecraftButtonLibrary.metrics = new android.util.DisplayMetrics();
MinecraftButtonLibrary.context.getWindowManager().getDefaultDisplay().getMetrics(MinecraftButtonLibrary.metrics);
MinecraftButtonLibrary.sdcard = new android.os.Environment.getExternalStorageDirectory();
MinecraftButtonLibrary.LOG_TAG = "Minecraft Button Library ";

MinecraftButtonLibrary.ProcessedResources.font = null;
MinecraftButtonLibrary.ProcessedResources.mcNormalNineDrawable = null;
MinecraftButtonLibrary.ProcessedResources.mcPressedNineDrawable = null;

//########################################################################################################################################################
// LIBRARY
//########################################################################################################################################################

// MinecraftButton(int textSize, bool enableSound, string customTextColor)
// set an argument null if you want to use the default value
function MinecraftButton(textSize, enableSound, customTextColor)
{
	if(textSize == null)
		textSize = MinecraftButtonLibrary.defaultButtonTextSize;
	if(enableSound == null)
		enableSound = true;
	if(customTextColor == null)
		customTextColor = MinecraftButtonLibrary.defaultButtonTextColor;

	var button = new android.widget.Button(MinecraftButtonLibrary.context);
	button.setTextSize(textSize);
	button.setOnTouchListener(new android.view.View.OnTouchListener()
	{
		onTouch: function(v, motionEvent)
		{
			MinecraftButtonLibrary.onTouch(v, motionEvent, enableSound, customTextColor);
			return false;
		}
	});
	if (android.os.Build.VERSION.SDK_INT >= 14)
		button.setAllCaps(false);
	MinecraftButtonLibrary.setButtonBackground(button, MinecraftButtonLibrary.ProcessedResources.mcNormalNineDrawable);
	button.setTag(false); // is pressed?
	button.setSoundEffectsEnabled(false);
	button.setGravity(android.view.Gravity.CENTER);
	button.setTextColor(android.graphics.Color.parseColor(customTextColor));
	button.setPadding(MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding));
	MinecraftButtonLibrary.addMinecraftStyleToTextView(button);

	return button;
}

// ######### BUTTON UTILS functions #########
MinecraftButtonLibrary.addMinecraftStyleToTextView = function(textview)
{
	// works also for subclasses of TextView
	// you must set the text size before calling this function!

	textview.setTypeface(MinecraftButtonLibrary.ProcessedResources.font);
	textview.setPaintFlags(textview.getPaintFlags() | android.graphics.Paint.SUBPIXEL_TEXT_FLAG);
	textview.setLineSpacing(MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonTextLineSpacing), 1);
	if (android.os.Build.VERSION.SDK_INT >= 19) // KitKat
		textview.setShadowLayer(1, Math.round((textview.getLineHeight() - MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonTextLineSpacing)) / 8), Math.round((textview.getLineHeight() - MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonTextLineSpacing)) / 8), android.graphics.Color.parseColor(MinecraftButtonLibrary.defaultButtonTextShadowColor));
	else
		textview.setShadowLayer(0.0001, Math.round((textview.getLineHeight() - MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonTextLineSpacing)) / 8), Math.round((textview.getLineHeight() - MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonTextLineSpacing)) / 8), android.graphics.Color.parseColor(MinecraftButtonLibrary.defaultButtonTextShadowColor));

}

MinecraftButtonLibrary.setButtonBackground = function(button, background)
{
	if (android.os.Build.VERSION.SDK_INT >= 16)
		button.setBackground(background);
	else
		button.setBackgroundDrawable(background);
}

MinecraftButtonLibrary.convertDpToPixel = function(dp)
{
	var density = MinecraftButtonLibrary.metrics.density;

	return (dp * density);
}

MinecraftButtonLibrary.onTouch = function(v, motionEvent, enableSound, customTextColor)
{
	if(enableSound == null)
		enableSound = true;
	if(customTextColor == null)
		customTextColor = MinecraftButtonLibrary.defaultButtonTextColor;
	
	var action = motionEvent.getActionMasked();
	if(action == android.view.MotionEvent.ACTION_DOWN)
	{
		// button pressed
		MinecraftButtonLibrary.changeToPressedState(v);
	}
	if(action == android.view.MotionEvent.ACTION_CANCEL || action == android.view.MotionEvent.ACTION_UP)
	{
		// button released
		MinecraftButtonLibrary.changeToNormalState(v, customTextColor);
		
		var rect = new android.graphics.Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
		if(rect.contains(v.getLeft() + motionEvent.getX(), v.getTop() + motionEvent.getY())) // detect if the event happens inside the view
		{
			// onClick will run soon

			// play sound
			if(enableSound)
				Level.playSoundEnt(Player.getEntity(), "random.click", 100, 0);
		}
	}
	if(action == android.view.MotionEvent.ACTION_MOVE)
	{
		var rect = new android.graphics.Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
		if(rect.contains(v.getLeft() + motionEvent.getX(), v.getTop() + motionEvent.getY())) // detect if the event happens inside the view
		{
			// pointer inside the view
			if(v.getTag() == false)
			{
				// restore pressed state
				v.setTag(true); // is pressed?

				MinecraftButtonLibrary.changeToPressedState(v);
			}
		} else
		{
			// pointer outside the view
			if(v.getTag() == true)
			{
				// restore pressed state
				v.setTag(false); // is pressed?

				MinecraftButtonLibrary.changeToNormalState(v, customTextColor);
			}
		}
	}
}

MinecraftButtonLibrary.changeToNormalState = function(button, customTextColor)
{
	MinecraftButtonLibrary.setButtonBackground(button, MinecraftButtonLibrary.ProcessedResources.mcNormalNineDrawable);
	button.setTextColor(android.graphics.Color.parseColor(customTextColor));
	// reset pressed padding
	button.setPadding(MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding));
}

MinecraftButtonLibrary.changeToPressedState = function(button)
{
	MinecraftButtonLibrary.setButtonBackground(button, MinecraftButtonLibrary.ProcessedResources.mcPressedNineDrawable);
	button.setTextColor(android.graphics.Color.parseColor(MinecraftButtonLibrary.defaultButtonTextPressedColor));
	// make the effect of a pressed button with padding
	button.setPadding(MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding) + MinecraftButtonLibrary.convertDpToPixel(2), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding), MinecraftButtonLibrary.convertDpToPixel(MinecraftButtonLibrary.defaultButtonPadding) - MinecraftButtonLibrary.convertDpToPixel(2));
}
// ######### END - BUTTON UTILS functions #########


// ######### CREATE NINE PATCH functions #########
MinecraftButtonLibrary.createNinePatchDrawables = function()
{
	var mcButtonNormalBitmap = MinecraftButtonLibrary.getMinecraftButtonBitmap();
	var mcButtonPressedBitmap = MinecraftButtonLibrary.getMinecraftButtonPressedBitmap();

	var mcNormalNinePatch = new android.graphics.NinePatch(mcButtonNormalBitmap, mcButtonNormalBitmap.getNinePatchChunk(), null);
	var mcPressedNinePatch = new android.graphics.NinePatch(mcButtonPressedBitmap, mcButtonPressedBitmap.getNinePatchChunk(), null);

	// here is used a deprecated method that doesn't deals with density
	//noinspection deprecation
	MinecraftButtonLibrary.ProcessedResources.mcNormalNineDrawable = new android.graphics.drawable.NinePatchDrawable(mcNormalNinePatch);
	MinecraftButtonLibrary.ProcessedResources.mcNormalNineDrawable.setFilterBitmap(false);
	//noinspection deprecation
	MinecraftButtonLibrary.ProcessedResources.mcPressedNineDrawable = new android.graphics.drawable.NinePatchDrawable(mcPressedNinePatch);
	MinecraftButtonLibrary.ProcessedResources.mcPressedNineDrawable.setFilterBitmap(false);
}

MinecraftButtonLibrary.getMinecraftButtonBitmap = function()
{
	var density = MinecraftButtonLibrary.metrics.density;

	if(density < 1)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", ldpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStateNormalLDPI);
	}
	if(density >= 1 && density < 1.5)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", mdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStateNormalMDPI);
	}
	if(density >= 1.5 && density < 2)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", hdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStateNormalHDPI);
	}
	if(density >= 2 && density < 2.5)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", xhdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXHDPI);
	}
	if(density >= 2.5)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", xxhdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXXHDPI);
	}

	ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Error: " + density + ", xhdpi");
	return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXHDPI);
}

MinecraftButtonLibrary.getMinecraftButtonPressedBitmap = function()
{
	var density = MinecraftButtonLibrary.metrics.density;

	if(density < 1)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", ldpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStatePressedLDPI);
	}
	if(density >= 1 && density < 1.5)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", mdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStatePressedMDPI);
	}
	if(density >= 1.5 && density < 2)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", hdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStatePressedHDPI);
	}
	if(density >= 2 && density < 2.5)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", xhdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXHDPI);
	}
	if(density >= 2.5)
	{
		ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Density: " + density + ", xxhdpi");
		return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXXHDPI);
	}

	ModPE.log(MinecraftButtonLibrary.LOG_TAG + "Error: " + density + ", xhdpi");
	return MinecraftButtonLibrary.decodeImageFromBase64(MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXHDPI);
}

MinecraftButtonLibrary.decodeImageFromBase64 = function(base64String)
{
	var byteArray = android.util.Base64.decode(base64String, 0);
	return android.graphics.BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
}
// ######### END - CREATE NINE PATCH functions #########


// ######### CREATE TYPEFACE functions #########
MinecraftButtonLibrary.createTypeface = function()
{
	MinecraftButtonLibrary.writeFileFromByteArray(android.util.Base64.decode(MinecraftButtonLibrary.Resources.base64Font, 0), MinecraftButtonLibrary.sdcard + "/minecraft.ttf");
	MinecraftButtonLibrary.ProcessedResources.font = android.graphics.Typeface.createFromFile(MinecraftButtonLibrary.sdcard + "/minecraft.ttf");
	MinecraftButtonLibrary.deleteFile(MinecraftButtonLibrary.sdcard + "/minecraft.ttf");
}

MinecraftButtonLibrary.writeFileFromByteArray = function(byteArray, path)
{
	var file = new java.io.File(path);
	if(file.exists())
		file.delete();
	file.createNewFile();
	var stream = new java.io.FileOutputStream(file);
	stream.write(byteArray);
	stream.close();
	byteArray = null;
}
// ######### END - CREATE TYPEFACE functions #########


// ######### UTILS functions #########
MinecraftButtonLibrary.removeResources = function()
{
	MinecraftButtonLibrary.Resources.minecraftButtonStateNormalLDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStateNormalMDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStateNormalHDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXHDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXXHDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStatePressedLDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStatePressedMDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStatePressedHDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXHDPI = null;
	MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXXHDPI = null;

	MinecraftButtonLibrary.Resources.base64Font = null;
}

MinecraftButtonLibrary.deleteFile = function(path)
{
	var file = new java.io.File(path);

	if(file.isDirectory())
	{
		var directoryFiles = file.listFiles();
		for(var i in directoryFiles)
		{
			deleteFile(directoryFiles[i].getAbsolutePath());
		}
		file.delete();
	}

	if(file.isFile())
		file.delete();
}
// ######### END - UTILS functions #########


//########################################################################################################################################################
// RESOURCES IN BASE64
//########################################################################################################################################################

// backgrounds
MinecraftButtonLibrary.Resources.minecraftButtonStateNormalLDPI = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAACAAAAAgAAAAMAAAACMAAAAAAAAAEAAAAHAAAAAgAAAAf/vLGr/7yxqwAAAAH/vLGr/5WGgQAAAAH/cmVnAAAAAf8oJyoMgs1WAAAASUlEQVQY02Pcs3H1fwY8gAmf5NYtexhYGBgYGG5evYZVwfZDh/GbwPD/PwEFjIwEFBByJAPDfwaWbVv2MGw7eIiBgRFuLrIdDADq5BFxvezsVAAAAABJRU5ErkJggg==";
MinecraftButtonLibrary.Resources.minecraftButtonStateNormalMDPI = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAEAAAABAAAAAYAAAAEMAAAAAAAAAIAAAAOAAAABAAAAA7/vLGr/7yxqwAAAAH/vLGr/5WGgQAAAAH/cmVnAAAAAf8oJyqdu2NqAAAAT0lEQVQ4y2Pcs3H1fwYKABMDhYCFXI1bt+yhsgtuXr1Gksbthw4PcBgw/P8/0C5gZKSOCwbeAPLDgIFasbANmqa3HTwEDV2M4MYVDdRxAQBscxGJWBLxyQAAAABJRU5ErkJggg==";
MinecraftButtonLibrary.Resources.minecraftButtonStateNormalHDPI = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAGAAAABgAAAAkAAAAGMAAAAAAAAAMAAAAVAAAABgAAABX/vLGr/7yxqwAAAAH/vLGr/5WGgQAAAAH/cmVnAAAAAf8oJyqPt1DfAAAAV0lEQVRIx2Pcs3H1fwYaAiYGGgOaW8BCC0O3btkzXIPo5tVrVDF0+6HDo6mIAPj/fzSICABGxmEURKMWDFAqYhhWGW0bUu2z7eAhpMyCMxcRk9OGURABAH/ZEZn4HdB3AAAAAElFTkSuQmCC";
MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXHDPI = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAIAAAACAAAAAwAAAAIMAAAAAAAAAQAAAAcAAAACAAAABz/vLGr/7yxqwAAAAH/vLGr/5WGgQAAAAH/cmVnAAAAAf8oJypkuTlTAAAAZUlEQVRYw2Pcs3H1f4YBBEwMAwxGHcBCbwu3btkzGgWDPA3cvHqNphZuP3R4NApGeDnA8P//aBSM9DTAyDgaBaMOGHXASC8HGEbrgsGWBrahtdO3HTyEVnYTLNxJrQxGo2BwOQAAWckRqcYME3kAAAAASUVORK5CYII=";
MinecraftButtonLibrary.Resources.minecraftButtonStateNormalXXHDPI = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAMAAAADAAAABIAAAAMMAAAAAAAAAYAAAAqAAAADAAAACr/vLGr/7yxqwAAAAH/vLGr/5WGgQAAAAH/cmVnAAAAAf8oJypAoV45AAAAg0lEQVRo3u3ZwQmAMAyF4UTcfxFPgtZLL9YZFJwmTtCDINQ0/5sgHzxCQzXPk4njDOI8AABEB4xeBl2XTIUAAAi1he7z+tWgWzmoEAAAvIVaxowKAQDAFmoZVSoEAAAAAAAAAIj7FhIuMgAA+txCqfL3lPZSuYxen1JfnWRUCACAHgEPIUcRyZ0dVsEAAAAASUVORK5CYII=";
MinecraftButtonLibrary.Resources.minecraftButtonStatePressedLDPI = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAACAAAAAgAAAAIAAAADMAAAAAAAAAEAAAAHAAAAAQAAAAb/KCcqAAAAAf9yZWcAAAABAAAAAQAAAAEAAAABAAAAAQAAAAGZYAV4AAAAb0lEQVQY012OQQqDMBRE34Scwp1QsHgHs7CbGnv9UjzNuIjRxuHD/wzzhq/nMBrKIIENAhA5TUQwS0pUiZIFeK8zEVSAQy5Vpxdq3k3HdYbLq2xTRwBht2T9QxJxSRP588I2d9kmPvqO7ftryb+9AxX7IG5YZYu3AAAAAElFTkSuQmCC";
MinecraftButtonLibrary.Resources.minecraftButtonStatePressedMDPI = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAEAAAABAAAAAQAAAAGMAAAAAAAAAIAAAAOAAAAAgAAAAz/KCcqAAAAAf9yZWcAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEDO+tuAAAAkklEQVQ4y62SwQrCMAyGvwyfwpswcOwd7EEvbu71Zfg08ZIgi10ZtLm0CSn5/r+R4ToqAGwPRCy3gqV+mdMNgI7KOPnIKaVsgw/WUH++7s0IZCsxhP5Myfa184CgVf5cyJvTgiDarUYixV/wQrtfUC1rjiRim1pPMNlOz8vDSPTQQ++rJ+gvZwA+77WseSevJvgCGagihHTV1j0AAAAASUVORK5CYII=";
MinecraftButtonLibrary.Resources.minecraftButtonStatePressedHDPI = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAGAAAABgAAAAYAAAAJMAAAAAAAAAMAAAAVAAAAAwAAABL/KCcqAAAAAf9yZWcAAAABAAAAAQAAAAEAAAABAAAAAQAAAAGqizz1AAAAl0lEQVRIx92VwQqAIAyGXfQU3YKg6B3yUJe0Xj+ip1k3+QOHGXhoO+l0Ch/flIZ+ZBMiPjREkIcFSOPE2ymMK1M4il9QIwtnbbIAqbCwZ91mXYgoLoUQ/NQrWavMIiMYQqJHadVUIJK6iAEXZTUaLihrNOY8WyRcBM+7AkQOfh+/L4CLPx+KtQoQdW0TJtdx5tnyIv9/RDcytSKUL7bTXAAAAABJRU5ErkJggg==";
MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXHDPI = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAAIAAAACAAAAAgAAAAMMAAAAAAAAAQAAAAcAAAABAAAABj/KCcqAAAAAf9yZWcAAAABAAAAAQAAAAEAAAABAAAAAQAAAAHs/TEDAAAAqUlEQVRYw+2WwQqAIAyGXfQU3YKg6B3yUJe0Xj+ip1ln/8AhBnrYbroxhe/frzSNM5sg4ktDBHkogDRueLsE68YUDr1Ai5CdtUkNEDkL9fuxKoLqNEDxMRaCv0aR1E8R1OcDRphrEp0gzTgUQQ0akMydQROU9RZggSKo7y1gzptzSRMEf0pFUF4DDv7p/txAE/zrgdhPEZTXwNB3wcZz3XlznphXBMUv8AJ9bSKkEsE9twAAAABJRU5ErkJggg==";
MinecraftButtonLibrary.Resources.minecraftButtonStatePressedXXHDPI = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGG5wT2wAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADYHRhkAAAAVG5wVGMAAgIJIAAAACgAAAAAAAALAAAACwAAAAsAAAARMAAAAAAAAAYAAAAqAAAABgAAACT/KCcqAAAAAf9yZWcAAAABAAAAAQAAAAEAAAABAAAAAQAAAAGV/cSjAAAAzElEQVRo3u2ZQQrCMBBFM9JTuBMEi3cwC93Y6vVFPM14gfzFQIpMfLNMJk0ffD5/WptPZy/Nii0XM9EvDoh2tbHWS3N9V5IXAAD8O8CkbGWptcsFymw8+Jz744qEAABgUBeyWFQJluvw1OVeJAQAAKNmoRLMMBZOQ31CFRICAID8LhQdpVy4UyzbRCcydQAJAQDAqBOZ+7bZJupOJr5+IyEAAMjuQov497Q+b8Kd/Ccvqu5FQgAAkN2Fjod9c+Pzem+bbTr1IyEAAEheX2f8IsSeFAnbAAAAAElFTkSuQmCC";

// font
MinecraftButtonLibrary.Resources.base64Font = "[encoded data, 26400 characters]";

//########################################################################################################################################################
// START CREATION OF RESOURCES
//########################################################################################################################################################

new java.lang.Thread(new java.lang.Runnable()
{
	run: function()
	{
		try
		{
			MinecraftButtonLibrary.createNinePatchDrawables();
			MinecraftButtonLibrary.createTypeface();

			MinecraftButtonLibrary.removeResources();
		} catch(e)
		{
			print("Error " + e);
		}
	}
}).start();

