Raised This Month: $12 Target: $400
 3% 

Blockmaker - Adding Blocks (12 Step) In-Depth | Updated 12/24/2013!


Post New Thread Reply   
 
Thread Tools Display Modes
Erox902
Veteran Member
Join Date: Jun 2009
Location: Never Never Land
Old 09-14-2011 , 14:02   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 7/16/2010!!
Reply With Quote #281

Just check through this thread, don't be a lazy-ass.
There are also many blockmakers here on this forum with xp blocks
Check them aswell.
Erox902 is offline
fcn_yocus
New Member
Join Date: Jan 2012
Old 02-18-2012 , 06:11   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 7/16/2010!!
Reply With Quote #282

Wassup everyone, i did every step 100% when i compile the sma no errors. But when i started the server and try to use the block it shuts down my server
.
The block i added was earthquake , which only shakes the screen. I tried this line below, but all i got was the normal size block.
precache_model(gszBlockModelEarthquake);

And when i try to use small pole or large *instant crash*. Its saying i dont have the files but i know i obviously do, is there another code i can use to precache the model differently? That i understand lol

case NORMAL: entity_set_model(ent, gszBlockModelEarthquake );
case SMALL: entity_set_model(ent, ??? );
case LARGE: entity_set_model(ent, ??? );
case POLE: entity_set_model(ent, ??? );

This also worked for me when i removed the line from above and filled in the ??? with gszBlockModelEarthquake. but then all my blocks became normal size but the server stop crashing.

Im not sure what to write to make the file read corretly. Any takers on why its doing this.
BTW i also tried renaming my file here differently
new const gszBlockModelEarthquake[] = "models/blockmaker/fcn_earthquake.mdl";
new const gszBlockModelEarthquake[] = "models/blockmaker/fcn_earthquake2.mdl"; - But still nothing
fcn_yocus is offline
r14170
Veteran Member
Join Date: Dec 2009
Old 05-05-2012 , 21:06   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 7/16/2010!!
Reply With Quote #283

read the tutorial again.
r14170 is offline
Exiliz
New Member
Join Date: Nov 2012
Old 11-02-2012 , 14:43   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 10/11/2012!!
Reply With Quote #284

I tried editing my IgZ blockmaker by adding a shotgun , and i keep getting errors , my sma doesnt compile :/ if anyone can help pm please ^.^
__________________
Just a New Modder in the New Moded Modding world
Turtles ... Are ... Amazing!
Exiliz is offline
Old 12-29-2012, 14:02
TheBeginner
This message has been deleted by YamiKaitou. Reason: cross-section posting
TheBeginner
Junior Member
Join Date: Jun 2012
Old 01-01-2013 , 05:01   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 10/11/2012!!
Reply With Quote #285

I have same problem, i want add more guns

.SMA CODE (pastebin)

I want add M4A1, Shotgun, glock ---- help???



Sorry my english is very bad because im from estonia and in english I'm not the best
TheBeginner is offline
Old 06-29-2013, 05:18
StepHD
This message has been deleted by StepHD.
Pacukas
New Member
Join Date: May 2012
Old 07-14-2013 , 07:53   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 10/11/2012!!
Reply With Quote #286

052 error multidimensional array must be fully initialized
help me
Attached Files
File Type: sma Get Plugin or Get Source (igz_blockmaker_original.sma - 772 views - 147.1 KB)
Pacukas is offline
Old 11-10-2013, 09:10
sfsv
This message has been deleted by sfsv. Reason: sotty
sfsv
Junior Member
Join Date: Nov 2013
Old 11-10-2013 , 09:13   Re: Blockmaker - Adding Blocks (Step-By-Step) UPDATED 10/11/2012!!
Reply With Quote #287

How i can add grenadE? everytime error



I want add he, frost grenade and i want the block come back faster sorry for my english
Attached Files
File Type: sma Get Plugin or Get Source (blockmaker_v4.01.sma - 733 views - 164.3 KB)
sfsv is offline
GXLZPGX
Veteran Member
Join Date: Sep 2009
Old 12-24-2013 , 15:13   Re: Blockmaker - Adding Blocks (Step-By-Step) In-Depth | Updated 12/24/2013!
Reply With Quote #288

Just updated, more descriptive to explain things that might mislead people.
__________________
Currently accepting payment US DOLLARS ONLY for custom plugins, contact me through PM.
GXLZPGX is offline
staNioN
Junior Member
Join Date: Jul 2013
Old 07-30-2014 , 06:40   Re: Blockmaker - Adding Blocks (12 Step) In-Depth | Updated 12/24/2013!
Reply With Quote #289

well,sorry for bumping this up,but i really need help here
can someone post a code showing how to add bhop block with no underblock feature + a chat message when someone actually tries to underblock/underknife
example: [Prefix] Player#1 tried to Underknife Player#2
would be awesome if someone would do this for me
here is the plugin to add the code in :
Code:
#include <amxmodx>
#include <amxmisc>
#include <engine>
#include <fun>
#include <cstrike>
#include <fakemeta>
#include <colorchat>
#include <dhudmessage>


#pragma semicolon 1;

#define PLUGIN "iGz Blockmaker"
#define VERSION "0.5.0"
#define AUTHOR "Koleos" // wishes he could code somethin like this..
#define BM_ADMIN_LEVEL ADMIN_RCON	//admin access level to use this plugin. ADMIN_BAN = flag 'b' , or change value for ADMIN_MENU = 'u'


new ScoutUsed[42];
new ShotgunUsed[42];
new DEagleUsed[42];
new AwpUsed[42];
new HeUsed[42];
new FlashUsed[42];
new FrostUsed[42];
new Ak47Used[42];
new M4a1Used[42];
new UspUsed[42];
new FamasUsed[42];
new GlockUsed[42];
new P90Used[42];
new GalilUsed[42];


#define HE 0
#define SMOKE 1
#define FLASH 2

new grenade_taken[3][42];
new gKeysMainMenu;
new gKeysBlockMenu;
new gKeysBlockSelectionMenu;
new gKeysTeleportMenu;
new gKeysOptionsMenu;
new gKeysChoiceMenu;

const Float:gfSnapDistance = 10.0;	//blocks snap together when they're within this value + the players snap gap

// enum for menu option values
enum
{
	N1, N2, N3, N4, N5, N6, N7, N8, N9, N0
};

// enum for bit-shifted numbers 1 - 10
enum
{
	B1 = 1 << N1, B2 = 1 << N2, B3 = 1 << N3, B4 = 1 << N4, B5 = 1 << N5,
	B6 = 1 << N6, B7 = 1 << N7, B8 = 1 << N8, B9 = 1 << N9, B0 = 1 << N0,
};

// enum for options with YES/NO confirmation
enum
{
	CHOICE_LOAD,
	CHOICE_DEL_BLOCKS,
	CHOICE_DEL_TELEPORTS
};

// enum for different block sizes
enum
{
	NORMAL,
	SMALL,
	LARGE,
	POLE
};

// enum for axes
enum
{
	X,
	Y,
	Z
};

// block scales
const Float:SCALE_SMALL = 0.25;
const Float:SCALE_NORMAL = 1.0;
const Float:SCALE_LARGE = 2.0;
const Float:SCALE_LJPOLE = 0.125;

// hud message values
const gHudRed = 255;
const gHudGreen = 125;
const gHudBlue = 0;
const Float:gfTextX = -1.0;
const Float:gfTextY = 0.84;
const gHudEffects = 0;
const Float:gfHudFxTime = 0.75;
const Float:gfHudHoldTime = 0.75;
const Float:gfHudFadeInTime = 0.75;
const Float:gfHudFadeOutTime = 0.75;
const gHudChannel = 2;

// Task ID offsets
const TASK_BHOPSOLID = 1000;
const TASK_BHOPSOLIDNOT = 2000;
const TASK_INVINCIBLE = 3000;
const TASK_STEALTH = 4000;
const TASK_ICE = 5000;
const TASK_SPRITE = 6000;
const TASK_CAMOUFLAGE = 7000;
const TASK_HONEY = 8000;
const TASK_FIRE = 9000;
const TASK_BOOTSOFSPEED = 10000;
const TASK_TELEPORT = 11000;
const TASK_NOSLOW = 12000;
const TASK_SUPERMAN = 13000;

// strings
new const gszPrefix[] = "[staNioN-MM]";
new const gszInfoTarget[] = "info_target";
new const gszHelpFilenameFormat[] = "blockmaker_v%s.txt";
new gszFile[128];
new gszNewFile[128];
new gszMainMenu[256];
new gszBlockMenu[256];
new gszTeleportMenu[256];
new gszOptionsMenu[256];
new gszChoiceMenu[128];
new gszHelpTitle[64];
new gszHelpText[1600];
new gszHelpFilename[32];
new gszViewModel[33][32];

// block dimensions
new Float:gfBlockSizeMinForX[3] = {-4.0,-32.0,-32.0};
new Float:gfBlockSizeMaxForX[3] = { 4.0, 32.0, 32.0};
new Float:gfBlockSizeMinForY[3] = {-32.0,-4.0,-32.0};
new Float:gfBlockSizeMaxForY[3] = { 32.0, 4.0, 32.0};
new Float:gfBlockSizeMinForZ[3] = {-32.0,-32.0,-4.0};
new Float:gfBlockSizeMaxForZ[3] = { 32.0, 32.0, 4.0};
new Float:gfDefaultBlockAngles[3] = { 0.0, 0.0, 0.0 };

// pole block dimensions
new Float:gfPoleBlockSizeMinForX[3] = {-32.0,-4.0,-4.0};
new Float:gfPoleBlockSizeMaxForX[3] = { 32.0, 4.0, 4.0};
new Float:gfPoleBlockSizeMinForZ[3] = {-4.0,-4.0,-32.0};
new Float:gfPoleBlockSizeMaxForZ[3] = { 4.0, 4.0, 32.0};
new Float:gfPoleBlockSizeMinForY[3] = {-4.0,-32.0,-4.0};
new Float:gfPoleBlockSizeMaxForY[3] = { 4.0, 32.0, 4.0};

// block models
new const gszBlockModelDefault[] = "models/blockmaker/bm_block_default.mdl";
new const gszBlockModelPlatform[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelBhop[] = "models/blockmaker/igz_bhopV5.mdl";
new const gszBlockModelDamage[] = "models/blockmaker/igz_damage.mdl";
new const gszBlockModelHealer[] = "models/blockmaker/igz_health.mdl";
new const gszBlockModelInvincibility[] = "models/blockmaker/igz_invincibility.mdl";
new const gszBlockModelStealth[] = "models/blockmaker/igz_stealth.mdl";
new const gszBlockModelSpeedBoost[] = "models/blockmaker/igz_speedboost.mdl";
new const gszBlockModelNoFallDamage[] = "models/blockmaker/igz_nofalldamage.mdl";
new const gszBlockModelIce[] = "models/blockmaker/igz_ice.mdl";
new const gszBlockModelDeath[] = "models/blockmaker/igz_death.mdl";
new const gszBlockModelCamouflage[] = "models/blockmaker/igz_camouflage.mdl";
new const gszBlockModelLowGravity[] = "models/blockmaker/bm_block_lowgravity.mdl";
new const gszBlockModelFire[] = "models/blockmaker/igz_fire.mdl";
new const gszBlockModelRandom[] = "models/blockmaker/igz_random.mdl";
new const gszBlockModelSlap[] = "models/blockmaker/igz_slapV2.mdl";
new const gszBlockModelHoney[] = "models/blockmaker/igz_honey.mdl";
new const gszBlockModelBarrierCT[] = "models/blockmaker/igz_ct_barrier.mdl";
new const gszBlockModelBarrierT[] = "models/blockmaker/igz_t_barrier.mdl";
new const gszBlockModelBootsOfSpeed[] = "models/blockmaker/igz_boostofspeedV2.mdl";
new const gszBlockModelGlass[] = "models/NextGen/NextGen_Glass.mdl";
new const gszBlockModelGlass_igz[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelGlass_Normal[] = "models/NextGen/NextGen_Glass.mdl";
new const gszBlockModelBhopNoSlow[] = "models/blockmaker/igz_bhopV5.mdl";
new const gszBlockModelDelayedBhop[] = "models/blockmaker/igz_delay_bhop.mdl";
new const gszBlockModelFade[] = "models/blockmaker/igz_blind.mdl";
new const gszBlockModelDEagle[] = "models/blockmaker/igz_deagle.mdl";
new const gszBlockModelHe[] = "models/blockmaker/igz_grenade.mdl";
new const gszBlockModelSmoke[] = "models/blockmaker/igz_frostblock.mdl";
new const gszBlockModelFlash[] = "models/blockmaker/igz_flashnade.mdl";
new const gszBlockModelAwp[] = "models/blockmaker/igz_gun_awp.mdl";
new const gszBlockModelTrampolineLow[] = "models/blockmaker/igz_trampoline.mdl";
new const gszBlockModelTrampolineMid[] = "models/blockmaker/igz_trampoline.mdl";
new const gszBlockModelTrampolineHigh[] = "models/blockmaker/igz_trampoline.mdl";
new const gszBlockModelLight[] = "models/blockmaker/bm_block_default.mdl";
new const gszBlockModelNoFallDamageBhop[] = "models/blockmaker/bm_block_default.mdl";
new const gszBlockModelDuck[] = "models/blockmaker/igz_duck.mdl";
new const gszBlockModelMoney[] = "models/blockmaker/igz_money.mdl";
new const gszBlockModelSuperman[] = "models/blockmaker/igz_superman.mdl";
new const gszBlockModelXP[] = "models/outrun/XP.mdl";
new const gszBlockModelShotgun[] = "models/NextGen/NextGen_Shotgun.mdl";
new const gszBlockModelScout[] = "models/blockmaker/mokescout.mdl";
new const gszBlockModelAk47[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelM4a1[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelUsp[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelFamas[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelGlock[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelP90[] = "models/blockmaker/igz_platformv2.mdl";
new const gszBlockModelGalil[] = "models/blockmaker/igz_platformv2.mdl";


// block sounds
new const gszFireSoundFlame[] = "ambience/flameburst1.wav";				//from HL
new const gszInvincibleSound[] = "warcraft3/divineshield.wav";				//from WC3 plugin
new const gszCamouflageSound[] = "warcraft3/antend.wav";					//from WC3 plugin
new const gszStealthSound[] = "warcraft3/levelupcaster.wav";				//from WC3 plugin
new const gszBootsOfSpeedSound[] = "warcraft3/purgetarget1.wav";				//from WC3 plugin

// teleport
new const Float:gfTeleportSizeMin[3] = {-16.0,-16.0,-16.0};
new const Float:gfTeleportSizeMax[3] = { 16.0, 16.0, 16.0};
new const Float:gfTeleportZOffset = 36.0;
new const gTeleportStartFrames = 20;
new const gTeleportEndFrames = 5;
new const gszTeleportSound[] = "warcraft3/blinkarrival.wav";				//from WC3 plugin
new const gszTeleportSpriteStart[] = "sprites/flare6.spr";				//from HL
new const gszTeleportSpriteEnd[] = "sprites/blockmaker/bm_teleport_end.spr";				//custom

// global variables
new gSpriteIdBeam;
new gSpriteIdFire;
new gMsgScreenFade;
new gBlockSize[33];
new gMenuBeforeOptions[33];
new gChoiceOption[33];
new gBlockMenuPage[33];
new gTeleportStart[33];
new gGrabbed[33];
new gGroupedBlocks[33][256];
new gGroupCount[33];

// global booleans
new bool:gbSnapping[33];
new bool:gbNoFallDamage[33];
new bool:gbNoFallDamageBhop[33];
new bool:gbOnIce[33];
new bool:gbNoSlowDown[33];
new bool:gbLowGravity[33];
new bool:gbOnFire[33];
new bool:gbJustDeleted[33];
new bool:gbAdminGodmode[33];
new bool:gbAdminNoclip[33];

// global floats
new Float:gfSnappingGap[33];
new Float:gfOldMaxSpeed[33];
new Float:gfGrablength[33];
new Float:gfNextHealTime[33];
new Float:gfNextDamageTime[33];
new Float:gfInvincibleNextUse[33];
new Float:gfInvincibleTimeOut[33];
new Float:gfStealthNextUse[33];
new Float:gfStealthTimeOut[33];
new Float:gfTrampolineTimeout[33];
new Float:gfSpeedBoostTimeOut[33];
new Float:gfTrampolineMidTimeout[42];
new Float:gfTrampolineLowTimeout[42];
new Float:gfCamouflageNextUse[33];
new Float:gfCamouflageTimeOut[33];
new Float:gfRandomNextUse[33];
new Float:gfBootsOfSpeedTimeOut[33];
new Float:gfBootsOfSpeedNextUse[33];
new Float:gfMoneyNextUse[33];
new Float:gfSupermanTimeOut[33];
new Float:gfSupermanNextUse[33];
new Float:gfXPNextUse[33];

// global vectors
new Float:gvGrabOffset[33][3];

// global strings
new gszCamouflageOldModel[33][32];

// block & teleport types
const gBlockMax = 48;
new gSelectedBlockType[gBlockMax];
new gRender[gBlockMax];
new gRed[gBlockMax];
new gGreen[gBlockMax];
new gBlue[gBlockMax];
new gAlpha[gBlockMax];

new const gszBlockClassname[] = "bm_block";
new const gszSpriteClassname[] = "bm_sprite";
new const gszTeleportStartClassname[] = "bm_teleport_start";
new const gszTeleportEndClassname[] = "bm_teleport_end";

enum
{
	TELEPORT_START,
	TELEPORT_END
};

enum
{
	BM_PLATFORM,		//A
	BM_BHOP,		//B
	BM_DAMAGE,		//C
	BM_HEALER,		//D
	BM_NOFALLDAMAGE,	//I
	BM_ICE,			//J
	BM_SPEEDBOOST,		//H
	BM_INVINCIBILITY,	//E
	BM_STEALTH,		//F
	BM_DEATH,		//K
	BM_CAMOUFLAGE,		//M
	BM_LOWGRAVITY,		//N
	BM_FIRE,		//O
	BM_SLAP,		//P
	BM_RANDOM,		//Q
	BM_HONEY,		//R
	BM_BARRIER_CT,		//S
	BM_BARRIER_T,		//T
	BM_BOOTSOFSPEED,	//U
	BM_GLASS,		//V
	BM_GLASS_IGZ,		//X
	BM_GLASS_NORMAL,		// +
	BM_BHOP_NOSLOW,		//W
	BM_DELAYEDBHOP,		//$
	BM_FADE,		// <
	BM_DEAGLE,  		// #
	BM_HE,			// Y
	BM_SMOKE,		// Z
	BM_FLASH,		// !
	BM_AWP,			// @
	BM_TRAMPOLINE_LOW,	// =
	BM_TRAMPOLINE_MID,	// G
	BM_TRAMPOLINE_HIGH,	// )
	BM_LIGHT,		// (
	BM_NOFALLDAMAGEBHOP,	// 4
	BM_DUCK,		// 9
	BM_MONEY,		// 7
	BM_SUPERMAN,		//8
	BM_XP,			// &
	BM_SHOTGUN,		// 2
	BM_SCOUT,		// 3
	BM_AK47,	// >
	BM_M4A1,   // ]
	BM_USP,   // [
	BM_FAMAS ,  // .
	BM_GLOCK,   // _
	BM_P90,   // ?
	BM_GALIL   // |
	
}

enum
{
	NORMAL,
	GLOWSHELL,
	TRANSCOLOR,
	TRANSALPHA,
	TRANSWHITE
};

new const gszBlockNames[gBlockMax][47] =
{
	"Platform",
	"Bunnyhop",
	"Damage",
	"Healer",
	"No Fall Damage",
	"Ice",
	"Speed Boost",
	"Invincibility",
	"Stealth",
	"Death",
	"Camouflage",
	"Low Gravity",
	"Fire",
	"Slap",
	"Random",
	"Honey",
	"CT Barrier",
	"T Barrier",
	"Boots Of Speed",
	"Glass",
	"Glass_igz",
	"Glass_Normal",
	"Bunnyhop [No slow down]",
	"Delayed Bhop",
	"Blind [Trap]",
	"Deagle [Block]",
	"He Grenade",
	"Frost Grenade",
	"Flash Grenade",
	"Awp [Block]",
	"Trampoline [Low]",
	"Trampoline",
	"Trampoline [High]",
	"Light",
	"No Fall Damage [Bhop]",
	"Duck",
	"Money",
	"Superman",
	"XP [Block]",
	"Shotgun [Block]",
	"Scout [Block]",
	"Ak47 [Block]",
	"M4a1 [Block]",
	"USP [Block]",
	"Famas [Block]",
	"Glock [Block]",
	"P90 [Block]",
	"Galil [Block]"
	
};

// save IDs
new const gBlockSaveIds[gBlockMax] =
{
	
'A', 'B', 'C', 'D', 'I', 'J', 'H', 'E', 'F', 'K', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', '+', '$', '<', '#', 'Y', 'Z', '!', '@', '=', 'G', ')', '(', '4', '9', '7', '8', '&', '2', '3', '>', ']', '[', '.', '_', '?', '|'

};

const gTeleportSaveId = '*';

//global array of strings to store the paths and filenames to the block models
new gszBlockModels[gBlockMax][256];

//array of blocks that the random block can be
const gRandomBlocksMax = 17;

new const gRandomBlocks[gRandomBlocksMax] =
{
	BM_INVINCIBILITY,
	BM_STEALTH,
	BM_DEATH,
	BM_CAMOUFLAGE,
	BM_SLAP,
	BM_BOOTSOFSPEED,
	BM_DEAGLE,
	BM_SHOTGUN,
	BM_SCOUT,
	BM_AWP,
	BM_AK47,
	BM_M4A1,
	BM_USP,
	BM_FAMAS,
	BM_GLOCK,
	BM_P90,
	BM_GALIL
	
};

//max speed for player when they have the boots of speed
const Float:gfBootsMaxSpeed = 400.0;

//how many pages for the block selection menu
new gBlockMenuPagesMax;

/***** PLUGIN START *****/
public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR);
	register_cvar(PLUGIN, VERSION, FCVAR_SERVER, 0.0);
	
	//register client commands
	register_clcmd("say /bm", "showMainMenu", ADMIN_RCON);
	register_clcmd("+bmgrab", "cmdGrab", BM_ADMIN_LEVEL, "bind a key to +bmgrab");
	register_clcmd("-bmgrab", "cmdRelease", BM_ADMIN_LEVEL);
	
	//register forwards
	register_forward(FM_EmitSound, "forward_EmitSound");
	
	//create the menus
	createMenus();
	
	//register menus
	register_menucmd(register_menuid("bmMainMenu"), gKeysMainMenu, "handleMainMenu");
	register_menucmd(register_menuid("bmBlockMenu"), gKeysBlockMenu, "handleBlockMenu");
	register_menucmd(register_menuid("bmBlockSelectionMenu"), gKeysBlockSelectionMenu, "handleBlockSelectionMenu");
	register_menucmd(register_menuid("bmTeleportMenu"), gKeysTeleportMenu, "handleTeleportMenu");
	register_menucmd(register_menuid("bmOptionsMenu"), gKeysOptionsMenu, "handleOptionsMenu");
	register_menucmd(register_menuid("bmChoiceMenu"), gKeysChoiceMenu, "handleChoiceMenu");
	
	//register CVARs
	register_cvar("bm_telefrags", "0");			//players near teleport exit die if someone comes through
	register_cvar("bm_firedamageamount", "20.0");		//damage you take per half-second on the fire block
	register_cvar("bm_damageamount", "5.0");		//damage you take per half-second on the damage block
	register_cvar("bm_healamount", "1.0");			//how much hp per half-second you get on the healing block
	register_cvar("bm_invincibletime", "20.0");		//how long a player is invincible
	register_cvar("bm_invinciblecooldown", "60.0");		//time before the invincible block can be used again
	register_cvar("bm_stealthtime", "20.0");		//how long a player is in stealth
	register_cvar("bm_stealthcooldown", "60.0");		//time before the stealth block can be used again
	register_cvar("bm_camouflagetime", "20.0");		//how long a player is in camouflage
	register_cvar("bm_camouflagecooldown", "60.0");		//time before the camouflage block can be used again
	register_cvar("bm_randomcooldown", "30.0");		//time before the random block can be used again
	register_cvar("bm_bootsofspeedtime", "20.0");		//how long the player has boots of speed
	register_cvar("bm_bootsofspeedcooldown", "60.0");	//time before boots of speed can be used again
	register_cvar("bm_teleportsound", "1");			//teleporters make sound
	register_cvar("bm_moneycooldown", "9999.0"); // how long until you can use money again
	register_cvar("bm_supermantime", "15.0");
	register_cvar("bm_supermancooldown", "60.0");
	register_cvar("bm_xpcooldown", "999.0");
	
	//register events
	register_event("DeathMsg", "eventPlayerDeath", "a");
	register_event("TextMsg", "eventRoundRestart", "a", "2&#Game_C", "2&#Game_w");
	register_event("ResetHUD", "eventPlayerSpawn", "b");
	register_event("CurWeapon", "eventCurWeapon", "be");
	register_logevent("eventRoundRestart", 2, "1=Round_Start");
	
	//make save folder in basedir (new saving/loading method)
	new szDir[64];
	new szMap[32];
	get_basedir(szDir, 64);
	add(szDir, 64, "/blockmaker");
	
	//make config folder if it doesn't already exist
	if (!dir_exists(szDir))
	{
		mkdir(szDir);
	}
	
	get_mapname(szMap, 32);
	formatex(gszNewFile, 96, "%s/%s.bm", szDir, szMap);
}
 
public plugin_precache()
{
	//set block models to defaults
	gszBlockModels[BM_PLATFORM] = gszBlockModelPlatform;
	gszBlockModels[BM_BHOP] = gszBlockModelBhop;
	gszBlockModels[BM_DAMAGE] = gszBlockModelDamage;
	gszBlockModels[BM_HEALER] = gszBlockModelHealer;
	gszBlockModels[BM_NOFALLDAMAGE] = gszBlockModelNoFallDamage;
	gszBlockModels[BM_ICE] = gszBlockModelIce;
	gszBlockModels[BM_SPEEDBOOST] = gszBlockModelSpeedBoost;
	gszBlockModels[BM_INVINCIBILITY] = gszBlockModelInvincibility;
	gszBlockModels[BM_STEALTH] = gszBlockModelStealth;
	gszBlockModels[BM_DEATH] = gszBlockModelDeath;
	gszBlockModels[BM_CAMOUFLAGE] = gszBlockModelCamouflage;
	gszBlockModels[BM_LOWGRAVITY] = gszBlockModelLowGravity;
	gszBlockModels[BM_FIRE] = gszBlockModelFire;
	gszBlockModels[BM_SLAP] = gszBlockModelSlap;
	gszBlockModels[BM_RANDOM] = gszBlockModelRandom;
	gszBlockModels[BM_HONEY] = gszBlockModelHoney;
	gszBlockModels[BM_BARRIER_CT] = gszBlockModelBarrierCT;
	gszBlockModels[BM_BARRIER_T] = gszBlockModelBarrierT;
	gszBlockModels[BM_BOOTSOFSPEED] = gszBlockModelBootsOfSpeed;
	gszBlockModels[BM_GLASS] = gszBlockModelGlass;
	gszBlockModels[BM_GLASS_IGZ] = gszBlockModelGlass_igz;
	gszBlockModels[BM_GLASS_NORMAL] = gszBlockModelGlass_Normal;
	gszBlockModels[BM_BHOP_NOSLOW] = gszBlockModelBhopNoSlow;
	gszBlockModels[BM_DELAYEDBHOP] = gszBlockModelDelayedBhop;
	gszBlockModels[BM_FADE] = gszBlockModelFade;
	gszBlockModels[BM_DEAGLE] = gszBlockModelDEagle;
	gszBlockModels[BM_HE] = gszBlockModelHe;
	gszBlockModels[BM_SMOKE] = gszBlockModelSmoke;
	gszBlockModels[BM_FLASH] = gszBlockModelFlash;
	gszBlockModels[BM_AWP] = gszBlockModelAwp;
	gszBlockModels[BM_TRAMPOLINE_LOW] = gszBlockModelTrampolineLow;
	gszBlockModels[BM_TRAMPOLINE_MID] = gszBlockModelTrampolineMid;
	gszBlockModels[BM_TRAMPOLINE_HIGH] = gszBlockModelTrampolineHigh;
	gszBlockModels[BM_LIGHT] = gszBlockModelLight;
	gszBlockModels[BM_NOFALLDAMAGEBHOP] = gszBlockModelNoFallDamageBhop;
	gszBlockModels[BM_DUCK] = gszBlockModelDuck;
	gszBlockModels[BM_MONEY] = gszBlockModelMoney;
	gszBlockModels[BM_SUPERMAN] = gszBlockModelSuperman;
	gszBlockModels[BM_XP] = gszBlockModelXP;
	gszBlockModels[BM_SHOTGUN] = gszBlockModelShotgun;
	gszBlockModels[BM_SCOUT] = gszBlockModelScout;
	gszBlockModels[BM_AK47] = gszBlockModelAk47;
	gszBlockModels[BM_M4A1] = gszBlockModelM4a1;
	gszBlockModels[BM_USP] = gszBlockModelUsp;
	gszBlockModels[BM_FAMAS] = gszBlockModelFamas;
	gszBlockModels[BM_GLOCK] = gszBlockModelGlock;
	gszBlockModels[BM_P90] = gszBlockModelP90;
	gszBlockModels[BM_GALIL] = gszBlockModelGalil;
	
	//setup default block rendering (unlisted block use normal rendering)
	setupBlockRendering(BM_INVINCIBILITY, GLOWSHELL, 55, 55, 55, 16);
	setupBlockRendering(BM_STEALTH, TRANSWHITE, 255, 255, 255, 50);
	setupBlockRendering(BM_GLASS, TRANSWHITE, 255, 255, 255, 100);
	setupBlockRendering(BM_GLASS_IGZ, TRANSWHITE, 255, 255, 255, 100);
	setupBlockRendering(BM_GLASS_NORMAL, TRANSALPHA, 255, 255, 255, 175);
	
	//process block models config file
	processBlockModels();
	
	new szBlockModelSmall[256];
	new szBlockModelLarge[256];
	new szBlockModelPole[256];
	
	//precache blocks
	for (new i = 0; i < gBlockMax; ++i)
	{
		//get filenames for the small and large blocks based on normal block name
		setBlockModelNameSmall(szBlockModelSmall, gszBlockModels[i], 256);
		setBlockModelNameLarge(szBlockModelLarge, gszBlockModels[i], 256);
		setBlockModelNamePole(szBlockModelPole, gszBlockModels[i], 256);
		
		precache_model(gszBlockModels[i]);
		precache_model(szBlockModelSmall);
		precache_model(szBlockModelLarge);
		precache_model(szBlockModelPole);
	}
	
	//precache sounds
	precache_sound(gszTeleportSound);
	precache_sound(gszInvincibleSound);
	precache_sound(gszCamouflageSound);
	precache_sound(gszStealthSound);
	precache_sound(gszFireSoundFlame);
	precache_sound(gszBootsOfSpeedSound);
	precache_model(gszTeleportSpriteStart);
	precache_model(gszTeleportSpriteEnd);
}

public plugin_cfg()
{
	//format help text filename
	format(gszHelpFilename, 32, gszHelpFilenameFormat, VERSION);
	
	//create help title
	format(gszHelpTitle, sizeof(gszHelpTitle), "%s v%s by %s", PLUGIN, VERSION, AUTHOR);
	
	//read in help text from file
	new szConfigsDir[32];
	new szHelpFilename[64];
	new szLine[128];
	get_configsdir(szConfigsDir, 32);
	format(szHelpFilename, sizeof(szHelpFilename), "%s/%s", szConfigsDir, gszHelpFilename);
	
	//open help file for reading
	new f = fopen(szHelpFilename, "rt");
	
	//iterate through all the lines in the file
	new size = sizeof(gszHelpText);
	while (!feof(f))
	{
		fgets(f, szLine, 128);
		
		add(gszHelpText, size, szLine);
	}
	
	//close file
	fclose(f);
	
	//get id for message 'ScreenFade'
	gMsgScreenFade = get_user_msgid("ScreenFade");
	
	//load blocks from file
	loadBlocks(0);
}

createMenus()
{
	//calculate maximum number of block menu pages from maximum amount of blocks
	gBlockMenuPagesMax = floatround((float(gBlockMax) / 8.0), floatround_ceil);
	
	//create main menu
	new size = sizeof(gszMainMenu);
	add(gszMainMenu, size, "\y[staNioN-MM] BlockMaker MainMenu^n^n");
	add(gszMainMenu, size, "\r1. \wBlock Menu^n");
	add(gszMainMenu, size, "\r2. \wTeleport Menu^n^n^n^n");
	add(gszMainMenu, size, "\r6. %s\wNoclip: %s^n");
	add(gszMainMenu, size, "\r7. %s\wGodmode: %s^n^n");
	add(gszMainMenu, size, "\r9. \wOptions Menu^n");
	add(gszMainMenu, size, "\r0. \wClose");
	gKeysMainMenu = B1 | B2 | B6 | B7 | B9 | B0;
	
	//create block menu
	size = sizeof(gszBlockMenu);
	add(gszBlockMenu, size, "\y[staNioN-MM] BlockMenu^n^n");
	add(gszBlockMenu, size, "\r1. \wBlock Type: \y%s^n");
	add(gszBlockMenu, size, "\r2. %s\wCreate Block^n");
	add(gszBlockMenu, size, "\r3. %s\wConvert Block^n");
	add(gszBlockMenu, size, "\r4. %s\wDelete Block^n");
	add(gszBlockMenu, size, "\r5. %s\wRotate Block^n^n");
	add(gszBlockMenu, size, "\r6. %s\wNoclip: %s^n");
	add(gszBlockMenu, size, "\r7. %s\wGodmode: %s^n");
	add(gszBlockMenu, size, "\r8. \wBlock Size: \y%s^n^n");
	add(gszBlockMenu, size, "\r9. \wOptions Menu^n");
	add(gszBlockMenu, size, "\r0. \yBack");
	gKeysBlockMenu = B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | B0;
	gKeysBlockSelectionMenu = B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | B0;
	
	//create teleport menu
	size = sizeof(gszTeleportMenu);
	add(gszTeleportMenu, size, "\y[staNioN-MM] Teleport Menu^n^n");
	add(gszTeleportMenu, size, "\r1. %s\wTeleport Start^n");
	add(gszTeleportMenu, size, "\r2. %s\wTeleport Destination^n");
	add(gszTeleportMenu, size, "\r3. %s\wSwap Teleport Start/Destination^n");
	add(gszTeleportMenu, size, "\r4. %s\wDelete Teleport^n");
	add(gszTeleportMenu, size, "\r5. %s\wTeleport Path^n^n");
	add(gszTeleportMenu, size, "\r6. %s\wNoclip: %s^n");
	add(gszTeleportMenu, size, "\r7. %s\wGodmode: %s^n^n^n");
	add(gszTeleportMenu, size, "\r9. \wOptions^n");
	add(gszTeleportMenu, size, "\r0. \yBack");
	gKeysTeleportMenu = B1 | B2 | B3 | B4 | B5 | B6 | B7 | B9 | B0;
		
	//create the options menu
	size = sizeof(gszOptionsMenu);
	add(gszOptionsMenu, size, "\y[staNioN-MM] Options Menu^n^n");
	add(gszOptionsMenu, size, "\r1. %s\wSnapping: %s^n");
	add(gszOptionsMenu, size, "\r2. %s\wSnapping gap: \r%.1f^n^n");
	add(gszOptionsMenu, size, "\r3. %s\wAdd to group^n");
	add(gszOptionsMenu, size, "\r4. %s\wClear group^n^n");
	add(gszOptionsMenu, size, "\r5. %s\wDelete all blocks^n");
	add(gszOptionsMenu, size, "\r6. %s\wDelete all teleports^n^n");
	add(gszOptionsMenu, size, "\r7. %s\wSave to file^n");
	add(gszOptionsMenu, size, "\r8. %s\wLoad from file^n^n");
	add(gszOptionsMenu, size, "\r9. \wHelp^n");
	add(gszOptionsMenu, size, "\r0. \yBack");
	gKeysOptionsMenu = B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | B0;
	
	//create choice (YES/NO) menu
	size = sizeof(gszChoiceMenu);
	add(gszChoiceMenu, size, "\w%s^n^n");
	add(gszChoiceMenu, size, "\r1. \wYes^n");
	add(gszChoiceMenu, size, "\r2. \wNo^n^n^n^n^n^n^n^n^n^n");
	add(gszChoiceMenu, size, "\r0. \yBack");
	gKeysChoiceMenu = B1 | B2 | B0;
}

setupBlockRendering(blockType, renderType, red, green, blue, alpha)
{
	gRender[blockType] = renderType;
	gRed[blockType] = red;
	gGreen[blockType] = green;
	gBlue[blockType] = blue;
	gAlpha[blockType] = alpha;
}

setBlockModelNameLarge(szBlockModelTarget[256], szBlockModelSource[256], size)
{
	szBlockModelTarget = szBlockModelSource;
	replace(szBlockModelTarget, size, ".mdl", "_large.mdl");
}

setBlockModelNameSmall(szBlockModelTarget[256], szBlockModelSource[256], size)
{
	szBlockModelTarget = szBlockModelSource;
	replace(szBlockModelTarget, size, ".mdl", "_small.mdl");
}

setBlockModelNamePole(szBlockModelTarget[256], szBlockModelSource[256], size)
{
	szBlockModelTarget = szBlockModelSource;
	replace(szBlockModelTarget, size, ".mdl", "_pole.mdl");
}

processBlockModels()
{     
	//get full path to block models config file
	new szBlockModelsFile[96];
	get_configsdir(szBlockModelsFile, 96);
	add(szBlockModelsFile, 96, "/blockmaker_models.ini");
	
	//open block models config file for reading
	new f = fopen(szBlockModelsFile, "rt");
	new szData[160];
	new szType[32];
	new szBlockModel[256];
	new szRender[16];
	new szRed[8];
	new szGreen[8];
	new szBlue[8];
	new szAlpha[8];
	new blockType;
	new render;
	new red;
	new green;
	new blue;
	new alpha;
	
	//iterate through all the lines in the file
	while (!feof(f))
	{
		//clear data
		szBlockModel = "";
		szRender = "";
		szRed = "";
		szGreen = "";
		szBlue = "";
		szAlpha = "";
		blockType = -1;
		
		//get and parse a line of data from file
		fgets(f, szData, 160);
		parse(szData, szType, 24, szBlockModel, 64, szRender, 16, szRed, 8, szGreen, 8, szBlue, 8, szAlpha, 8);
		
		//replace '\' with '/' in block model path
		replace_all(szBlockModel, 64, "\", "/");
		
		if (equal(szType, "PLATFORM")) blockType = BM_PLATFORM;
		else if (equal(szType, "BHOP")) blockType = BM_BHOP;
		else if (equal(szType, "DAMAGE")) blockType = BM_DAMAGE;
		else if (equal(szType, "HEALER")) blockType = BM_HEALER;
		else if (equal(szType, "NOFALLDAMAGE")) blockType = BM_NOFALLDAMAGE;
		else if (equal(szType, "ICE")) blockType = BM_ICE;
		else if (equal(szType, "SPEEDBOOST")) blockType = BM_SPEEDBOOST;
		else if (equal(szType, "INVINCIBILITY")) blockType = BM_INVINCIBILITY;
		else if (equal(szType, "STEALTH")) blockType = BM_STEALTH;
		else if (equal(szType, "DEATH")) blockType = BM_DEATH;
		else if (equal(szType, "CAMOUFLAGE")) blockType = BM_CAMOUFLAGE;
		else if (equal(szType, "LOWGRAVITY")) blockType = BM_LOWGRAVITY;
		else if (equal(szType, "FIRE")) blockType = BM_FIRE;
		else if (equal(szType, "SLAP")) blockType = BM_SLAP;
		else if (equal(szType, "RANDOM")) blockType = BM_RANDOM;
		else if (equal(szType, "HONEY")) blockType = BM_HONEY;
		else if (equal(szType, "BARRIER_CT")) blockType = BM_BARRIER_CT;
		else if (equal(szType, "BARRIER_T")) blockType = BM_BARRIER_T;
		else if (equal(szType, "BOOTSOFSPEED")) blockType = BM_BOOTSOFSPEED;
		else if (equal(szType, "GLASS")) blockType = BM_GLASS;
		else if (equal(szType, "GLASS_igz")) blockType = BM_GLASS_IGZ;
		else if (equal(szType, "GLASS_Normal")) blockType = BM_GLASS_NORMAL;
		else if (equal(szType, "BHOP_NOSLOW")) blockType = BM_BHOP_NOSLOW;
		else if (equal(szType, "DELAYED_BHOP")) blockType = BM_DELAYEDBHOP;
		else if (equal(szType, "FADE")) blockType = BM_FADE;
		else if (equal(szType, "DEAGLE")) blockType = BM_DEAGLE;
		else if (equal(szType, "HE")) blockType = BM_HE;
		else if (equal(szType, "SMOKE")) blockType = BM_SMOKE;
		else if (equal(szType, "FLASH")) blockType = BM_FLASH;
		else if (equal(szType, "AWP")) blockType = BM_AWP;
		else if (equal(szType, "TRAMPOLINE_LOW")) blockType = BM_TRAMPOLINE_LOW;
		else if (equal(szType, "TRAMPOLINE_MID")) blockType = BM_TRAMPOLINE_MID;
		else if (equal(szType, "TRAMPOLINE_HIGH")) blockType = BM_TRAMPOLINE_HIGH;
		else if (equal(szType, "LIGHT")) blockType = BM_LIGHT;
		else if (equal(szType, "NOFALLDAMAGEBHOP")) blockType = BM_NOFALLDAMAGEBHOP;
		else if (equal(szType, "DUCK")) blockType = BM_DUCK;
		else if (equal(szType, "MONEY")) blockType = BM_MONEY;
		else if (equal(szType, "SUPERMAN")) blockType = BM_SUPERMAN;
		else if (equal(szType, "XP")) blockType = BM_XP;
		else if (equal(szType, "Shotgun")) blockType = BM_SHOTGUN;
		else if (equal(szType, "Scout")) blockType = BM_SCOUT;
		else if (equal(szType, "Ak47")) blockType = BM_AK47;
		else if (equal(szType, "M4a1")) blockType = BM_M4A1;
		else if (equal(szType, "Usp")) blockType = BM_USP;
		else if (equal(szType, "Famas")) blockType = BM_FAMAS;
		else if (equal(szType, "Glock")) blockType = BM_GLOCK;
		else if (equal(szType, "P90")) blockType = BM_P90;
		else if (equal(szType, "Galil")) blockType = BM_GALIL;
		
		//if we're dealing with a valid block type
		if (blockType >= 0 && blockType < gBlockMax)
		{
			new bool:bDoRendering = false;
			
			//if block model file exists
			if (file_exists(szBlockModel))
			{
				//set block models for given block type
				gszBlockModels[blockType] = szBlockModel;
				
				//block model file does exist so process rendering values as well
				bDoRendering = true;
			}
			else
			{
				if (equal(szBlockModel, "DEFAULT"))
				{
					//block is set to use default so process rendering values
					bDoRendering = true;
				}
			}
			
			//process rendering values
			if (bDoRendering)
			{
				render = NORMAL;
				red = 255;
				green = 255;
				blue = 255;
				alpha = 255;
				
				if (equal(szRender, "GLOWSHELL")) render = GLOWSHELL;
				if (equal(szRender, "TRANSCOLOR")) render = TRANSCOLOR;
				if (equal(szRender, "TRANSALPHA")) render = TRANSALPHA;
				if (equal(szRender, "TRANSWHITE")) render = TRANSWHITE;
				
				if (strlen(szRed) > 0) red = str_to_num(szRed);
				if (strlen(szGreen) > 0) green = str_to_num(szGreen);
				if (strlen(szBlue) > 0) blue = str_to_num(szBlue);
				if (strlen(szAlpha) > 0) alpha = str_to_num(szAlpha);
				
				//set blocks rendering values
				setupBlockRendering(blockType, render, red, green, blue, alpha);
			}
		}
	}
	
	//close file
	fclose(f);
}

/***** FORWARDS *****/
public client_connect(id)
{
	//make sure snapping is on by default
	gbSnapping[id] = true;
	
	//players chosen snapping gap defaults to 0.0 units
	gfSnappingGap[id] = 0.0;
	
	//make sure players can die
	gbNoFallDamage[id] = false;
	
		//make sure players can die
	gbNoFallDamageBhop[id] = false;
	
	//players block selection menu is on page 1
	gBlockMenuPage[id] = 1;
	
	//player doesn't have godmode or noclip
	gbAdminGodmode[id] = false;
	gbAdminNoclip[id] = false;
	
	//player doesn't have any blocks grouped
	gGroupCount[id] = 0;
}

public client_disconnect(id)
{
	//clear players group
	groupClear(id);
	
	//if player was grabbing an entity when they disconnected
	if (gGrabbed[id])
	{
		//if entity is valid
		if (is_valid_ent(gGrabbed[id]))
		{
			//set the entity to 'not being grabbed'
			entity_set_int(gGrabbed[id], EV_INT_iuser2, 0);
		}
		
		gGrabbed[id] = 0;
	}
}

public pfn_touch(ent, id)
{
	//if touch event involves a player
	if (id > 0 && id <= 32)
	{
		//if player is alive
		if (is_user_alive(id))
		{
			//if entity involved is a block
			if (isBlock(ent))
			{
				//get the blocktype
				new blockType = entity_get_int(ent, EV_INT_body);
				
				//if blocktype is a bunnyhop block or barrier
				if (blockType == BM_BHOP || blockType == BM_BARRIER_CT || blockType == BM_BARRIER_T || blockType == BM_BHOP_NOSLOW || blockType == BM_DELAYEDBHOP || blockType == BM_NOFALLDAMAGEBHOP)
				{
					//if task does not already exist for bunnyhop block
					if (!task_exists(TASK_BHOPSOLIDNOT + ent) && !task_exists(TASK_BHOPSOLID + ent))
					{
						//get the players team
						new CsTeams:team = cs_get_user_team(id);
						
						//if players team is different to barrier
						if (blockType == BM_BARRIER_CT && team == CS_TEAM_T)
						{
							//make block SOLID_NOT without any delay
							taskSolidNot(TASK_BHOPSOLIDNOT + ent);
						}
						else if (blockType == BM_BARRIER_T && team == CS_TEAM_CT)
						{
							//make block SOLID_NOT without any delay
							taskSolidNot(TASK_BHOPSOLIDNOT + ent);
						}
						else if (blockType == BM_BHOP || blockType == BM_BHOP_NOSLOW || blockType == BM_NOFALLDAMAGEBHOP)
						{
							if(IsUnderAttack(id, ent))
							{
								return;
							}
								
							//set bhop block to be SOLID_NOT after 0.1 seconds
							set_task(0.1, "taskSolidNot", TASK_BHOPSOLIDNOT + ent);
						}
						
						else if (blockType == BM_DELAYEDBHOP)
						{
							if(IsUnderAttack(id, ent))
							{
								return;
							}
							
							set_task(2.0, "taskSolidNot", TASK_BHOPSOLIDNOT + ent);
						}
					}
				}
			}
		}
	}
	
	return;
}

IsUnderAttack(id, iEnt)
{
	static iTr;
	iTr = create_tr2();
	
	static Float:vOrigin[3], Float:vEnd[3];
	
	pev(id, pev_origin, vOrigin);
	
	vEnd[0] = vOrigin[0];
	vEnd[1] = vOrigin[1];
	vEnd[2] = vOrigin[2] + ( 50 );
	
	static iHitEnt;
	engfunc(EngFunc_TraceLine, vOrigin, vEnd, DONT_IGNORE_MONSTERS, id, iTr);
	
	iHitEnt = get_tr2(iTr, TR_pHit);
	
	free_tr2(iTr);
	
	if(iEnt == iHitEnt)
	{
		static szName[32]; get_user_name(id, szName, 31);
		
		ColorChat(id, TEAM_COLOR, "^4%s ^4%s ^3Tried to ^4underblock!", gszPrefix, szName);
		return 1;
	}
	
	return 0;
}

public server_frame()
{
	new ent;
	new Float:vOrigin[3];
	new bool:entNear = false;
	new tele;
	new entinsphere;
	
	//iterate through all players and remove slow down after jumping
	for (new i = 1; i <= 32; ++i)
	{
		if (is_user_alive(i))
		{
			if (gbOnIce[i] || gbNoSlowDown[i])
			{
				entity_set_float(i, EV_FL_fuser2, 0.0);
			}
		}
	}
	
	//find all teleport start entities in map and if a player is close to one, teleport the player
	while ((ent = find_ent_by_class(ent, gszTeleportStartClassname)))
	{
		new Float:vOrigin[3];
		entity_get_vector(ent, EV_VEC_origin, vOrigin);
		
		//teleport players and grenades within a sphere around the teleport start entity
		entinsphere = -1;
		while ((entinsphere = find_ent_in_sphere(entinsphere, vOrigin, 32.0)))
		{
			//get classname of entity
			new szClassname[32];
			entity_get_string(entinsphere, EV_SZ_classname, szClassname, 32);
			
			//if entity is a player
			if (entinsphere > 0 && entinsphere <= 32)
			{
				//only teleport player if they're alive
				if (is_user_alive(entinsphere))
				{
					//teleport the player
					actionTeleport(entinsphere, ent);
				}
			}
			//or if entity is a grenade
			else if (equal(szClassname, "grenade"))
			{
				//get the end of the teleport
				tele = entity_get_int(ent, EV_INT_iuser1);
				
				//if the end of the teleport exists
				if (tele)
				{
					//set the end of the teleport to be not solid
					entity_set_int(tele, EV_INT_solid, SOLID_NOT);	//can't be grabbed or deleted
					
					//teleport the grenade
					actionTeleport(entinsphere, ent);
					
					//set a time in the teleport it will become solid after 2 seconds
					entity_set_float(tele, EV_FL_ltime, halflife_time() + 2.0);
				}
			}
		}
	}
	
	//make teleporters SOLID_NOT when players are near them
	while ((ent = find_ent_by_class(ent, gszTeleportEndClassname)))
	{
		//get the origin of the teleport end entity
		entity_get_vector(ent, EV_VEC_origin, vOrigin);
		
		//compare this origin with all player and grenade origins
		entinsphere = -1;
		while ((entinsphere = find_ent_in_sphere(entinsphere, vOrigin, 64.0)))
		{
			//get classname of entity
			new szClassname[32];
			entity_get_string(entinsphere, EV_SZ_classname, szClassname, 32);
			
			//if entity is a player
			if (entinsphere > 0 && entinsphere <= 32)
			{
				//make sure player is alive
				if (is_user_alive(entinsphere))
				{
					entNear = true;
					
					break;
				}
			}
			//or if entity is a grenade
			else if (equal(szClassname, "grenade"))
			{
				entNear = true;
				
				break;
			}
		}
		
		//set the solid type of the teleport end entity depending on whether or not a player is near
		if (entNear)
		{
			//only do this if it is not being grabbed
			if (entity_get_int(ent, EV_INT_iuser2) == 0)
			{
				entity_set_int(ent, EV_INT_solid, SOLID_NOT);	//can't be grabbed or deleted
			}
		}
		else
		{
			//get time from teleport end entity to check if it can go solid
			new Float:fTime = entity_get_float(ent, EV_FL_ltime);
			
			//only set teleport end entity to solid if its 'solid not' time has elapsed
			if (halflife_time() >= fTime)
			{
				entity_set_int(ent, EV_INT_solid, SOLID_BBOX);	//CAN be grabbed and deleted
			}
		}
	}
	
	//find all block entities
	while ((ent = find_ent_by_class(ent, gszBlockClassname)))
	{
		//get block type
		new blockType = entity_get_int(ent, EV_INT_body);
		
		//if block is a speed boost
		if (blockType == BM_SPEEDBOOST)
		{
			new Float:vOrigin[3];
			new Float:pOrigin[3];
			new Float:dist = 9999.9;
			new Float:playerDist = 9999.9;
			new nearestPlayer = 0;
			
			//get the origin of the speed boost block
			entity_get_vector(ent, EV_VEC_origin, vOrigin);
			
			//compare this origin with all players origins to find the nearest player to the block
			for (new id = 1; id <= 32; ++id)
			{
				//if player is alive
				if (is_user_alive(id))
				{
					//get player origin
					entity_get_vector(id, EV_VEC_origin, pOrigin);
					
					//get the distance from the block to the player
					dist = get_distance_f(vOrigin, pOrigin);
					
					if (dist < playerDist)
					{
						nearestPlayer = id;
						playerDist = dist;
					}
				}
			}
			
			//if we found a player within 100 units of the speed boost block
			if (nearestPlayer > 0 && playerDist < 200.0)
			{
				//get the sprite on top of the speed boost block
				new sprite = entity_get_int(ent, EV_INT_iuser3);
				
				//make sure sprite entity is valid
				if (sprite)
				{
					new Float:vAngles[3];
					
					//get the direction the player is looking
					entity_get_vector(nearestPlayer, EV_VEC_angles, vAngles);
					
					//set the angles of the sprite to be the same as the player
					vAngles[0] = 90.0;	//always make sure sprite is flat to the block
					vAngles[1] += 90.0;	//rotate the sprite by 90 because it doesnt point up (PAT!)
					entity_set_vector(sprite, EV_VEC_angles, vAngles);
				}
			}
		}
	}
}

public client_PreThink(id)
{
	//make sure player is connected
	if (is_user_connected(id))
	{
		//display type of block that player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body, 555);
		
		if (isBlock(ent) && (pev(id, pev_button) & IN_USE && !(pev(id, pev_oldbuttons) & IN_USE) ))
		{
			new blockType = entity_get_int(ent, EV_INT_body);
			new szCreator[32];
			
			pev(ent, pev_targetname, szCreator, 31);
			replace_all(szCreator, 31, "_", " ");
			
			set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
			show_hudmessage(id, "[staNioN-MM]^n Block Type: %s^nCreator: %s", gszBlockNames[blockType], szCreator);
		}
		
		//make sure player is alive
		if (is_user_alive(id))
		{
			//if player has low gravity
			if (gbLowGravity[id])
			{
				//get players flags
				new flags = entity_get_int(id, EV_INT_flags);
				
				//if player has feet on the ground, set gravity to normal
				if (flags & FL_ONGROUND)
				{
					set_user_gravity(id);
					
					gbLowGravity[id] = false;
				}
			}
			
			//trace directly down to see if there is a block beneath player
			new Float:pOrigin[3];
			new Float:pSize[3];
			new Float:pMaxs[3];
			new Float:vTrace[3];
			new Float:vReturn[3];
			entity_get_vector(id, EV_VEC_origin, pOrigin);
			entity_get_vector(id, EV_VEC_size, pSize);
			entity_get_vector(id, EV_VEC_maxs, pMaxs);
			
			//calculate position of players feet
			pOrigin[2] = pOrigin[2] - ((pSize[2] - 36.0) - (pMaxs[2] - 36.0));
			
			//make the trace small for some blocks
			vTrace[2] = pOrigin[2] - 1.0;
			
			//do 4 traces for each corner of the player
			for (new i = 0; i < 4; ++i)
			{
				switch (i)
				{
					case 0: { vTrace[0] = pOrigin[0] - 16; vTrace[1] = pOrigin[1] + 16; }
					case 1: { vTrace[0] = pOrigin[0] + 16; vTrace[1] = pOrigin[1] + 16; }
					case 2: { vTrace[0] = pOrigin[0] + 16; vTrace[1] = pOrigin[1] - 16; }
					case 3: { vTrace[0] = pOrigin[0] - 16; vTrace[1] = pOrigin[1] - 16; }
				}
				
				ent = trace_line(id, pOrigin, vTrace, vReturn);
				
				//if entity found is a block
				if (isBlock(ent))
				{
					new blockType = entity_get_int(ent, EV_INT_body);
					
					switch (blockType)
					{
						case BM_HEALER: actionHeal(id);
						case BM_DAMAGE: actionDamage(id);
						case BM_INVINCIBILITY: actionInvincible(id, false);
						case BM_STEALTH: actionStealth(id, false);
						case BM_SPEEDBOOST: actionSpeedBoost(id);
						case BM_DEATH: actionDeath(id);
						case BM_LOWGRAVITY: actionLowGravity(id);
						case BM_CAMOUFLAGE: actionCamouflage(id, false);
						case BM_FIRE: actionFire(id, ent);
						case BM_SLAP: actionSlap(id);
						case BM_RANDOM: actionRandom(id, ent);
						case BM_HONEY: actionHoney(id);
						case BM_BOOTSOFSPEED: actionBootsOfSpeed(id, false);
						case BM_FADE: actionFade(id);
						case BM_DEAGLE: actionDEagle(id);
						case BM_HE: actionGrenade(id, HE);
						case BM_SMOKE: actionGrenade(id, SMOKE);
						case BM_FLASH: actionGrenade(id, FLASH);
						case BM_AWP: actionAWP(id);
						case BM_TRAMPOLINE_LOW: actionTrampolineLow(id);
						case BM_TRAMPOLINE_MID: actionTrampolineMid(id);
						case BM_TRAMPOLINE_HIGH: actionTrampolineHigh(id);
						case BM_LIGHT: actionLight(id);
						case BM_DUCK: actionDuck(id);
						case BM_MONEY: actionMoney(id, false);
						case BM_SUPERMAN: actionSuperman(id, false);
						case BM_XP: actionXP(id, false);
						case BM_SHOTGUN: actionShotgun(id);
						case BM_SCOUT: actionScout(id);
						case BM_AK47: actionAk47(id);
						case BM_M4A1: actionM4a1(id);
						case BM_USP: actionUsp(id);
						case BM_FAMAS: actionFamas(id);
						case BM_GLOCK: actionGlock(id);
						case BM_P90: actionP90(id);
						case BM_GALIL: actionGalil(id);
					}
				}
			}
			
			//make the trace longer for other blocks
			vTrace[2] = pOrigin[2] - 20.0;
			
			//do 4 traces for each corner of the player
			for (new i = 0; i < 4; ++i)
			{
				switch (i)
				{
					case 0: { vTrace[0] = pOrigin[0] - 16; vTrace[1] = pOrigin[1] + 16; }
					case 1: { vTrace[0] = pOrigin[0] + 16; vTrace[1] = pOrigin[1] + 16; }
					case 2: { vTrace[0] = pOrigin[0] + 16; vTrace[1] = pOrigin[1] - 16; }
					case 3: { vTrace[0] = pOrigin[0] - 16; vTrace[1] = pOrigin[1] - 16; }
				}
				
				ent = trace_line(id, pOrigin, vTrace, vReturn);
				
				//if entity found is a block
				if (isBlock(ent))
				{
					new blockType = entity_get_int(ent, EV_INT_body);
					
					switch (blockType)
					{
						case BM_TRAMPOLINE_MID: actionTrampolineMid(id);
						case BM_NOFALLDAMAGE: actionNoFallDamage(id);
						case BM_ICE: actionOnIce(id);
						case BM_BHOP_NOSLOW: actionNoSlowDown(id);
						case BM_TRAMPOLINE_LOW: actionTrampolineLow(id);
						case BM_TRAMPOLINE_HIGH: actionTrampolineHigh(id);
						case BM_NOFALLDAMAGEBHOP: actionNoFallDamageBhop(id);
					}
				}
			}
			
			//display amount of invincibility/stealth/camouflage/boots of speed timeleft
			new Float:fTime = halflife_time();
			new Float:fTimeleftInvincible = gfInvincibleTimeOut[id] - fTime;
			new Float:fTimeleftStealth = gfStealthTimeOut[id] - fTime;
			new Float:fTimeleftCamouflage = gfCamouflageTimeOut[id] - fTime;
			new Float:fTimeleftBootsOfSpeed = gfBootsOfSpeedTimeOut[id] - fTime;
			new szTextToShow[256] = "";
			new szText[48];
			new bool:bShowText = false;
			
			if (fTimeleftInvincible >= 0.0)
			{
				format(szText, sizeof(szText), "Invincible: %.1f^n", fTimeleftInvincible);
				add(szTextToShow, sizeof(szTextToShow), szText);
				bShowText = true;
			}
			
			if (fTimeleftStealth >= 0.0)
			{
				format(szText, sizeof(szText), "Stealth: %.1f^n", fTimeleftStealth);
				add(szTextToShow, sizeof(szTextToShow), szText);
				bShowText = true;
			}
			
			if (fTimeleftCamouflage >= 0.0)
			{
				//if player is a CT
				if (get_user_team(id) == 1)
				{
					format(szText, sizeof(szText), "You look like a Counter-Terrorist: %.1f^n", fTimeleftCamouflage);
				}
				else
				{
					format(szText, sizeof(szText), "You look like a Terrorist: %.1f^n", fTimeleftCamouflage);
				}
				
				add(szTextToShow, sizeof(szTextToShow), szText);
				bShowText = true;
			}
			
			if (fTimeleftBootsOfSpeed >= 0.0)
			{
				format(szText, sizeof(szText), "Boots of speed: %.1f^n", fTimeleftBootsOfSpeed);
				add(szTextToShow, sizeof(szTextToShow), szText);
				bShowText = true;
			}
			
			//if there is some text to show then show it
			if (bShowText)
			{
				set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
				show_hudmessage(id, szTextToShow);
			}
		}
		
		//get players buttons
		new buttons = get_user_button(id);
		new oldbuttons = get_user_oldbutton(id);
		
		//if player has grabbed an entity
		if (gGrabbed[id] > 0)
		{
			//check for a single press on the following buttons
			if (buttons & IN_JUMP && !(oldbuttons & IN_JUMP)) cmdJump(id);
			if (buttons & IN_DUCK && !(oldbuttons & IN_DUCK)) cmdDuck(id);
			if (buttons & IN_ATTACK && !(oldbuttons & IN_ATTACK)) cmdAttack(id);
			if (buttons & IN_ATTACK2 && !(oldbuttons & IN_ATTACK2)) cmdAttack2(id);
			
			//prevent player from using attack
			buttons &= ~IN_ATTACK;
			entity_set_int(id, EV_INT_button, buttons);
			
			//if player has grabbed a valid entity
			if (is_valid_ent(gGrabbed[id]))
			{
				//if block the player is grabbing is in their group and group count is larger than 1
				if (isBlockInGroup(id, gGrabbed[id]) && gGroupCount[id] > 1)
				{
					new Float:vMoveTo[3];
					new Float:vOffset[3];
					new Float:vOrigin[3];
					new block;
					
					//move the block the player has grabbed and get the move vector
					moveGrabbedEntity(id, vMoveTo);
					
					//move the rest of the blocks in the players group using vector for grabbed block
					for (new i = 0; i <= gGroupCount[id]; ++i)
					{
						block = gGroupedBlocks[id][i];
						
						//if block is still in this players group
						if (isBlockInGroup(id, block))
						{
							//get offset vector from block
							entity_get_vector(block, EV_VEC_vuser1, vOffset);
							
							vOrigin[0] = vMoveTo[0] - vOffset[0];
							vOrigin[1] = vMoveTo[1] - vOffset[1];
							vOrigin[2] = vMoveTo[2] - vOffset[2];
							
							//move grouped block
							moveEntity(id, block, vOrigin, false);
						}
					}
				}
				else
				{
					//move the entity the player has grabbed
					moveGrabbedEntity(id);
				}
			}
			else
			{
				cmdRelease(id);
			}
		}
		
		//if player has just deleted something
		if (gbJustDeleted[id])
		{
			//if player is pressing attack2
			if (buttons & IN_ATTACK2)
			{
				//prevent player from using attack2
				buttons &= ~IN_ATTACK2;
				entity_set_int(id, EV_INT_button, buttons);
			}
			else
			{
				//player has now NOT just deleted something
				gbJustDeleted[id] = false;
			}
		}
	}
	
	return PLUGIN_CONTINUE;
}

public client_PostThink(id)
{
	//if player is alive
	if (is_user_alive(id))
	{
		//if player is set to not get fall damage
		if (gbNoFallDamage[id])
		{
			entity_set_int(id,  EV_INT_watertype, -3);
			gbNoFallDamage[id] = false;
		}
		
		//if player is set to not get fall damage
		if (gbNoFallDamageBhop[id])
		{
			entity_set_int(id,  EV_INT_watertype, -3);
			gbNoFallDamageBhop[id] = false;
		}
	}
}

/***** EVENTS *****/
public eventPlayerDeath()
{
	new id = read_data(2);
	
	resetTimers(id);
}

public eventRoundRestart()
{
	//iterate through all players
	for (new id = 1; id <= 32; ++id)
	{
		//reset all players timers
		resetTimers(id);
		
		grenade_taken[HE][id] = false;
		grenade_taken[SMOKE][id] = false;
		grenade_taken[FLASH][id] = false;
		DEagleUsed[id] = false;
		ShotgunUsed[id] = false;
		ScoutUsed[id] = false;
		AwpUsed[id] = false;
		HeUsed[id] = false;
		FrostUsed[id] = false;
		FlashUsed[id] = false;
		Ak47Used[id] = false;
		M4a1Used[id] = false;
		UspUsed[id] = false;
		FamasUsed[id] = false;
		GlockUsed[id] = false;
		P90Used[id] = false;
		GalilUsed[id] = false;
	}
}

public eventPlayerSpawn(id)
{
	//if player has godmode enabled
	if (gbAdminGodmode[id])
	{
		//re-enable godmode on player
		set_user_godmode(id, 1);
	}
	
	//if player has noclip enabled
	if (gbAdminNoclip[id])
	{
		//re-enable noclip on player
		set_user_noclip(id, 1);
	}
}

resetTimers(id)
{
	gfInvincibleTimeOut[id] = 0.0;
	gfInvincibleNextUse[id] = 0.0;
	gfStealthTimeOut[id] = 0.0;
	gfStealthNextUse[id] = 0.0;
	gfCamouflageTimeOut[id] = 0.0;
	gfCamouflageNextUse[id] = 0.0;
	gbOnFire[id] = false;
	gfRandomNextUse[id] = 0.0;
	gfBootsOfSpeedTimeOut[id] = 0.0;
	gfBootsOfSpeedNextUse[id] = 0.0;
	gfMoneyNextUse[id] = 0.0;
	gfSupermanTimeOut[id] = 0.0;
	gfSupermanNextUse[id] = 0.0;
	gfXPNextUse[id] = 0.0;
	
	//remove any task this player might have
	new taskId = TASK_INVINCIBLE + id;
	if (task_exists(taskId)) 
	{
		remove_task(taskId);
	}
	
	taskId = TASK_STEALTH + id;
	if (task_exists(taskId))
	{
		remove_task(taskId);
	}
	
	taskId = TASK_CAMOUFLAGE + id;
	if (task_exists(taskId))
	{
		remove_task(taskId);
		
		//change back to players old model
		cs_set_user_model(id, gszCamouflageOldModel[id]);
	}
	
	taskId = TASK_BOOTSOFSPEED + id;
	if (task_exists(taskId))
	{
		remove_task(taskId);
	}
	
	//make sure player is connected
	if (is_user_connected(id))
	{
		//set players rendering to normal
		set_user_rendering(id, kRenderFxGlowShell, 0, 0, 0, kRenderNormal, 255);
	}
	
	//player is not 'on ice'
	gbOnIce[id] = false;
	
	//player slows down after jumping
	gbNoSlowDown[id] = false;
}

public eventCurWeapon(id)
{
	new Float:fTime = halflife_time();
	new Float:fTimeleftBootsOfSpeed = gfBootsOfSpeedTimeOut[id] - fTime;
	
	//if the player has the boots of speed
	if (fTimeleftBootsOfSpeed >= 0.0)
	{
		//set their max speed so they obtain their speed after changing weapon
		set_user_maxspeed(id, gfBootsMaxSpeed);
	}
}

/***** BLOCK ACTIONS *****/
actionXP(id, OverrideTimer)
{
	new Float:fTime = halflife_time();
    
	if (fTime >= gfXPNextUse[id] || OverrideTimer)
	{
		if ( get_user_team ( id ) == 1 )
		{
			hnsxp_add_user_xp(id, 100);
			ColorChat(id, TEAM_COLOR, "^3You have been given ^4100 ^3XP!");
		}
		
		gfXPNextUse[id] = fTime + get_cvar_float("bm_xpcooldown");  
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Wait Time: One Round", gfXPNextUse[id] - fTime);
	}
	return PLUGIN_HANDLED;
}

actionSuperman(id, OverrideTimer)
{
	new Float:fTime = halflife_time();
	
	if (fTime >= gfSupermanNextUse[id] || OverrideTimer)
	{
		new Float:fTimeout = get_cvar_float("bm_supermantime");
		
		set_task(fTimeout, "taskSupermanRemove", TASK_SUPERMAN + id, "", 0, "a", 1);
		
		set_user_gravity(id, 0.50);
		
		gfSupermanTimeOut[id] = fTime + fTimeout;
		gfSupermanNextUse[id] = fTime + fTimeout + get_cvar_float("bm_supermancooldown");
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Superman next use: %.1f", gfSupermanNextUse[id] - fTime);
	}
}

actionMoney(id, OverrideTimer)
{
    new Float:fTime = halflife_time();
    
    if (fTime >= gfMoneyNextUse[id] || OverrideTimer)
    {
		if ( get_user_team ( id ) == 1 )
		{
			if ( cs_get_user_money( id ) == 16000 )
			{
				return PLUGIN_HANDLED;
			}
			else 
			{
				cs_set_user_money(id, cs_get_user_money(id) + 5000);
			}
		}
		
		gfMoneyNextUse[id] = fTime + get_cvar_float("bm_moneycooldown");
    }
    else
    {
        set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
        show_hudmessage(id, "Wait Time: One Round", gfMoneyNextUse[id] - fTime);
    }
    return PLUGIN_HANDLED;
}

actionDuck(id)
{
	entity_set_int(id, EV_INT_bInDuck, 15);
}

actionLight(id)
{
    new origin[3];
    get_user_origin(id, origin);
                            
    message_begin(MSG_BROADCAST,SVC_TEMPENTITY,origin);
    
    if ( get_user_team ( id ) == 1 ) {              
     	  write_byte(TE_DLIGHT);
    	  write_coord(origin[0]);
    	  write_coord(origin[1]); 
    	  write_coord(origin[2]);  
    	  write_byte(50);
    	  write_byte(255);
    	  write_byte(0);
    	  write_byte(0);
    	  write_byte(255);
    	  write_byte(50); 
    	  message_end();
    }
    if ( get_user_team ( id ) == 2 ) {
          write_byte(TE_DLIGHT);
    	  write_coord(origin[0]);
    	  write_coord(origin[1]); 
    	  write_coord(origin[2]);  
    	  write_byte(50);
    	  write_byte(0);
    	  write_byte(0);
    	  write_byte(255);
    	  write_byte(255);
    	  write_byte(50); 
    	  message_end();
    }
    if ( get_user_team ( id ) == 3 ) {
	  write_byte(TE_DLIGHT);
    	  write_coord(origin[0]);
    	  write_coord(origin[1]); 
    	  write_coord(origin[2]);  
    	  write_byte(50);
    	  write_byte(69);
    	  write_byte(69);
    	  write_byte(69);
    	  write_byte(255);
    	  write_byte(50); 
    	  message_end();
    }
}
 
actionDamage(id)
{
	if (halflife_time() >= gfNextDamageTime[id])
	{
		if (get_user_health(id) > 0)
		{
			new Float:amount = get_cvar_float("bm_damageamount");
			fakedamage(id, "damage block", amount, DMG_CRUSH);
		}
		
		gfNextDamageTime[id] = halflife_time() + 0.5;
	}
}

actionHeal(id)
{
	if (halflife_time() >= gfNextHealTime[id])
	{
		new hp = get_user_health(id);
		new amount = floatround(get_cvar_float("bm_healamount"), floatround_floor);
		new sum = (hp + amount);
		
		if (sum < 100)
		{
			set_user_health(id, sum);
		}
		else if (hp > 101)
		{
			set_user_health( id, hp );
		}
		
		gfNextHealTime[id] = halflife_time() + 0.5;
	}
}

actionInvincible(id, OverrideTimer)
{
	new Float:fTime = halflife_time();
	
	if (fTime >= gfInvincibleNextUse[id] || OverrideTimer)
	{
		new Float:fTimeout = get_cvar_float("bm_invincibletime");
		
		set_user_godmode(id, 1);
		set_task(fTimeout, "taskInvincibleRemove", TASK_INVINCIBLE + id, "", 0, "a", 1);
		
		//only make player glow white for invincibility if player isn't already stealth
		if (fTime >= gfStealthTimeOut[id])
		{
			set_user_rendering(id, kRenderFxGlowShell, 255, 255, 255, kRenderNormal, 16);
		}
		
		//play invincibility sound
		emit_sound(id, CHAN_STATIC, gszInvincibleSound, 1.0, ATTN_NORM, 0, PITCH_NORM);
		
		gfInvincibleTimeOut[id] = fTime + fTimeout;
		gfInvincibleNextUse[id] = fTime + fTimeout + get_cvar_float("bm_invinciblecooldown");
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Invincibility next use: %.1f", gfInvincibleNextUse[id] - fTime);
	}
}

actionStealth(id, OverrideTimer)
{
	new Float:fTime = halflife_time();
	
	//check if player is outside of cooldown time to use stealth
	if (fTime >= gfStealthNextUse[id] || OverrideTimer)
	{
		new Float:fTimeout = get_cvar_float("bm_stealthtime");
		
		//set a task to remove stealth after time out amount
		set_task(fTimeout, "taskStealthRemove", TASK_STEALTH + id, "", 0, "a", 1);
		
		//make player invisible
		set_user_rendering(id, kRenderFxGlowShell, 0, 0, 0, kRenderTransColor, 0);
		
		//play stealth sound
		emit_sound(id, CHAN_STATIC, gszStealthSound, 1.0, ATTN_NORM, 0, PITCH_NORM);
		
		gfStealthTimeOut[id] = fTime + fTimeout;
		gfStealthNextUse[id] = fTime + fTimeout + get_cvar_float("bm_stealthcooldown");
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Stealth next use: %.1f", gfStealthNextUse[id] - fTime);
	}
}

actionTrampolineMid(id)
{
	//if trampoline timeout has exceeded (needed to prevent velocity being given multiple times)
	if (halflife_time() >= gfTrampolineTimeout[id])
		{
		new Float:velocity[3];
		
		//set player Z velocity to make player bounce
		entity_get_vector(id, EV_VEC_velocity, velocity);
		velocity[2] = 500.0;					//jump velocity
		entity_set_vector(id, EV_VEC_velocity, velocity);
		
		entity_set_int(id, EV_INT_gaitsequence, 6);   		//play the Jump Animation
		
		gfTrampolineTimeout[id] = halflife_time() + 0.5;
	}
}

actionTrampolineLow(id)
{
	//if trampoline timeout has exceeded (needed to prevent velocity being given multiple times)
	if (halflife_time() >= gfTrampolineLowTimeout[id])
		{
		new Float:velocity[3];
		
		//set player Z velocity to make player bounce
		entity_get_vector(id, EV_VEC_velocity, velocity);
		velocity[2] = 250.0;					//jump velocity
		entity_set_vector(id, EV_VEC_velocity, velocity);
		
		entity_set_int(id, EV_INT_gaitsequence, 6);   		//play the Jump Animation
		
		gfTrampolineLowTimeout[id] = halflife_time() + 0.5;
	}
}

actionTrampolineHigh(id)
{
	//if trampoline timeout has exceeded (needed to prevent velocity being given multiple times)
	if (halflife_time() >= gfTrampolineMidTimeout[id])
	{
		new Float:velocity[3];
		
		//set player Z velocity to make player bounce
		entity_get_vector(id, EV_VEC_velocity, velocity);
		velocity[2] = 750.0;					//jump velocity
		entity_set_vector(id, EV_VEC_velocity, velocity);
		
		entity_set_int(id, EV_INT_gaitsequence, 6);   		//play the Jump Animation
		
		gfTrampolineMidTimeout[id] = halflife_time() + 0.5;
	}
}

actionSpeedBoost(id)
{
	//if speed boost timeout has exceeded (needed to prevent speed boost being given multiple times)
	if (halflife_time() >= gfSpeedBoostTimeOut[id])
	{
		new Float:pAim[3];
		
		//set velocity on player in direction they're aiming
		velocity_by_aim(id, 800, pAim);
		pAim[2] = 260.0;					//make sure Z velocity is only as high as a jump
		entity_set_vector(id, EV_VEC_velocity, pAim);
		
		entity_set_int(id, EV_INT_gaitsequence, 6);   		//play the Jump Animation
		
		gfSpeedBoostTimeOut[id] = halflife_time() + 0.5;
	}
}

actionNoFallDamage(id)
{
	//set the player to not receive any fall damage (handled in client_PostThink)
	gbNoFallDamage[id] = true;
}

actionNoFallDamageBhop(id)
{
	//set the player to not receive any fall damage (handled in client_PostThink)
	gbNoFallDamageBhop[id] = true;	
}

actionOnIce(id)
{
	new taskid = TASK_ICE + id;
	
	if (!gbOnIce[id])
	{
		//save players maxspeed value
		gfOldMaxSpeed[id] = get_user_maxspeed(id);
		
		//make player feel like they're on ice
		entity_set_float(id, EV_FL_friction, 0.15);
		set_user_maxspeed(id, 600.0);
		
		//player is now 'on ice'
		gbOnIce[id] = true;
	}
	
	//remove any existing 'not on ice' task
	if (task_exists(taskid))
	{
		remove_task(taskid);
	}
	
	//set task to remove 'on ice' effect very soon (task replaced if player is still on ice before task time reached)
	set_task(0.1, "taskNotOnIce", taskid);
}

actionDeath(id)
{
	//if player does not have godmode enabled (admin godmode or invincibility)
	if (!get_user_godmode(id))
	{
		//kill player by inflicting massive damage
		fakedamage(id, "the block of death", 10000.0, DMG_GENERIC);
		
		new Deathname[42];
		get_user_name(id, Deathname, 32);
		set_hudmessage(255, 0, 0, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "%s Died on a Death", Deathname);
		
	}
}

actionCamouflage(id, OverrideTimer)
{
	new Float:fTime = halflife_time();
	
	//check if player is outside of cooldown time to use camouflage
	if (fTime >= gfCamouflageNextUse[id] || OverrideTimer)
	{
		new Float:fTimeout = get_cvar_float("bm_camouflagetime");
		
		//get players team and model
		new szModel[32];
		new team;
		
		cs_get_user_model(id, szModel, 32);
		
		team = get_user_team(id);
		
		//save current player model
		gszCamouflageOldModel[id] = szModel;
		
		//change player model depending on their current team
		if (team == 1)		//TERRORIST
		{
			cs_set_user_model(id, "urban");
		}
		else
		{
			cs_set_user_model(id, "leet");
		}
		
		//play camouflage sound
		emit_sound(id, CHAN_STATIC, gszCamouflageSound, 1.0, ATTN_NORM, 0, PITCH_NORM);
		
		//set a task to remove camouflage after time out amount
		set_task(fTimeout, "taskCamouflageRemove", TASK_CAMOUFLAGE + id, "", 0, "a", 1);
		
		//set timers to prevent player from using camouflage again so soon
		gfCamouflageTimeOut[id] = fTime + fTimeout;
		gfCamouflageNextUse[id] = fTime + fTimeout + get_cvar_float("bm_camouflagecooldown");
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Camouflage next use: %.1f", gfCamouflageNextUse[id] - fTime);
	}
}

actionLowGravity(id)
{
	//set player to have low gravity
	set_user_gravity(id, 0.25);
	
	//set global boolean showing player has low gravity
	gbLowGravity[id] = true;
}

actionFire(id, ent)
{
	if (halflife_time() >= gfNextDamageTime[id])
	{
		new hp = get_user_health(id);
		
		//if players health is greater than 0
		if (hp > 0)
		{
			//if player does not have godmode
			if (!get_user_godmode(id))
			{
				new Float:amount = get_cvar_float("bm_firedamageamount") / 10.0;
				new Float:newAmount = hp - amount;
				
				//if this amount of damage won't kill the player
				if (newAmount > 0)
				{
					set_user_health(id, floatround(newAmount, floatround_floor));
				}
				else
				{
					//use fakedamage to kill the player
					fakedamage(id, "fire block", amount, DMG_BURN);
				}
			}
			
			//get halflife time and time for next fire sound from fire block
			new Float:fTime = halflife_time();
			new Float:fNextFireSoundTime = entity_get_float(ent, EV_FL_ltime); 
			
			//if the current time is greater than or equal to the next time to play the fire sound
			if (fTime >= fNextFireSoundTime)
			{
				//play the fire sound
				emit_sound(ent, CHAN_ITEM, gszFireSoundFlame, 0.6, ATTN_NORM, 0, PITCH_NORM);
				
				//set the fire blocks time
				entity_set_float(ent, EV_FL_ltime, fTime + 0.75);
			}
			
			//get effects vectors using block origin
			new Float:origin1[3];
			new Float:origin2[3];
			entity_get_vector(ent, EV_VEC_origin, origin1);
			entity_get_vector(ent, EV_VEC_origin, origin2);
			origin1[0] -= 32.0;
			origin1[1] -= 32.0;
			origin1[2] += 10.0;
			origin2[0] += 32.0;
			origin2[1] += 32.0;
			origin2[2] += 10.0;
			
			//get a random height for the flame
			new randHeight = random_num(0, 32) + 16;
			
			//show some effects
			message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
			write_byte(TE_BUBBLES);
			write_coord(floatround(origin1[0], floatround_floor));	//min start position
			write_coord(floatround(origin1[1], floatround_floor));
			write_coord(floatround(origin1[2], floatround_floor));
			write_coord(floatround(origin2[0], floatround_floor));	//max start position
			write_coord(floatround(origin2[1], floatround_floor));
			write_coord(floatround(origin2[2], floatround_floor));
			write_coord(randHeight);				//float height
			write_short(gSpriteIdFire);				//model index
			write_byte(10);						//count
			write_coord(1);						//speed
			message_end();
		}
		
		gfNextDamageTime[id] = halflife_time() + 0.05;
	}
}

actionSlap(id)
{
	user_slap(id, 0);
	user_slap(id, 0);
	set_hudmessage(0, 100, 255, -1.0, 0.20, 0, 6.0, 12.0, 0.0, 1.0, 3);
	
	show_hudmessage(id, "GET OFF MY FACE!!!");
}

actionGrenade(id, num)
{
    if(is_user_alive(id))
    {
        switch(num)
        {
            case HE:
			{
				if(!cs_get_user_bpammo(id, CSW_HEGRENADE) && !HeUsed[id] && get_user_team(id) == 1)
				{
					give_item(id, "weapon_hegrenade");
					HeUsed[id] = true;
				}
			}
            case SMOKE:
			{
				if(!cs_get_user_bpammo(id, CSW_SMOKEGRENADE) && !FrostUsed[id] && get_user_team(id) == 1)
				{
					give_item(id, "weapon_smokegrenade");
					FrostUsed[id] = true;
				}
			}
            case FLASH:
			{
				if(cs_get_user_bpammo(id, CSW_FLASHBANG) < 2 && !FlashUsed[id] && get_user_team(id) == 1)
				{
					give_item(id, "weapon_flashbang");
					FlashUsed[id] = true;
				}
			}
        }
    }
}

actionRandom(id, ent)
{
	new Float:fTime = halflife_time();
	
	//check if player is outside of cooldown time to use camouflage
	if (fTime >= gfRandomNextUse[id])
	{
		//get which type of block this is set to be
		new blockType = entity_get_int(ent, EV_INT_iuser4);
		
		//do the random block action
		switch (blockType)
		{
			case BM_INVINCIBILITY: actionInvincible(id, true);
			case BM_STEALTH: actionStealth(id, true);
			case BM_DEATH: actionDeath(id);
			case BM_CAMOUFLAGE: actionCamouflage(id, true);
			case BM_SLAP: actionSlap(id); 
			case BM_BOOTSOFSPEED: actionBootsOfSpeed(id, true);
			case BM_DEAGLE: actionDEagle(id);
			case BM_SHOTGUN: actionShotgun(id);
			case BM_SCOUT: actionScout(id);
			case BM_AWP: actionAWP(id);
			case BM_AK47: actionAk47(id);
			case BM_M4A1: actionM4a1(id);
			case BM_USP: actionUsp(id);
			case BM_FAMAS: actionFamas(id);
			case BM_GLOCK: actionGlock(id);
			case BM_P90: actionP90(id);
			case BM_GALIL: actionGalil(id);
			
		}
		
		//set timer to prevent player from using the random block again so soon
		gfRandomNextUse[id] = fTime + get_cvar_float("bm_randomcooldown");
		
		//set this random block to another random block!
		new randNum = random_num(0, gRandomBlocksMax - 1);
		entity_set_int(ent, EV_INT_iuser4, gRandomBlocks[randNum]);
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Random block next use: %.1f", gfRandomNextUse[id] - fTime);
	}
}

actionHoney(id)
{
	new taskid = TASK_HONEY + id;
	
	//make player feel like they're stuck in honey by lowering their maxspeed
	set_user_maxspeed(id, 50.0);
	
	//remove any existing 'in honey' task
	if (task_exists(taskid))
	{
		remove_task(taskid);
	}
	else
	{
		//half the players velocity the first time they touch it
		new Float:vVelocity[3];
		entity_get_vector(id, EV_VEC_velocity, vVelocity);
		vVelocity[0] = vVelocity[0] / 2.0;
		vVelocity[1] = vVelocity[1] / 2.0;
		entity_set_vector(id, EV_VEC_velocity, vVelocity);
	}
	
	//set task to remove 'in honey' effect very soon (task replaced if player is still in honey before task time reached)
	set_task(0.1, "taskNotInHoney", taskid);
}

actionBootsOfSpeed(id, bool:OverrideTimer)
{
	new Float:fTime = halflife_time();
	
	//check if player is outside of cooldown time to use the boots of speed
	if (fTime >= gfBootsOfSpeedNextUse[id] || OverrideTimer)
	{
		new Float:fTimeout = get_cvar_float("bm_bootsofspeedtime");
		
		//set a task to remove the boots of speed after time out amount
		set_task(fTimeout, "taskBootsOfSpeedRemove", TASK_BOOTSOFSPEED + id, "", 0, "a", 1);
		
		//set the players maxspeed to 400 so they run faster!
		set_user_maxspeed(id, gfBootsMaxSpeed);
		
		//play boots of speed sound
		emit_sound(id, CHAN_STATIC, gszBootsOfSpeedSound, 1.0, ATTN_NORM, 0, PITCH_NORM);
		
		gfBootsOfSpeedTimeOut[id] = fTime + fTimeout;
		gfBootsOfSpeedNextUse[id] = fTime + fTimeout + get_cvar_float("bm_bootsofspeedcooldown");
	}
	else
	{
		set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel);
		show_hudmessage(id, "Boots of speed next use: %.1f", gfBootsOfSpeedNextUse[id] - fTime);
	}
}

actionNoSlowDown(id)
{
	new taskid = TASK_NOSLOW + id;
	
	gbNoSlowDown[id] = true;
	
	//remove any existing 'slow down' task
	if (task_exists(taskid))
	{
		remove_task(taskid);
	}
	
	//set task to remove 'no slow down' effect very soon
	set_task(0.1, "taskSlowDown", taskid);
}

public Flash(id){
	message_begin(MSG_ONE,gMsgScreenFade,{0,0,0},id);
	write_short( 1<<15 );
	write_short( 1<<10 );
	write_short( 1<<12 );
	write_byte( 0 );
	write_byte( 0 );
	write_byte( 0 );
	write_byte( 255 );
	message_end();
}

actionFade(id){
	if(is_user_alive(id)){
		gMsgScreenFade = get_user_msgid("ScreenFade");
		Flash(id);
	}
}

actionDEagle(id)
{
	if (is_user_alive(id) && !DEagleUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_deagle");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_deagle", id), 1);
		DEagleUsed[id] = true;
		new deaglename[42];
		get_user_name(id, deaglename, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Be Careful CT! %s is running around with a Deagle!", deaglename);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Be Careful CT! ^4%s ^3is running around with a ^4Deagle!", gszPrefix, deaglename);
	}
}

actionShotgun(id)
{
	if (is_user_alive(id) && !ShotgunUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_m3");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_m3", id), 1);
		ShotgunUsed[id] = true;
		new shotgunname[42];
		get_user_name(id, shotgunname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Be Aware CT! %s is running around with a Shotgun!", shotgunname);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Be Aware CT! ^4%s ^3is running around with a ^4Shotgun!", gszPrefix, shotgunname);
	}
}

actionScout(id)
{
	if (is_user_alive(id) && !ScoutUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_scout");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_scout", id), 1);
		ScoutUsed[id] = true;
		new scoutname[42];
		get_user_name(id, scoutname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Be Careful CT! %s is running around with a Scout!", scoutname);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Be Careful CT! ^4%s ^3is running around with a ^4Scout!", gszPrefix, scoutname);
	}
}

actionAWP(id)
{
	if (is_user_alive(id) && !AwpUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_awp");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_awp", id), 1);
		AwpUsed[id] = true;
		new awpname[42];
		get_user_name(id, awpname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Watch out CT! %s has picked up an AWP!", awpname);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Watch out CT! ^4%s ^3has picked up an ^4AWP!", gszPrefix, awpname);
		
	}
}

actionAk47(id)
{
	if (is_user_alive(id) && !Ak47Used[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_ak47");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_ak47", id), 1);
		Ak47Used[id] = true;
		new ak47name[42];
		get_user_name(id, ak47name, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "CT is in DANGER! %s got an Ak47!", ak47name);
		ColorChat(0, TEAM_COLOR, "^4%s ^3CT is in DANGER! ^4%s ^3got an ^4Ak47!", gszPrefix, ak47name);
		
	}
}

actionM4a1(id)
{

    if (is_user_alive(id) && !M4a1Used[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_m4a1");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_m4a1", id), 1);
		M4a1Used[id] = true;
		new m4a1name[42];
		get_user_name(id, m4a1name, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Cover Your Head CT! %s has took an M4a1!", m4a1name);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Cover Your Head CT! ^4%s ^3has took an ^4M4a1!", gszPrefix, m4a1name);
		
	}
}

actionUsp(id)
{
	if (is_user_alive(id) && !UspUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_usp");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_usp", id), 1);
		UspUsed[id] = true;
		new uspname[42];
		get_user_name(id, uspname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "WTF! %s has picked a USP!", uspname);
		ColorChat(0, TEAM_COLOR, "^4%s WTF! ^4%s ^3has picked a ^4USP!", gszPrefix, uspname);
		
	}
}

actionFamas(id)
{
	if (is_user_alive(id) && !FamasUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_famas");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_famas", id), 1);
		FamasUsed[id] = true;
		new famasname[42];
		get_user_name(id, famasname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Holy Shit! %s has a FAMAS!", famasname);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Holy Shit! ^4%s ^3Got a ^4FAMAS!", gszPrefix, famasname);
		
	}
}

actionGlock(id)
{
	if (is_user_alive(id) && !GlockUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_glock18");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_glock18", id), 1);
		GlockUsed[id] = true;
		new glockname[42];
		get_user_name(id, glockname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Wow! %s has a Glock!", glockname);
		ColorChat(0, TEAM_COLOR, "^4%s ^3WOW! ^4%s ^3Took ^4Glock!", gszPrefix, glockname);
		
	}
}

actionP90(id)
{
	if (is_user_alive(id) && !P90Used[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_p90");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_p90", id), 1);
		P90Used[id] = true;
		new p90name[42];
		get_user_name(id, p90name, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "HIDE CT! %s has a P90!", p90name);
		ColorChat(0, TEAM_COLOR, "^4%s ^3HiDE CT! ^4%s ^3has a ^4P90!", gszPrefix, p90name);
		
	}
}

actionGalil(id)
{
	if (is_user_alive(id) && !GalilUsed[id] && get_user_team(id) == 1)
		{
		give_item(id, "weapon_galil");
		cs_set_weapon_ammo(find_ent_by_owner(1, "weapon_galil", id), 1);
		GalilUsed[id] = true;
		new galilname[42];
		get_user_name(id, galilname, 32);
		set_hudmessage(0, 100, 255, -1.0, -1.0, 0, 6.0, 4.0);
		show_hudmessage(0, "Keep RUNNING CT! %s has a GAlil!", galilname);
		ColorChat(0, TEAM_COLOR, "^4%s ^3Keep RUNNING CT! ^4%s ^3Got a ^4GAlil!", gszPrefix, galilname);
		
	}
}

actionTeleport(id, ent)
{
	//get end entity id
	new tele = entity_get_int(ent, EV_INT_iuser1);
	
	//if teleport end id is valid
	if (tele)
	{
		//get end entity origin
		new Float:vTele[3];
		entity_get_vector(tele, EV_VEC_origin, vTele);
		
		//if id of entity being teleported is a player and telefrags CVAR is set then kill any nearby players
		if ((id > 0 && id <= 32) && get_cvar_num("bm_telefrags") > 0)
		{
			new player = -1;
			
			do
			{
				player = find_ent_in_sphere(player, vTele, 16.0);
				
				//if entity found is a player
				if (player > 0 && player <= 32)
				{
					//if player is alive, and is not the player that went through the teleport
					if (is_user_alive(player) && player != id)
					{
						//kill the player
						user_kill(player, 1);
					}
				}
			}while(player);
		}
		
		//get origin of the start of the teleport
		new Float:vOrigin[3];
		new origin[3];
		entity_get_vector(ent, EV_VEC_origin, vOrigin);
		FVecIVec(vOrigin, origin);
		
		//show some teleporting effects
		message_begin(MSG_PVS, SVC_TEMPENTITY, origin);
		write_byte(TE_IMPLOSION);
		write_coord(origin[0]);
		write_coord(origin[1]);
		write_coord(origin[2]);
		write_byte(64);		// radius
		write_byte(100);	// count
		write_byte(6);		// life
		message_end();
		
		//teleport player
		entity_set_vector(id, EV_VEC_origin, vTele);
		
		//reverse players Z velocity
		new Float:vVelocity[3];
		entity_get_vector(id, EV_VEC_velocity, vVelocity);
		vVelocity[2] = floatabs(vVelocity[2]);
		entity_set_vector(id, EV_VEC_velocity, vVelocity);
		
		//if teleport sound CVAR is set
		if (get_cvar_num("bm_teleportsound") > 0)
		{
			//play teleport sound
			emit_sound(id, CHAN_STATIC, gszTeleportSound, 1.0, ATTN_NORM, 0, PITCH_NORM);
		}
	}
}

/***** TASKS *****/
public taskSupermanRemove(id)
{
	id -= TASK_SUPERMAN;
	
	if (is_user_alive(id))
	{
		set_user_gravity(id, 1.0);
	}
}

public taskSolidNot(ent)
{
	ent -= TASK_BHOPSOLIDNOT;
	
	//make sure entity is valid
	if (is_valid_ent(ent))
	{
		//if block isn't being grabbed
		if (entity_get_int(ent, EV_INT_iuser2) == 0)
		{
			entity_set_int(ent, EV_INT_solid, SOLID_NOT);
			set_rendering(ent, kRenderFxNone, 255, 255, 255, kRenderTransAdd, 25);
			set_task(1.0, "taskSolid", TASK_BHOPSOLID + ent);
		}
	}
}

public taskSolid(ent)
{
	ent -= TASK_BHOPSOLID;
	
	//make sure entity is valid
	if (isBlock(ent))
	{
		//make block solid
		entity_set_int(ent, EV_INT_solid, SOLID_BBOX);
		
		//get the player ID of who has the block in a group (if anyone)
		new player = entity_get_int(ent, EV_INT_iuser1);
		
		//if the block is in a group
		if (player > 0)
		{
			//set the block so it is now 'being grouped' (for setting the rendering)
			groupBlock(0, ent);
		}
		else
		{
			new blockType = entity_get_int(ent, EV_INT_body);
			
			set_block_rendering(ent, gRender[blockType], gRed[blockType], gGreen[blockType], gBlue[blockType], gAlpha[blockType]);
		}
	}
}

public taskInvincibleRemove(id)
{
	id -= TASK_INVINCIBLE;
	
	//make sure player is alive
	if (is_user_alive(id))
	{
		set_user_godmode(id, 0);
		
		//only set players rendering back to normal if player is not stealth
		if (halflife_time() >= gfStealthTimeOut[id])
		{
			set_user_rendering(id, kRenderFxGlowShell, 0, 0, 0, kRenderNormal, 16);
		}
	}
}

public taskStealthRemove(id)
{
	id -= TASK_STEALTH;
	
	//make sure player is connected
	if (is_user_connected(id))
	{
		//only set players rendering back to normal if player is not invincible
		if (halflife_time() >= gfInvincibleTimeOut[id])
		{	
			set_user_rendering(id, kRenderFxGlowShell, 0, 0, 0, kRenderNormal, 255);
		}
		else	//if player is invincible then set player to glow white
		{
			set_user_rendering(id, kRenderFxGlowShell, 255, 255, 255, kRenderTransColor, 16);
		}
	}
}

public taskNotOnIce(id)
{
	id -= TASK_ICE;
	
	//make player run normally
	entity_set_float(id, EV_FL_friction, 1.0);
	
	if (gfOldMaxSpeed[id] > 100.0)
	{
		set_user_maxspeed(id, gfOldMaxSpeed[id]);
	}
	else
	{
		set_user_maxspeed(id, 250.0);
	}
	
	//no longer 'on ice'
	gbOnIce[id] = false;
	gfOldMaxSpeed[id] = 0.0;
}

public taskCamouflageRemove(id)
{
	id -= TASK_CAMOUFLAGE;
	
	//if player is still connected
	if (is_user_connected(id))
	{
		//change back to players old model
		cs_set_user_model(id, gszCamouflageOldModel[id]);
	}
}

public taskNotInHoney(id)
{
	id -= TASK_HONEY;
	
	//if player is alive
	if (is_user_alive(id))
	{
		//make player move normally
		set_user_maxspeed(id, 250.0);
		
		//this will set the players maxspeed faster if they have the boots of speed!
		eventCurWeapon(id);
	}
}

public taskBootsOfSpeedRemove(id)
{
	id -= TASK_BOOTSOFSPEED;
	
	//set players speed back to normal
	if (is_user_alive(id))
	{
		set_user_maxspeed(id, 250.0);
	}
}

public taskSlowDown(id)
{
	id -= TASK_NOSLOW;
	
	//player no longer has 'no slow down'
	gbNoSlowDown[id] = false;
}

public taskSpriteNextFrame(params[])
{
	new ent = params[0];
	
	//make sure entity is valid
	if (is_valid_ent(ent))
	{
		new frames = params[1];
		new Float:current_frame = entity_get_float(ent, EV_FL_frame);
		
		if (current_frame < 0.0 || current_frame >= frames)
		{
			entity_set_float(ent, EV_FL_frame, 1.0);
		}
		else
		{
			entity_set_float(ent, EV_FL_frame, current_frame + 1.0);
		}
	}
	else
	{
		remove_task(TASK_SPRITE + ent);
	}
}

/***** COMMANDS *****/
public cmdJump(id)
{
	//if the object the player is grabbing isn't too close
	if (gfGrablength[id] > 72.0)
	{
		//move the object closer
		gfGrablength[id] -= 16.0;
	}
}

public cmdDuck(id)
{
	//move the object further away
	gfGrablength[id] += 16.0;
}

public cmdAttack(id)
{
	//if entity being grabbed is a block
	if (isBlock(gGrabbed[id]))
	{
		//if block the player is grabbing is in their group and group count is larger than 1
		if (isBlockInGroup(id, gGrabbed[id]) && gGroupCount[id] > 1)
		{
			new block;
			
			//move the rest of the blocks in the players group using vector for grabbed block
			for (new i = 0; i <= gGroupCount[id]; ++i)
			{
				block = gGroupedBlocks[id][i];
				
				//if this block is in this players group
				if (isBlockInGroup(id, block))
				{
					//only copy the block if it is not stuck
					if (!isBlockStuck(block))
					{
						//copy the block
						copyBlock(id, block);
					}
				}
			}
		}
		else
		{
			//only copy the block the player has grabbed if it is not stuck
			if (!isBlockStuck(gGrabbed[id]))
			{
				//copy the block
				new newBlock = copyBlock(id, gGrabbed[id]);
				
				//if new block was created successfully
				if (newBlock)
				{
					//set currently grabbed block to 'not being grabbed'
					entity_set_int(gGrabbed[id], EV_INT_iuser2, 0);
					
					//set new block to 'being grabbed'
					entity_set_int(newBlock, EV_INT_iuser2, id);
					
					//set player to grabbing new block
					gGrabbed[id] = newBlock;
				}
			}
			else
			{
				//tell the player they can't copy a block when it is in a stuck position
				ColorChat(id, TEAM_COLOR, "^4%s ^3You cannot ^4copy ^3a block that is in a ^4stuck ^3position!", gszPrefix);
			}
		}
	}
}

public cmdAttack2(id)
{
	//if player is grabbing a block
	if (isBlock(gGrabbed[id]))
	{
		//if block the player is grabbing is in their group and group count is larger than 1
		if (isBlockInGroup(id, gGrabbed[id]) && gGroupCount[id] > 1)
		{
			new block;
			
			//iterate through all blocks in the players group
			for (new i = 0; i <= gGroupCount[id]; ++i)
			{
				block = gGroupedBlocks[id][i];
				
				//if block is still valid
				if (is_valid_ent(block))
				{
					//if block is still in this players group
					if (isBlockInGroup(id, block))
					{
						//delete the block
						gbJustDeleted[id] = deleteBlock(block);
					}
				}
			}
		}
		else
		{
			//delete the block
			gbJustDeleted[id] = deleteBlock(gGrabbed[id]);
		}
	}
	//if player is grabbing a teleport
	else if (isTeleport(gGrabbed[id]))
	{
		//delete the teleport
		gbJustDeleted[id] = deleteTeleport(id, gGrabbed[id]);
	}
}

public cmdGrab(id)
{
	//make sure player has access to use this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get the entity the player is aiming at and the length
		new ent, body;
		gfGrablength[id] = get_user_aiming(id, ent, body);
		
		//set booleans depending on entity type
		new bool:bIsBlock = isBlock(ent);
		new bool:bIsTeleport = isTeleport(ent);
		
		//if the entity is a block or teleport
		if (bIsBlock || bIsTeleport)
		{
			//get who is currently grabbing the entity (if anyone)
			new grabber = entity_get_int(ent, EV_INT_iuser2);
			
			//if entity is not being grabbed by someone else
			if (grabber == 0 || grabber == id)
			{
				//if entity is a block
				if (bIsBlock)
				{
					//get the player ID of who has the block in a group (if anyone)
					new player = entity_get_int(ent, EV_INT_iuser1);
					
					//if the block is not in a group or is in this players group
					if (player == 0 || player == id)
					{
						//set the block to 'being grabbed'
						setGrabbed(id, ent);
						
						//if this block is in this players group and group count is greater than 1
						if (player == id && gGroupCount[id] > 1)
						{
							new Float:vGrabbedOrigin[3];
							new Float:vOrigin[3];
							new Float:vOffset[3];
							new block;
							
							//get origin of the block
							entity_get_vector(ent, EV_VEC_origin, vGrabbedOrigin);
							
							//iterate through all blocks in players group
							for (new i = 0; i <= gGroupCount[id]; ++i)
							{
								block = gGroupedBlocks[id][i];
								
								//if block is still valid
								if (is_valid_ent(block))
								{
									player = entity_get_int(ent, EV_INT_iuser1);
									
									//if block is still in this players group
									if (player == id)
									{
										//get origin of block in players group
										entity_get_vector(block, EV_VEC_origin, vOrigin);
										
										//calculate offset from grabbed block
										vOffset[0] = vGrabbedOrigin[0] - vOrigin[0];
										vOffset[1] = vGrabbedOrigin[1] - vOrigin[1];
										vOffset[2] = vGrabbedOrigin[2] - vOrigin[2];
										
										//save offset value in grouped block
										entity_set_vector(block, EV_VEC_vuser1, vOffset);
										
										//indicate that entity is being grabbed
										entity_set_int(block, EV_INT_iuser2, id);
									}
								}
							}
						}
					}
				}
				//if entity is a teleporter
				else if (bIsTeleport)
				{
					//set the teleport to 'being grabbed'
					setGrabbed(id, ent);
				}
			}
		}
	}
	
	return PLUGIN_HANDLED;
}

setGrabbed(id, ent)
{
	new Float:fpOrigin[3];
	new Float:fbOrigin[3];
	new Float:fAiming[3];
	new iAiming[3];
	new bOrigin[3];
	
	//get players current view model then clear it
	entity_get_string(id, EV_SZ_viewmodel, gszViewModel[id], 32);
	entity_set_string(id, EV_SZ_viewmodel, "");
	
	get_user_origin(id, bOrigin, 1);			//position from eyes (weapon aiming)
	get_user_origin(id, iAiming, 3);			//end position from eyes (hit point for weapon)
	entity_get_vector(id, EV_VEC_origin, fpOrigin);		//get player position
	entity_get_vector(ent, EV_VEC_origin, fbOrigin);	//get block position
	IVecFVec(iAiming, fAiming);
	FVecIVec(fbOrigin, bOrigin);
	
	//set gGrabbed
	gGrabbed[id] = ent;
	gvGrabOffset[id][0] = fbOrigin[0] - iAiming[0];
	gvGrabOffset[id][1] = fbOrigin[1] - iAiming[1];
	gvGrabOffset[id][2] = fbOrigin[2] - iAiming[2];
	
	//indicate that entity is being grabbed
	entity_set_int(ent, EV_INT_iuser2, id);
}

public cmdRelease(id)
{
	//make sure player has access to use this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//if player is grabbing an entity
		if (gGrabbed[id])
		{
			//if entity player is grabbing is a block
			if (isBlock(gGrabbed[id]))
			{
				//if block the player is grabbing is in their group and group count is > 1
				if (isBlockInGroup(id, gGrabbed[id]) && gGroupCount[id] > 1)
				{
					new block;
					new bool:bGroupIsStuck = true;
					
					//iterate through all blocks in the players group
					for (new i = 0; i <= gGroupCount[id]; ++i)
					{
						block = gGroupedBlocks[id][i];
						
						//if this block is in this players group
						if (isBlockInGroup(id, block))
						{
							//indicate that entity is no longer being grabbed
							entity_set_int(block, EV_INT_iuser2, 0);
							
							//start off thinking all blocks in group are stuck
							if (bGroupIsStuck)
							{
								//if block is not stuck
								if (!isBlockStuck(block))
								{
									//at least one of the blocks in the group are not stuck
									bGroupIsStuck = false;
								}
							}
						}
					}
					
					//if all the blocks in the group are stuck
					if (bGroupIsStuck)
					{
						//iterate through all blocks in the players group
						for (new i = 0; i <= gGroupCount[id]; ++i)
						{
							block = gGroupedBlocks[id][i];
							
							//if this block is in this players group
							if (isBlockInGroup(id, block))
							{
								//delete the block
								deleteBlock(block);
							}
						}
						
						//tell the player all the blocks were deleted because they were stuck
						ColorChat(id, TEAM_COLOR, "^4%s ^3Group ^4deleted ^3because all the blocks were ^4stuck!", gszPrefix);
					}
				}
				else
				{
					//if block player has grabbed is valid
					if (is_valid_ent(gGrabbed[id]))
					{
						//if the block is stuck
						if (isBlockStuck(gGrabbed[id]))
						{
							//delete the block
							new bool:bDeleted = deleteBlock(gGrabbed[id]);
							
							//if the block was deleted successfully
							if (bDeleted)
							{
								//tell the player the block was deleted and why
								ColorChat(0, GREEN,  "^x03%s^x04 Block^x01 deleted because it was ^x04stuck!", gszPrefix);
							}
						}
						else
						{
							//indicate that the block is no longer being grabbed
							entity_set_int(gGrabbed[id], EV_INT_iuser2, 0);
						}
					}
				}
			}
			else if (isTeleport(gGrabbed[id]))
			{
				//indicate that the teleport is no longer being grabbed
				entity_set_int(gGrabbed[id], EV_INT_iuser2, 0);
			}
			
			//set the players view model back to what it was
			entity_set_string(id, EV_SZ_viewmodel, gszViewModel[id]);
			
			//indicate that player is not grabbing an object
			gGrabbed[id] = 0;
		}
	}
	
	return PLUGIN_HANDLED;
}

/* MENUS */
public showMainMenu(id)
{
	new col[3];
	new szMenu[256];
	new szGodmode[6];
	new szNoclip[6];
	col = (get_user_flags(id) & BM_ADMIN_LEVEL ? "\w" : "\d");
	szNoclip = (get_user_noclip(id) ? "\yOn" : "\rOff");
	szGodmode = (get_user_godmode(id) ? "\yOn" : "\rOff");
	
	//format the main menu
	format(szMenu, 256, gszMainMenu, col, szNoclip, col, szGodmode);
	
	//show the main menu to the player
	show_menu(id, gKeysMainMenu, szMenu, -1, "bmMainMenu");
	
	return PLUGIN_HANDLED;
}

showBlockMenu(id)
{
	new col[3];
	new szMenu[256];
	new szGodmode[6];
	new szNoclip[6];
	new szSize[8];
	col = (get_user_flags(id) & BM_ADMIN_LEVEL ? "\w" : "\d");
	szNoclip = (get_user_noclip(id) ? "\yOn" : "\rOff");
	szGodmode = (get_user_godmode(id) ? "\yOn" : "\rOff");
	
	switch (gBlockSize[id])
	{
		case SMALL: szSize = "Small";
		case NORMAL: szSize = "Normal";
		case LARGE: szSize = "Large";
		case POLE: szSize = "Pole";
	}
	
	//format the main menu
	format(szMenu, 256, gszBlockMenu, gszBlockNames[gSelectedBlockType[id]], col, col, col, col, col, szNoclip, col, szGodmode, szSize);
	
	//show the block menu to the player
	show_menu(id, gKeysBlockMenu, szMenu, -1, "bmBlockMenu");
	
	return PLUGIN_HANDLED;
}

showBlockSelectionMenu(id)
{
	//create block selection menu 1 (first 8 blocks)
	new szBlockMenu[256];
	new szTitle[32];
	new szEntry[32];
	new num;
	new startBlock;
	
	//format the page number into the menu title
	format(szTitle, sizeof(szTitle), "\yBlock Selection %d^n^n", gBlockMenuPage[id]);
	
	//add the title to the menu
	add(szBlockMenu, sizeof(szBlockMenu), szTitle);
	
	//calculate the block that the menu will start on
	startBlock = (gBlockMenuPage[id] - 1) * 8;
	
	//iterate through 8 blocks to add to the menu
	for (new i = startBlock; i < startBlock + 8; ++i)
	{
		//make sure the loop doesn't go above the maximum number of blocks
		if (i < gBlockMax)
		{
			//calculate the menu item number
			num = (i - startBlock) + 1;
			
			//format the block name into the menu entry
			format(szEntry, sizeof(szEntry), "\r%d. \w%s^n", num, gszBlockNames[i]);
		}
		else
		{
			//format a blank menu entry
			format(szEntry, sizeof(szEntry), "^n");
		}
		
		//add the entry to the menu
		add(szBlockMenu, sizeof(szBlockMenu), szEntry);
	}
	
	//if the block selection page the player is on is less than the maximum page
	if (gBlockMenuPage[id] < gBlockMenuPagesMax)
	{
		add(szBlockMenu, sizeof(szBlockMenu), "^n\r9. \wMore");
	}
	else
	{
		add(szBlockMenu, sizeof(szBlockMenu), "^n");
	}
	
	//add a back option to the menu
	add(szBlockMenu, sizeof(szBlockMenu), "^n\r0. \wBack");
	
	//display the block selection menu
	show_menu(id, gKeysBlockSelectionMenu, szBlockMenu, -1, "bmBlockSelectionMenu");
}

showTeleportMenu(id)
{
	new col[3];
	new szMenu[256];
	new szGodmode[6];
	new szNoclip[6];
	col = (get_user_flags(id) & BM_ADMIN_LEVEL ? "\w" : "\d");
	szNoclip = (get_user_noclip(id) ? "\yOn" : "\rOff");
	szGodmode = (get_user_godmode(id) ? "\yOn" : "\rOff");
	
	//format teleport menu
	format(szMenu, sizeof(szMenu), gszTeleportMenu, col, (gTeleportStart[id] ? "\w" : "\d"), col, col, col, col, szNoclip, col, szGodmode);
	
	show_menu(id, gKeysTeleportMenu, szMenu, -1, "bmTeleportMenu");
}

showOptionsMenu(id, oldMenu)
{
	//set the oldmenu global variable so when the back key is pressed it goes back to the previous menu
	gMenuBeforeOptions[id] = oldMenu;
	
	new col[3];
	new szSnapping[6];
	new szMenu[256];
	col = (get_user_flags(id) & BM_ADMIN_LEVEL ? "\w" : "\d");
	szSnapping = (gbSnapping[id] ? "\yOn" : "\rOff");
	
	//format the options menu
	format(szMenu, sizeof(szMenu), gszOptionsMenu, col, szSnapping, col, gfSnappingGap[id], col, col, col, col, col, col);
	
	//show the options menu to the player
	show_menu(id, gKeysOptionsMenu, szMenu, -1, "bmOptionsMenu");
}

showChoiceMenu(id, gChoice, const szTitle[96])
{
	gChoiceOption[id] = gChoice;
	
	//format choice menu using given title
	new szMenu[128];
	format(szMenu, sizeof(szMenu), gszChoiceMenu, szTitle);
	
	//show the choice menu to the player
	show_menu(id, gKeysChoiceMenu, szMenu, -1, "bmChoiceMenu");
}

public handleMainMenu(id, num)
{
	switch (num)
	{
		case N1: { showBlockMenu(id); }
		case N2: { showTeleportMenu(id); }
		case N6: { toggleNoclip(id); }
		case N7: { toggleGodmode(id); }
		case N9: { showOptionsMenu(id, 1); }
		case N0: { return; }
	}
	
	//selections 1, 2, 3, 4, 5 and 9 show different menus
	if (num != N1 && num != N2 && num != N3 && num != N4 && num!= N5 && num != N9)
	{
		//display menu again
		showMainMenu(id);
	}
}

public handleBlockMenu(id, num)
{
	switch (num)
	{
		case N1: { showBlockSelectionMenu(id); }
		case N2: { createBlockAiming(id, gSelectedBlockType[id]); }
		case N3: { convertBlockAiming(id, gSelectedBlockType[id]); }
		case N4: { deleteBlockAiming(id); }
		case N5: { rotateBlockAiming(id); }
		case N6: { toggleNoclip(id); }
		case N7: { toggleGodmode(id); }
		case N8: { changeBlockSize(id); }
		case N9: { showOptionsMenu(id, 2); }
		case N0: { showMainMenu(id); }
	}
	
	//selections 1, 9 and 0 show different menus
	if (num != N1 && num != N9 && num != N0)
	{
		//display menu again
		showBlockMenu(id);
	}
}

public handleBlockSelectionMenu(id, num)
{
	switch (num)
	{
		case N9:
		{
			//goto next block selection menu page
			++gBlockMenuPage[id];
			
			//make sure the player can't go above the maximum block selection page
			if (gBlockMenuPage[id] > gBlockMenuPagesMax)
			{
				gBlockMenuPage[id] = gBlockMenuPagesMax;
			}
			
			//show block selection menu again
			showBlockSelectionMenu(id);
		}
		
		case N0:
		{
			//goto previous block selection menu page
			--gBlockMenuPage[id];
			
			//show block menu if player goes back too far
			if (gBlockMenuPage[id] < 1)
			{
				showBlockMenu(id);
				gBlockMenuPage[id] = 1;
			}
			else
			{
				//show block selection menu again
				showBlockSelectionMenu(id);
			}
		}
		
		default:
		{
			//offset the num value using the players block selection page number
			num += (gBlockMenuPage[id] - 1) * 8;
			
			//if block number is within range
			if (num < gBlockMax)
			{
				gSelectedBlockType[id] = num;
				showBlockMenu(id);
			}
			else
			{
				showBlockSelectionMenu(id);
			}
		}
	}
}

public handleTeleportMenu(id, num)
{
	switch (num)
	{
		case N1: { createTeleportAiming(id, TELEPORT_START); }
		case N2: { createTeleportAiming(id, TELEPORT_END); }
		case N3: { swapTeleportAiming(id); }
		case N4: { deleteTeleportAiming(id); }
		case N5: { showTeleportPath(id); }
		case N6: { toggleNoclip(id); }
		case N7: { toggleGodmode(id); }
		case N9: { showOptionsMenu(id, 3); }
		case N0: { showMainMenu(id); }
	}
	
	//selections 9 and 0 show different menus
	if (num != N9 && num != N0)
	{
		showTeleportMenu(id);
	}
}

public handleOptionsMenu(id, num)
{
	switch (num)
	{
		case N1: { toggleSnapping(id); }
		case N2: { toggleSnappingGap(id); }
		case N3: { groupBlockAiming(id); }
		case N4: { groupClear(id); }
		case N5: { showChoiceMenu(id, CHOICE_DEL_BLOCKS, "Are you sure you want to erase all blocks on the map?"); }
		case N6: { showChoiceMenu(id, CHOICE_DEL_TELEPORTS, "Are you sure you want to erase all teleports on the map?"); }
		case N7: { saveBlocks(id); }
		case N8: { showChoiceMenu(id, CHOICE_LOAD, "Loading will erase all blocks and teleports, do you want to continue?"); }
		case N9: { showHelp(id); }
		
		case N0:  //back to previous menu
		{
			switch (gMenuBeforeOptions[id])
			{
				case 1: showMainMenu(id);
				case 2: showBlockMenu(id);
				case 3: showTeleportMenu(id);
				
				//for some reason the players 'gMenuBeforeOptions' number is invalid
				default: log_amx("%sPlayer ID: %d has an invalid gMenuBeforeOptions: %d", gszPrefix, id, gMenuBeforeOptions[id]);
			}
		}
	}
	
	//these selections show a different menu
	if (num != N5 && num != N6 && num != N8 && num != N0)
	{
		//display menu again
		showOptionsMenu(id, gMenuBeforeOptions[id]);
	}
}

public handleChoiceMenu(id, num)
{
	switch (num)
	{
		case N1:	//YES
		{
			switch (gChoiceOption[id])
			{
				case CHOICE_LOAD: loadBlocks(id);
				case CHOICE_DEL_BLOCKS: deleteAllBlocks(id, true);
				case CHOICE_DEL_TELEPORTS: deleteAllTeleports(id, true);
				
				default:
				{
					log_amx("%sInvalid choice in handleChoiceMenu()", gszPrefix);
				}
			}
		}
	}
	
	//show options menu again
	showOptionsMenu(id, gMenuBeforeOptions[id]);
}

toggleGodmode(id)
{
    new szPlayerName[32];
    get_user_name(id, szPlayerName, 32);
    
    //make sure player has access to this command
    if (get_user_flags(id) & BM_ADMIN_LEVEL)
    {
        //if player has godmode
        if (get_user_godmode(id))
        {
            //turn off godmode for player
            set_user_godmode(id, 0);
            gbAdminGodmode[id] = false;
            
            ColorChat(0, GREEN, "^x03[staNioN-MM]^x04 %s^x01 Has^x04 Disabled^x01 godmode!", szPlayerName);
        }
        else
        {
            //turn on godmode for player
            set_user_godmode(id, 1);
            gbAdminGodmode[id] = true;
            
            ColorChat(0, TEAM_COLOR, "^x03[staNioN-MM]^x04 %s^x01 Has^x04 Enabled^x01 godmode!", szPlayerName);
        }
    }
}

toggleNoclip(id)
{
    new szPlayerName[32];
    get_user_name(id, szPlayerName, 32);
    
    //make sure player has access to this command
    if (get_user_flags(id) & BM_ADMIN_LEVEL)
    {
        //if player has noclip
        if (get_user_noclip(id))
        {
            //turn off noclip for player
            set_user_noclip(id, 0);
            gbAdminNoclip[id] = false;
            
            ColorChat(0, GREEN, "^x03[staNioN-MM]^x04 %s^x01 Has^x04 Disabled^x01 noclip!", szPlayerName);
        }
        else
        {
            //turn on noclip for player
            set_user_noclip(id, 1);
            gbAdminNoclip[id] = true;
            
            ColorChat(0, GREEN, "^x03[staNioN-MM]^x04 %s^x01 Has^x04 Enabled^x01 noclip!", szPlayerName);
        }
    }
}



changeBlockSize(id)
{
	switch (gBlockSize[id])
	{
		case SMALL: gBlockSize[id] = NORMAL;
		case NORMAL: gBlockSize[id] = LARGE;
		case LARGE: gBlockSize[id] = POLE;
		case POLE: gBlockSize[id] = SMALL;
	}
}

toggleSnapping(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		gbSnapping[id] = !gbSnapping[id];
	}
}

toggleSnappingGap(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//increment this players snapping gap by 5
		gfSnappingGap[id] += 4.0;
		
		//if this players snapping gap gets too big then loop it back to 0
		if (gfSnappingGap[id] > 40.0)
		{
			gfSnappingGap[id] = 0.0;
		}
	}
}

showHelp(id)
{
	//get cvar values
	new szHelpText[1600];
	
	new Telefrags = get_cvar_num("bm_telefrags");
	new Float:fFireDamageAmount = get_cvar_float("bm_firedamageamount");
	new Float:fDamageAmount = get_cvar_float("bm_damageamount");
	new Float:fHealAmount = get_cvar_float("bm_healamount");
	new Float:fInvincibleTime = get_cvar_float("bm_invincibletime");
	new Float:fInvincibleCooldown = get_cvar_float("bm_invinciblecooldown");
	new Float:fStealthTime = get_cvar_float("bm_stealthtime");
	new Float:fStealthCooldown = get_cvar_float("bm_stealthcooldown");
	new Float:fCamouflageTime = get_cvar_float("bm_camouflagetime");
	new Float:fCamouflageCooldown = get_cvar_float("bm_camouflagecooldown");
	new Float:fRandomCooldown = get_cvar_float("bm_randomcooldown");
	new Float:fBootsOfSpeedTime = get_cvar_float("bm_bootsofspeedtime");
	new Float:fBootsOfSpeedCooldown = get_cvar_float("bm_bootsofspeedcooldown");
	new TeleportSound = get_cvar_num("bm_teleportsound");
	
	//format the help text
	format(szHelpText, sizeof(szHelpText), gszHelpText, Telefrags, fFireDamageAmount, fDamageAmount, fHealAmount, fInvincibleTime, fInvincibleCooldown, fStealthTime, fStealthCooldown, fCamouflageTime, fCamouflageCooldown, fRandomCooldown, fBootsOfSpeedTime, fBootsOfSpeedCooldown, TeleportSound);
	
	//show the help
	show_motd(id, szHelpText, gszHelpTitle);
}

showTeleportPath(id)
{
	//get the entity the player is aiming at
	new ent, body;
	get_user_aiming(id, ent, body);
	
	//if entity found is a teleport
	if (isTeleport(ent))
	{
		//get other side of teleport
		new tele = entity_get_int(ent, EV_INT_iuser1);
		
		//if there is another end to the teleport
		if (tele)
		{
			//get origins of the start and end teleport entities
			new life = 50;
			new Float:vOrigin1[3];
			new Float:vOrigin2[3];
			entity_get_vector(ent, EV_VEC_origin, vOrigin1);
			entity_get_vector(tele, EV_VEC_origin, vOrigin2);
			
			//draw a line in between the 2 origins
			drawLine(vOrigin1, vOrigin2, life);
			
			//get the distance between the points
			new Float:fDist = get_distance_f(vOrigin1, vOrigin2);
			
			//notify that a line has been drawn between the start and end of the teleport
			ColorChat(id, TEAM_COLOR, "^4%s ^3A line has been ^4drawn ^3to show the ^4teleport path. ^3Distance: ^4%f ^3units.", gszPrefix, fDist);
		}
	}
}

/* GROUPING BLOCKS */
groupBlockAiming(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get the entity the player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body);
		
		//is entity is a block
		if (isBlock(ent))
		{
			//get whether or not block is already being grouped
			new player = entity_get_int(ent, EV_INT_iuser1);
			
			//if block is not in a group
			if (player == 0)
			{
				//increment group value
				++gGroupCount[id];
				
				//add this entity to the players group
				gGroupedBlocks[id][gGroupCount[id]] = ent;
				
				//set the block so it is now 'being grouped'
				groupBlock(id, ent);
				
			}
			//if block is in this players group
			else if (player == id)
			{
				//remove block from being grouped
				groupRemoveBlock(ent);
			}
			//if another player has the block grouped
			else
			{
				//get id and name of who has the block grouped
				new szName[32];
				new player = entity_get_int(ent, EV_INT_iuser1);
				get_user_name(player, szName, 32);
				
				//notify player who the block is being grouped by
				ColorChat(id, TEAM_COLOR, "^4%s ^3Block is ^4already ^3in a group by: ^4%s", gszPrefix, szName);
			}
		}
	}
}

groupBlock(id, ent)
{
	//if entity is valid
	if (is_valid_ent(ent))
	{
		//if id passed in is a player
		if (id > 0 && id <= 32)
		{
			//set block so it is now being grouped
			entity_set_int(ent, EV_INT_iuser1, id);
		}
		
		//make block glow red to show it is grouped
		set_rendering(ent, kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 16);
	}
}

groupRemoveBlock(ent)
{
	//make sure entity is a block
	if (isBlock(ent))
	{
		//remove block from being grouped (stays in players gGroupedBlocks[id][] array
		entity_set_int(ent, EV_INT_iuser1, 0);
		
		//get block type
		new blockType = entity_get_int(ent, EV_INT_body);
		
		//set rendering on block
		set_block_rendering(ent, gRender[blockType], gRed[blockType], gGreen[blockType], gBlue[blockType], gAlpha[blockType]);
	}
}

groupClear(id)
{
	new blockCount = 0;
	new blocksDeleted = 0;
	new block;
	
	//remove all players blocks from being grouped
	for (new i = 0; i <= gGroupCount[id]; ++i)
	{
		block = gGroupedBlocks[id][i];
		
		//if block is in this players group
		if (isBlockInGroup(id, block))
		{
			//if block is stuck
			if (isBlockStuck(block))
			{
				//delete the stuck block
				deleteBlock(block);
				
				//count how many blocks have been deleted
				++blocksDeleted;
			}
			else
			{
				//remove block from being grouped
				groupRemoveBlock(block);
				
				//count how many blocks have been removed from group
				++blockCount;
			}
		}
	}
	
	//set players group count to 0
	gGroupCount[id] = 0;
	
	//if player is connected
	if (is_user_connected(id))
	{
		//if some blocks were deleted
		if (blocksDeleted > 0)
		{
			//notify player how many blocks were cleared from group and deleted
			ColorChat(id, TEAM_COLOR, "^4%s ^3Removed ^4%d ^3blocks from group, deleted ^4%d stuck ^3blocks", gszPrefix, blockCount, blocksDeleted);
		}
		else
		{
			//notify player how many blocks were cleared from group
			ColorChat(id, TEAM_COLOR, "^4%s ^3Removed ^4%d ^3blocks from group", gszPrefix, blockCount);
		}
	}
}

/* BLOCK & TELEPORT OPERATIONS */
moveGrabbedEntity(id, Float:vMoveTo[3] = {0.0, 0.0, 0.0})
{
	new iOrigin[3], iLook[3];
	new Float:fOrigin[3], Float:fLook[3], Float:fDirection[3], Float:fLength;
	
	get_user_origin(id, iOrigin, 1);		//Position from eyes (weapon aiming)
	get_user_origin(id, iLook, 3);			//End position from eyes (hit point for weapon)
	IVecFVec(iOrigin, fOrigin);
	IVecFVec(iLook, fLook);
	
	fDirection[0] = fLook[0] - fOrigin[0];
	fDirection[1] = fLook[1] - fOrigin[1];
	fDirection[2] = fLook[2] - fOrigin[2];
	fLength = get_distance_f(fLook, fOrigin);
	
	if (fLength == 0.0) fLength = 1.0;		//avoid division by 0
	
	//calculate the position to move the block
	vMoveTo[0] = (fOrigin[0] + fDirection[0] * gfGrablength[id] / fLength) + gvGrabOffset[id][0];
	vMoveTo[1] = (fOrigin[1] + fDirection[1] * gfGrablength[id] / fLength) + gvGrabOffset[id][1];
	vMoveTo[2] = (fOrigin[2] + fDirection[2] * gfGrablength[id] / fLength) + gvGrabOffset[id][2];
	vMoveTo[2] = float(floatround(vMoveTo[2], floatround_floor));
	
	//move the block and its sprite (if it has one)
	moveEntity(id, gGrabbed[id], vMoveTo, true);
}

moveEntity(id, ent, Float:vMoveTo[3], bool:bDoSnapping)
{
	//if entity is a block
	if (isBlock(ent))
	{
		//do snapping for entity if snapping boolean passed in is true
		if (bDoSnapping)
		{
			doSnapping(id, ent, vMoveTo);
		}
		
		//set the position of the block
		entity_set_origin(ent, vMoveTo);
		
		//get the sprite that sits above the block (if any)
		new sprite = entity_get_int(ent, EV_INT_iuser3);
		
		//if sprite entity is valid
		if (sprite)
		{
			//get size of block
			new Float:vSizeMax[3];
			entity_get_vector(ent, EV_VEC_maxs, vSizeMax);
			
			//move the sprite onto the top of the block
			vMoveTo[2] += vSizeMax[2] + 0.15;
			entity_set_origin(sprite, vMoveTo);
		}
	}
	else
	{
		//set the position of the entity
		entity_set_origin(ent, vMoveTo);
	}
}

/* TELEPORTS */
createTeleportAiming(const id, const teleportType)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get where player is aiming for origin of teleport entity
		new pOrigin[3], Float:vOrigin[3];
		get_user_origin(id, pOrigin, 3);
		IVecFVec(pOrigin, vOrigin);
		vOrigin[2] += gfTeleportZOffset;
		
		//create the teleport of the given type
		createTeleport(id, teleportType, vOrigin);
	}
}

createTeleport(const id, const teleportType, Float:vOrigin[3])
{
	new ent = create_entity(gszInfoTarget);
	
	if (is_valid_ent(ent))
	{
		switch (teleportType)
		{
			case TELEPORT_START:
			{
				//if player has already created a teleport start entity then delete it
				if (gTeleportStart[id])
				{
					remove_entity(gTeleportStart[id]);
				}
				
				//set teleport properties
				entity_set_string(ent, EV_SZ_classname, gszTeleportStartClassname);
				entity_set_int(ent, EV_INT_solid, SOLID_BBOX);
				entity_set_int(ent, EV_INT_movetype, MOVETYPE_NONE);
				entity_set_model(ent, gszTeleportSpriteStart);
				entity_set_size(ent, gfTeleportSizeMin, gfTeleportSizeMax);
				entity_set_origin(ent, vOrigin);
				
				//set the rendermode and transparency
				entity_set_int(ent, EV_INT_rendermode, 5);	//rendermode
				entity_set_float(ent, EV_FL_renderamt, 255.0);	//visable 
				
				//set task for animating sprite
				new params[2];
				params[0] = ent;
				params[1] = gTeleportStartFrames;
				set_task(0.1, "taskSpriteNextFrame", TASK_SPRITE + ent, params, 2, "b");
				
				//store teleport start entity to a global variable so it can be linked to the end entity 
				gTeleportStart[id] = ent;
			}
			
			case TELEPORT_END:
			{
				//make sure there is a teleport start entity
				if (gTeleportStart[id])
				{
					//set teleport properties
					entity_set_string(ent, EV_SZ_classname, gszTeleportEndClassname);
					entity_set_int(ent, EV_INT_solid, SOLID_BBOX);
					entity_set_int(ent, EV_INT_movetype, MOVETYPE_NONE);
					entity_set_model(ent, gszTeleportSpriteEnd);
					entity_set_size(ent, gfTeleportSizeMin, gfTeleportSizeMax);
					entity_set_origin(ent, vOrigin);
					
					//set the rendermode and transparency
					entity_set_int(ent, EV_INT_rendermode, 5);	//rendermode
					entity_set_float(ent, EV_FL_renderamt, 255.0);	//visable 
					
					//link up teleport start and end entities
					entity_set_int(ent, EV_INT_iuser1, gTeleportStart[id]);
					entity_set_int(gTeleportStart[id], EV_INT_iuser1, ent);
					
					//set task for animating sprite
					new params[2];
					params[0] = ent;
					params[1] = gTeleportEndFrames;
					set_task(0.1, "taskSpriteNextFrame", TASK_SPRITE + ent, params, 2, "b");
					
					//indicate that this player has no teleport start entity waiting for an end
					gTeleportStart[id] = 0;
				}
				else
				{
					//delete entity that was created because there is no start entity
					remove_entity(ent);
				}
			}
		}
	}
	else
	{
		log_amx("%sCouldn't create 'env_sprite' entity", gszPrefix);
	}
}

swapTeleportAiming(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get entity that player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body, 9999);
		
		//swap teleport start and destination
		if (isTeleport(ent))
		{
			swapTeleport(id, ent);
		}
	}
}

swapTeleport(id, ent)
{
	new Float:vOriginEnt[3];
	new Float:vOriginTele[3];
	
	//get the other end of the teleport
	new tele = entity_get_int(ent, EV_INT_iuser1);
	
	//if the teleport has another side
	if (is_valid_ent(tele))
	{
		//get teleport properties
		entity_get_vector(ent, EV_VEC_origin, vOriginEnt);
		entity_get_vector(tele, EV_VEC_origin, vOriginTele);
		
		new szClassname[32];
		entity_get_string(ent, EV_SZ_classname, szClassname, 32);
		
		//delete old teleport
		deleteTeleport(id, ent);
		
		//create new teleport at opposite positions
		if (equal(szClassname, gszTeleportStartClassname))
		{
			createTeleport(id, TELEPORT_START, vOriginTele);
			createTeleport(id, TELEPORT_END, vOriginEnt);
		}
		else if (equal(szClassname, gszTeleportEndClassname))
		{
			createTeleport(id, TELEPORT_START, vOriginEnt);
			createTeleport(id, TELEPORT_END, vOriginTele);
		}
	}
	else
	{
		//tell player they cant swap because its only 1 sided
		ColorChat(0, TEAM_COLOR, "%s^3Can't ^4swap ^3teleport positions", gszPrefix);
	}
}

deleteTeleportAiming(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get entity that player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body, 9999);
		
		//delete block that player is aiming at
		new bool:deleted = deleteTeleport(id, ent);
		
		if (deleted)
		{
			ColorChat(0, TEAM_COLOR, "^4%s^3Teleport ^4deleted!", gszPrefix);
		}
	}
}

bool:deleteTeleport(id, ent)
{
	//if entity is a teleport then delete both the start and the end of the teleport
	if (isTeleport(ent))
	{
		//get entity id of the other side of the teleport
		new tele = entity_get_int(ent, EV_INT_iuser1);
		
		//clear teleport start entity if it was just deleted
		if (gTeleportStart[id] == ent || gTeleportStart[id] == tele)
		{
			gTeleportStart[id] = 0;
		}
		
		//remove tasks that exist to animate the teleport sprites
		if (task_exists(TASK_SPRITE + ent))
		{
			remove_task(TASK_SPRITE + ent);
		}
		
		if (task_exists(TASK_SPRITE + tele))
		{
			remove_task(TASK_SPRITE + tele);
		}
		
		//delete both the start and end positions of the teleporter
		if (tele)
		{
			remove_entity(tele);
		}
		
		remove_entity(ent);
		
		//delete was deleted
		return true;
	}
	
	//teleport was not deleted
	return false;
}

/* OPTIONS */
deleteAllBlocks(id, bool:bNotify)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		new bool:bDeleted;
		new blockCount = 0;
		new ent = -1;
		
		//find all blocks in the map
		while ((ent = find_ent_by_class(ent, gszBlockClassname)))
		{
			//delete the block
			bDeleted = deleteBlock(ent);
			
			//if block was successfully deleted
			if (bDeleted)
			{
				//increment counter for how many blocks have been deleted
				++blockCount;
			}
		}
		
		//if some blocks were deleted
		if (blockCount > 0)
		{
			//get players name
			new szName[32];
			get_user_name(id, szName, 32);
			
			//iterate through all players
			for (new i = 1; i <= 32; ++i)
			{
				//make sure nobody is grabbing a block because they've all been deleted!
				gGrabbed[id] = 0;
				
				//make sure player is connected
				if (is_user_connected(i))
				{
					//notify all admins that the player deleted all the blocks
					if (bNotify && get_user_flags(i) & BM_ADMIN_LEVEL)
					{
						ColorChat(0, GREEN, "^x03%s^x04'%s'^x01 deleted all the^x04 blocks^x01 from the map. Total blocks: ^x03%d", gszPrefix, szName, blockCount);
					}
				}
			}
		}
	}
}

deleteAllTeleports(id, bool:bNotify)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		new bool:bDeleted;
		new teleCount = 0;
		new ent = -1;
		
		//find all teleport start entities in the map
		while ((ent = find_ent_by_class(ent, gszTeleportStartClassname)))
		{
			//delete the teleport
			bDeleted = deleteTeleport(id, ent);
			
			//if teleport was successfully deleted
			if (bDeleted)
			{
				//increment counter for how many teleports have been deleted
				++teleCount;
			}
		}
		
		//if some teleports were deleted
		if (teleCount > 0)
		{
			//get players name
			new szName[32];
			get_user_name(id, szName, 32);
			
			//iterate through all players
			for (new i = 1; i <= 32; ++i)
			{
				//make sure nobody has a teleport start set
				gTeleportStart[id] = 0;
				
				//make sure player is connected
				if (is_user_connected(i))
				{
					//notify all admins that the player deleted all the teleports
					if (bNotify && get_user_flags(i) & BM_ADMIN_LEVEL)
					{
						ColorChat(0, GREEN, "^x03%s^x04'%s'^x01 deleted all the ^x04teleports^x01 from the map. Total teleports: ^x03%d", gszPrefix, szName, teleCount);
					}
				}
			}
		}
	}
}

/***** BLOCKS *****/
createBlockAiming(const id, const blockType)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		new origin[3];
		new Float:vOrigin[3];
		new szCreator[32];
		
		get_user_name(id, szCreator, 31);
		replace_all(szCreator, 31, " ", "_");
		
		//get the origin of the player and add Z offset
		get_user_origin(id, origin, 3);
		IVecFVec(origin, vOrigin);
		vOrigin[2] += gfBlockSizeMaxForZ[2];
		
		//create the block
		createBlock(id, blockType, vOrigin, Z, gBlockSize[id], szCreator);
	}
}

createBlock(const id, const blockType, Float:vOrigin[3], const axis, const size, szCreator[] = "Unknown")
{
	new ent = create_entity(gszInfoTarget);
	
	//make sure entity was created successfully
	if (is_valid_ent(ent))
	{
		//set block properties
		entity_set_string(ent, EV_SZ_classname, gszBlockClassname);
		entity_set_int(ent, EV_INT_solid, SOLID_BBOX);
		entity_set_int(ent, EV_INT_movetype, MOVETYPE_NONE);
		
		new Float:vSizeMin[3];
		new Float:vSizeMax[3];
		new Float:vAngles[3];
		new Float:fScale;
		new szBlockModel[256];
		
		//set mins, maxs and angles depending on axis
		switch (axis)
		{
			case X:
			{
				if (size == POLE) {
					vSizeMin = gfPoleBlockSizeMinForX;
					vSizeMax = gfPoleBlockSizeMaxForX;
				} else {
					vSizeMin = gfBlockSizeMinForX;
					vSizeMax = gfBlockSizeMaxForX;
				}
				
				vAngles[0] = 90.0;
			}
			
			case Y:
			{
				if (size == POLE) {
					vSizeMin = gfPoleBlockSizeMinForY;
					vSizeMax = gfPoleBlockSizeMaxForY;
				} else {
					vSizeMin = gfBlockSizeMinForY;
					vSizeMax = gfBlockSizeMaxForY;
				}
				
				vAngles[0] = 90.0;
				vAngles[2] = 90.0;
			}
			
			case Z:
			{
				if (size == POLE) {
					vSizeMin = gfPoleBlockSizeMinForZ;
					vSizeMax = gfPoleBlockSizeMaxForZ;
				} else {
					vSizeMin = gfBlockSizeMinForZ;
					vSizeMax = gfBlockSizeMaxForZ;
				}
				
				vAngles = gfDefaultBlockAngles;
			}
		}
		
		//set block model name and scale depending on size
		switch (size)
		{
			case SMALL:
			{
				setBlockModelNameSmall(szBlockModel, gszBlockModels[blockType], 256);
				fScale = SCALE_SMALL;
			}
			
			case NORMAL:
			{
				szBlockModel = gszBlockModels[blockType];
				fScale = SCALE_NORMAL;
			}
			
			case LARGE:
			{
				setBlockModelNameLarge(szBlockModel, gszBlockModels[blockType], 256);
				fScale = SCALE_LARGE;
			}
			
			case POLE:
			{
				setBlockModelNamePole(szBlockModel, gszBlockModels[blockType], 256);
			}
		}
		
		//adjust size min/max vectors depending on scale
		if (size != POLE) {
			for (new i = 0; i < 3; ++i)
			{
				if (vSizeMin[i] != 4.0 && vSizeMin[i] != -4.0)
				{
					vSizeMin[i] *= fScale;
				}
				
				if (vSizeMax[i] != 4.0 && vSizeMax[i] != -4.0)
				{
					vSizeMax[i] *= fScale;
				}
			}
		}
		
		//if it's a valid block type
		if (blockType >= 0 && blockType < gBlockMax)
		{
			entity_set_model(ent, szBlockModel);
		}
		else
		{
			entity_set_model(ent, gszBlockModelDefault);
		}
		
		entity_set_vector(ent, EV_VEC_angles, vAngles);
		entity_set_size(ent, vSizeMin, vSizeMax);
		entity_set_int(ent, EV_INT_body, blockType);
		
		//if a player is creating the block
		if (id > 0 && id <= 32)
		{
			//do snapping for new block
			doSnapping(id, ent, vOrigin);
		}
		
		//set origin of new block
		entity_set_origin(ent, vOrigin);
		
		// Set creator
		set_pev(ent, pev_targetname, szCreator, 31);
		
		//setup special properties for the random block
		if (blockType == BM_RANDOM)
		{
			//set this random block to a random block!
			new randNum = random_num(0, gRandomBlocksMax - 1);
			entity_set_int(ent, EV_INT_iuser4, gRandomBlocks[randNum]);
		}
		
		//set rendering on block
		set_block_rendering(ent, gRender[blockType], gRed[blockType], gGreen[blockType], gBlue[blockType], gAlpha[blockType]);
		
		//if blocktype is one which requires an additional sprite
		if (blockType == BM_FIRE)
		{
			//add sprite on top of the block
			new sprite = create_entity(gszInfoTarget);
			
			//make sure entity was created successfully
			if (sprite)
			{
				//create angle vector and rotate it so its horizontal
				new Float:vAngles[3];
				vAngles[0] = 90.0;
				vAngles[1] = 0.0;
				vAngles[2] = 0.0;
				
				//move the sprite up onto the top of the block, adding 0.15 to prevent flickering
				vOrigin[2] += vSizeMax[2] + 0.15;
				
				//set block properties
				entity_set_string(sprite, EV_SZ_classname, gszSpriteClassname);
				entity_set_int(sprite, EV_INT_solid, SOLID_NOT);
				entity_set_int(sprite, EV_INT_movetype, MOVETYPE_NONE);
				entity_set_vector(sprite, EV_VEC_angles, vAngles);
				
				//set the rendermode to additive and set the transparency
				entity_set_int(sprite, EV_INT_rendermode, 5);
				entity_set_float(sprite, EV_FL_renderamt, 255.0);
				
				//set origin of new sprite
				entity_set_origin(sprite, vOrigin);
				
				//link this sprite to the block
				entity_set_int(ent, EV_INT_iuser3, sprite);
				
				//set task for animating the sprite
				if (blockType == BM_FIRE)
				{
					new params[2];
					params[0] = sprite;
					params[1] = 8;		//both the fire and trampoline sprites have 8 frames
					set_task(0.1, "taskSpriteNextFrame", TASK_SPRITE + sprite, params, 2, "b");
				}
			}
		}
		
		return ent;
	}
	
	return 0;
}

convertBlockAiming(id, const convertTo)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get entity that player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body);
		
		//if player is aiming at a block
		if (isBlock(ent))
		{
			//get who is currently grabbing the block (if anyone)
			new grabber = entity_get_int(ent, EV_INT_iuser2);
			
			//if entity is not being grabbed by someone else
			if (grabber == 0 || grabber == id)
			{
				//get the player ID of who has the block in a group (if anyone)
				new player = entity_get_int(ent, EV_INT_iuser1);
				
				//if the block is not in a group or is in this players group
				if (player == 0 || player == id)
				{
					new newBlock;
					
					//if block is in the players group and group count is larger than 1
					if (isBlockInGroup(id, ent) && gGroupCount[id] > 1)
					{
						new block;
						new blockCount = 0;
						
						//iterate through all blocks in the players group
						for (new i = 0; i <= gGroupCount[id]; ++i)
						{
							block = gGroupedBlocks[id][i];
							
							//if this block is in this players group
							if (isBlockInGroup(id, block))
							{
								//convert the block
								newBlock = convertBlock(id, block, convertTo, true);
								
								//if block was converted
								if (newBlock != 0)
								{
									//new block is now in the group
									gGroupedBlocks[id][i] = newBlock;
									
									//set the block so it is now 'being grouped'
									groupBlock(id, newBlock);
								}
								//count how many blocks could NOT be converted
								else
								{
									++blockCount;
								}
							}
						}
						
						//if some blocks could NOT be converted
						if (blockCount > 1)
						{
							ColorChat(id, TEAM_COLOR, "^4%s ^3Couldn't convert ^4%d ^3blocks!", gszPrefix, blockCount);
						}
					}
					else
					{
						new szCreator[32];
						get_user_name(id, szCreator, 31);
						replace_all(szCreator, 31, " ", "_");
						
						newBlock = convertBlock(id, ent, convertTo, false, szCreator);
						
						//if block was not converted
						if (newBlock == 0)
						{
							//get the block type
							new blockType = entity_get_int(ent, EV_INT_body);
							
							ColorChat(id, TEAM_COLOR, "^4%s ^3You cannot ^4convert ^3a ^4%s ^3block into a ^4%s ^3block while it is ^4rotated!", gszPrefix, gszBlockNames[blockType], gszBlockNames[convertTo]);
						}
					}
				}
				else
				{
					//get name of player who has this block in their group
					new szName[32]; 
					get_user_name(player, szName, 32);
					
					//notify player who has this block in their group
					ColorChat(id, TEAM_COLOR, "^4%s ^4%s ^3currently has this block in their ^4group!", gszPrefix, szName);
				}
			}
		}
	}
}

convertBlock(id, ent, const convertTo, const bool:bPreserveSize, szCreator[] = "Unknown")
{
	new blockType;
	new Float:vOrigin[3];
	new Float:vSizeMax[3];
	new axis;
	
	//get block information from block player is aiming at
	blockType = entity_get_int(ent, EV_INT_body);
	entity_get_vector(ent, EV_VEC_origin, vOrigin);
	entity_get_vector(ent, EV_VEC_maxs, vSizeMax);
	
	//work out the axis orientation
	for (new i = 0; i < 3; ++i)
	{
		if (vSizeMax[i] == 4.0)
		{
			axis = i;
			break;
		}
	}
	
	//if block is rotated and we're trying to convert it to a block that cannot be rotated
	if ((axis == X || axis == Y) && !isBlockTypeRotatable(blockType))
	{
		return 0;
	}
	else
	{
		//delete old block and create new one of given type
		deleteBlock(ent);
		
		if (bPreserveSize)
		{
			//work out the block size
			new size = SMALL;
			new Float:fMax = vSizeMax[0] + vSizeMax[1] + vSizeMax[2];
			if (fMax > 36.0) size = POLE;
			if (fMax > 64.0) size = NORMAL;
			if (fMax > 128.0) size = LARGE;
			
			return createBlock(id, convertTo, vOrigin, axis, size, szCreator);
		}
		else
		{
			return createBlock(id, convertTo, vOrigin, axis, gBlockSize[id], szCreator);
		}
	}
	
	return ent;
}

deleteBlockAiming(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get entity that player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body);
		
		//if entity player is aiming at is a block
		if (isBlock(ent))
		{
			//get who is currently grabbing the block (if anyone)
			new grabber = entity_get_int(ent, EV_INT_iuser2);
			
			//if entity is not being grabbed by someone else
			if (grabber == 0 || grabber == id)
			{
				//get the player ID of who has the block in a group (if anyone)
				new player = entity_get_int(ent, EV_INT_iuser1);
				
				//if the block is not in a group or is in this players group
				if (player == 0 || player == id)
				{
					//if block is not being grabbed
					if (entity_get_int(ent, EV_INT_iuser2) == 0)
					{
						//if block is in the players group and group count is larger than 1
						if (isBlockInGroup(id, ent) && gGroupCount[id] > 1)
						{
							new block;
							
							//iterate through all blocks in the players group
							for (new i = 0; i <= gGroupCount[id]; ++i)
							{
								block = gGroupedBlocks[id][i];
								
								//if block is still valid
								if (is_valid_ent(block))
								{
									//get player id of who has this block in their group
									new player = entity_get_int(block, EV_INT_iuser1);
									
									//if block is still in this players group
									if (player == id)
									{
										//delete the block
										deleteBlock(block);
									}
								}
							}
						}
						else
						{
							//delete the block
							deleteBlock(ent);
						}
					}
				}
				else
				{
					//get name of player who has this block in their group
					new szName[32]; 
					get_user_name(player, szName, 32);
					
					//notify player who has this block in their group
					ColorChat(id, TEAM_COLOR, "^4%s %s ^3currently has this ^4block ^3in their ^4group!", gszPrefix, szName);
				}
			}
		}
	}
}

bool:deleteBlock(ent)
{
	//if entity is a block
	if (isBlock(ent))
	{
		//get the sprite attached to the top of the block
		new sprite = entity_get_int(ent, EV_INT_iuser3);
		
		//if sprite entity is valid
		if (sprite)
		{
			//remove the task for the animation of the sprite (if one exists)
			if (task_exists(TASK_SPRITE + sprite))
			{
				remove_task(TASK_SPRITE + sprite);
			}
			
			//delete the sprite
			remove_entity(sprite);
		}
		
		//delete the block
		remove_entity(ent);
		
		//block was deleted
		return true;
	}
	
	//block was not deleted
	return false;
}

rotateBlockAiming(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		//get block that player is aiming at
		new ent, body;
		get_user_aiming(id, ent, body);
		
		//if entity found is a block
		if (isBlock(ent))
		{
			//get who is currently grabbing the block (if anyone)
			new grabber = entity_get_int(ent, EV_INT_iuser2);
			
			//if entity is not being grabbed by someone else
			if (grabber == 0 || grabber == id)
			{
				//get the player ID of who has the block in a group (if anyone)
				new player = entity_get_int(ent, EV_INT_iuser1);
				
				//if the block is not in a group or is in this players group
				if (player == 0 || player == id)
				{
					//if block is in the players group and group count is larger than 1
					if (isBlockInGroup(id, ent) && gGroupCount[id] > 1)
					{
						new block;
						new bool:bRotateGroup = true;
						
						//iterate through all blocks in the players group
						for (new i = 0; i <= gGroupCount[id]; ++i)
						{
							block = gGroupedBlocks[id][i];
							
							//if block is in players group
							if (isBlockInGroup(id, block))
							{
								//get block type
								new blockType = entity_get_int(block, EV_INT_body);
								 
								//if block cannot be rotated
								if (!isBlockTypeRotatable(blockType))
								{
									//found a block that cannot be rotated
									bRotateGroup = false;
									
									break;
								}
							}
						}
						
						//if we can rotate the group
						if (bRotateGroup)
						{
							//iterate through all blocks in the players group
							for (new i = 0; i <= gGroupCount[id]; ++i)
							{
								block = gGroupedBlocks[id][i];
								
								//if block is still valid
								if (isBlockInGroup(id, block))
								{
									//rotate the block
									rotateBlock(block);
								}
							}
						}
						else
						{
							//notify player that their group cannot be rotated
							ColorChat(id, TEAM_COLOR, "^4%s ^3Your ^4group ^3contains blocks that cannot be ^4rotated!", gszPrefix);
						}
					}
					else
					{
						//rotate the block and get rotated block ID
						new bool:bRotatedBlock = rotateBlock(ent);
						
						//if block did not rotate successfully
						if (!bRotatedBlock)
						{
							//get block type
							new blockType = entity_get_int(ent, EV_INT_body);
							
							//notify player block couldn't rotate
							ColorChat(id, TEAM_COLOR, "^4%s %s ^3blocks cannot be ^4rotated!", gszPrefix, gszBlockNames[blockType]);
						}
					}
				}
				else
				{
					//get name of player who has this block in their group
					new szName[32]; 
					get_user_name(player, szName, 32);
					
					//notify player who has this block in their group
					ColorChat(id, TEAM_COLOR, "^4%s %s ^3currently has this block in their ^4group!", gszPrefix, szName);
				}
			}
		}
	}
}

bool:rotateBlock(ent)
{
	//if entity is valid
	if (is_valid_ent(ent))
	{
		//get block type
		new blockType = entity_get_int(ent, EV_INT_body);
		
		//if block is a type that can be rotated (a block without a sprite, makes it easier!)
		if (isBlockTypeRotatable(blockType))
		{
			new Float:vAngles[3]; 
			new Float:vSizeMin[3];
			new Float:vSizeMax[3];
			new Float:fTemp;
			
			//get block information
			entity_get_vector(ent, EV_VEC_angles, vAngles);
			entity_get_vector(ent, EV_VEC_mins, vSizeMin);
			entity_get_vector(ent, EV_VEC_maxs, vSizeMax);
			
			//create new block using current block information with new angles and sizes
			if (vAngles[0] == 0.0 && vAngles[2] == 0.0)
			{
				vAngles[0] = 90.0;
			}
			else if (vAngles[0] == 90.0 && vAngles[2] == 0.0)
			{
				vAngles[0] = 90.0;
				vAngles[2] = 90.0;
			}
			else
			{ 
				vAngles = gfDefaultBlockAngles;
			}
			
			//shift vector values along
			fTemp = vSizeMin[0];
			vSizeMin[0] = vSizeMin[2];
			vSizeMin[2] = vSizeMin[1];
			vSizeMin[1] = fTemp;
			
			fTemp = vSizeMax[0];
			vSizeMax[0] = vSizeMax[2];
			vSizeMax[2] = vSizeMax[1];
			vSizeMax[1] = fTemp;
			
			//set the blocks new angle
			entity_set_vector(ent, EV_VEC_angles, vAngles);
			
			//set the blocks new size
			entity_set_size(ent, vSizeMin, vSizeMax);
			
			return true;
		}
	}
	
	return false;
}

copyBlock(id, ent)
{
	//if entity is valid
	if (is_valid_ent(ent))
	{
		new Float:vOrigin[3];
		new Float:vAngles[3];
		new Float:vSizeMin[3];
		new Float:vSizeMax[3];
		new Float:fMax;
		new blockType;
		new size;
		new axis;
		new szCreator[32];
		
		get_user_name(id, szCreator, 31);
		replace_all(szCreator, 31, " ", "_");
		
		//get blocktype and origin of currently grabbed block
		blockType = entity_get_int(ent, EV_INT_body);
		entity_get_vector(ent, EV_VEC_origin, vOrigin);
		entity_get_vector(ent, EV_VEC_angles, vAngles);
		entity_get_vector(ent, EV_VEC_mins, vSizeMin);
		entity_get_vector(ent, EV_VEC_maxs, vSizeMax);
		
		//work out the block size
		size = SMALL;
		fMax = vSizeMax[0] + vSizeMax[1] + vSizeMax[2];
		if (fMax > 36.0) size = POLE;
		if (fMax > 64.0) size = NORMAL;
		if (fMax > 128.0) size = LARGE;
		
		if (size == POLE) {
			if (vSizeMin[0] == gfPoleBlockSizeMinForX[0] && vSizeMin[1] == gfPoleBlockSizeMinForX[1] && vSizeMin[2] == gfPoleBlockSizeMinForX[2] && vSizeMax[0] == gfPoleBlockSizeMaxForX[0] && vSizeMax[1] == gfPoleBlockSizeMaxForX[1] && vSizeMax[2] == gfPoleBlockSizeMaxForX[2]) {
				axis = X;
			} else if (vSizeMin[0] == gfPoleBlockSizeMinForY[0] && vSizeMin[1] == gfPoleBlockSizeMinForY[1] && vSizeMin[2] == gfPoleBlockSizeMinForY[2] && vSizeMax[0] == gfPoleBlockSizeMaxForY[0] && vSizeMax[1] == gfPoleBlockSizeMaxForY[1] && vSizeMax[2] == gfPoleBlockSizeMaxForY[2]) {
				axis = Y;
			} else if (vSizeMin[0] == gfPoleBlockSizeMinForZ[0] && vSizeMin[1] == gfPoleBlockSizeMinForZ[1] && vSizeMin[2] == gfPoleBlockSizeMinForZ[2] && vSizeMax[0] == gfPoleBlockSizeMaxForZ[0] && vSizeMax[1] == gfPoleBlockSizeMaxForZ[1] && vSizeMax[2] == gfPoleBlockSizeMaxForZ[2]) {
				axis = Z;
			}
		} else {
			//work out the axis orientation
			for (new i = 0; i < 3; ++i)
			{
				if (vSizeMax[i] == 4.0)
				{
					axis = i;
					break;
				}
			}
		}
		
		//create a block of the same type in the same location
		return createBlock(0, blockType, vOrigin, axis, size, szCreator);
	}
	
	return 0;
}

set_block_rendering(ent, type, red, green, blue, alpha)
{
	if (isBlock(ent))
	{
		switch (type)
		{
			case GLOWSHELL: set_rendering(ent, kRenderFxGlowShell, red, green, blue, kRenderNormal, alpha);
			case TRANSCOLOR: set_rendering(ent, kRenderFxGlowShell, red, green, blue, kRenderTransColor, alpha);
			case TRANSALPHA: set_rendering(ent, kRenderFxNone, red, green, blue, kRenderTransColor, alpha);
			case TRANSWHITE: set_rendering(ent, kRenderFxNone, red, green, blue, kRenderTransAdd, alpha);
			default: set_rendering(ent, kRenderFxNone, red, green, blue, kRenderNormal, alpha);
		}
	}
}

/* BLOCK TESTS */
bool:isBlockInGroup(id, ent)
{
	//is entity valid
	if (is_valid_ent(ent))
	{
		//get player who has this block in their group (if anyone)
		new player = entity_get_int(ent, EV_INT_iuser1);
		
		if (player == id)
		{
			return true;
		}
	}
	
	return false;
}

bool:isBlockTypeRotatable(blockType)
{
	if (blockType != BM_FIRE)
	{
		return true;
	}
	
	return false;
}

bool:isBlock(ent)
{
	//is it a valid entity
	if (is_valid_ent(ent))
	{
		//get classname of entity
		new szClassname[32];
		entity_get_string(ent, EV_SZ_classname, szClassname, 32);
		
		//if classname of entity matches global block classname
		if (equal(szClassname, gszBlockClassname) || equal(szClassname, "bcm"))
		{
			//entity is a block
			return true;
		}
	}
	
	//entity is not a block
	return false;
}

bool:isBlockStuck(ent)
{
	//first make sure the entity is valid
	if (is_valid_ent(ent))
	{
		new content;
		new Float:vOrigin[3];
		new Float:vPoint[3];
		new Float:fSizeMin[3];
		new Float:fSizeMax[3];
		
		//get the size of the block being grabbed
		entity_get_vector(ent, EV_VEC_mins, fSizeMin);
		entity_get_vector(ent, EV_VEC_maxs, fSizeMax);
		
		//get the origin of the block
		entity_get_vector(ent, EV_VEC_origin, vOrigin);
		
		//decrease the size values of the block
		fSizeMin[0] += 1.0;
		fSizeMax[0] -= 1.0;
		fSizeMin[1] += 1.0;
		fSizeMax[1] -= 1.0; 
		fSizeMin[2] += 1.0;
		fSizeMax[2] -= 1.0;
		
		//get the contents of the centre of all 6 faces of the block
		for (new i = 0; i < 14; ++i)
		{
			//start by setting the point to the origin of the block (the middle)
			vPoint = vOrigin;
			
			//set the values depending on the loop number
			switch (i)
			{
				//corners
				case 0: { vPoint[0] += fSizeMax[0]; vPoint[1] += fSizeMax[1]; vPoint[2] += fSizeMax[2]; }
				case 1: { vPoint[0] += fSizeMin[0]; vPoint[1] += fSizeMax[1]; vPoint[2] += fSizeMax[2]; }
				case 2: { vPoint[0] += fSizeMax[0]; vPoint[1] += fSizeMin[1]; vPoint[2] += fSizeMax[2]; }
				case 3: { vPoint[0] += fSizeMin[0]; vPoint[1] += fSizeMin[1]; vPoint[2] += fSizeMax[2]; }
				case 4: { vPoint[0] += fSizeMax[0]; vPoint[1] += fSizeMax[1]; vPoint[2] += fSizeMin[2]; }
				case 5: { vPoint[0] += fSizeMin[0]; vPoint[1] += fSizeMax[1]; vPoint[2] += fSizeMin[2]; }
				case 6: { vPoint[0] += fSizeMax[0]; vPoint[1] += fSizeMin[1]; vPoint[2] += fSizeMin[2]; }
				case 7: { vPoint[0] += fSizeMin[0]; vPoint[1] += fSizeMin[1]; vPoint[2] += fSizeMin[2]; }
				
				//centre of faces
				case 8: { vPoint[0] += fSizeMax[0]; }
				case 9: { vPoint[0] += fSizeMin[0]; }
				case 10: { vPoint[1] += fSizeMax[1]; }
				case 11: { vPoint[1] += fSizeMin[1]; }
				case 12: { vPoint[2] += fSizeMax[2]; }
				case 13: { vPoint[2] += fSizeMin[2]; }
			}
			
			//get the contents of the point on the block
			content = point_contents(vPoint);
			
			//if the point is out in the open
			if (content == CONTENTS_EMPTY || content == 0)
			{
				//block is not stuck
				return false;
			}
		}
	}
	else
	{
		//entity is invalid but don't say its stuck
		return false;
	}
	
	//block is stuck
	return true;
}

bool:isTeleport(ent)
{
	if (is_valid_ent(ent))
	{
		//get classname of entity
		new szClassname[32];
		entity_get_string(ent, EV_SZ_classname, szClassname, 32);
		
		//compare classnames
		if (equal(szClassname, gszTeleportStartClassname) || equal(szClassname, gszTeleportEndClassname))
		{
			//entity is a teleport
			return true;
		}
	}
	
	//entity is not a teleport
	return false;
}

doSnapping(id, ent, Float:fMoveTo[3])
{
	//if player has snapping enabled
	if (gbSnapping[id])
	{
		new Float:fSnapSize = gfSnapDistance + gfSnappingGap[id];
		new Float:vReturn[3];
		new Float:dist;
		new Float:distOld = 9999.9;
		new Float:vTraceStart[3];
		new Float:vTraceEnd[3];
		new tr;
		new trClosest = 0;
		new blockFace;
		
		//get the size of the block being grabbed
		new Float:fSizeMin[3];
		new Float:fSizeMax[3];
		entity_get_vector(ent, EV_VEC_mins, fSizeMin);
		entity_get_vector(ent, EV_VEC_maxs, fSizeMax);
		
		//do 6 traces out from each face of the block
		for (new i = 0; i < 6; ++i)
		{
			//setup the start of the trace
			vTraceStart = fMoveTo;
			
			switch (i)
			{
				case 0: vTraceStart[0] += fSizeMin[0];		//edge of block on -X
				case 1: vTraceStart[0] += fSizeMax[0];		//edge of block on +X
				case 2: vTraceStart[1] += fSizeMin[1];		//edge of block on -Y
				case 3: vTraceStart[1] += fSizeMax[1];		//edge of block on +Y
				case 4: vTraceStart[2] += fSizeMin[2];		//edge of block on -Z
				case 5: vTraceStart[2] += fSizeMax[2];		//edge of block on +Z
			}
			
			//setup the end of the trace
			vTraceEnd = vTraceStart;
			
			switch (i)
			{
				case 0: vTraceEnd[0] -= fSnapSize;
				case 1: vTraceEnd[0] += fSnapSize;
				case 2: vTraceEnd[1] -= fSnapSize;
				case 3: vTraceEnd[1] += fSnapSize;
				case 4: vTraceEnd[2] -= fSnapSize;
				case 5: vTraceEnd[2] += fSnapSize;
			}
			
			//trace a line out from one of the block faces
			tr = trace_line(ent, vTraceStart, vTraceEnd, vReturn);
			
			//if the trace found a block and block is not in group or block to snap to is not in group
			if (isBlock(tr) && (!isBlockInGroup(id, tr) || !isBlockInGroup(id, ent)))
			{
				//get the distance from the grabbed block to the found block
				dist = get_distance_f(vTraceStart, vReturn);
				
				//if distance to found block is less than the previous block
				if (dist < distOld)
				{
					trClosest = tr;
					distOld = dist;
					
					//save the block face where the trace came from
					blockFace = i;
				}
			}
		}
		
		//if there is a block within the snapping range
		if (is_valid_ent(trClosest))
		{
			//get origin of closest block
			new Float:vOrigin[3];
			entity_get_vector(trClosest, EV_VEC_origin, vOrigin);
			
			//get sizes of closest block
			new Float:fTrSizeMin[3];
			new Float:fTrSizeMax[3];
			entity_get_vector(trClosest, EV_VEC_mins, fTrSizeMin);
			entity_get_vector(trClosest, EV_VEC_maxs, fTrSizeMax);
			
			//move the subject block to the origin of the closest block
			fMoveTo = vOrigin;
			
			//offset the block to be on the side where the trace hit the closest block
			if (blockFace == 0) fMoveTo[0] += (fTrSizeMax[0] + fSizeMax[0]) + gfSnappingGap[id];
			if (blockFace == 1) fMoveTo[0] += (fTrSizeMin[0] + fSizeMin[0]) - gfSnappingGap[id];
			if (blockFace == 2) fMoveTo[1] += (fTrSizeMax[1] + fSizeMax[1]) + gfSnappingGap[id];
			if (blockFace == 3) fMoveTo[1] += (fTrSizeMin[1] + fSizeMin[1]) - gfSnappingGap[id];
			if (blockFace == 4) fMoveTo[2] += (fTrSizeMax[2] + fSizeMax[2]) + gfSnappingGap[id];
			if (blockFace == 5) fMoveTo[2] += (fTrSizeMin[2] + fSizeMin[2]) - gfSnappingGap[id];
		}
	}
}

/***** FILE HANDLING *****/
saveBlocks(id)
{
	//make sure player has access to this command
	if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		new file = fopen(gszNewFile, "wt");
		new ent = -1;
		new blockType;
		new Float:vOrigin[3];
		new Float:vAngles[3];
		new Float:vStart[3];
		new Float:vEnd[3];
		new blockCount = 0;
		new teleCount = 0;
		new szData[128];
		new Float:fMax;
		new size;
		new Float:vSizeMax[3];
		new szNameCreator[32];
		
		while ((ent = find_ent_by_class(ent, gszBlockClassname)))
		{
			//get block info
			blockType = entity_get_int(ent, EV_INT_body);
			entity_get_vector(ent, EV_VEC_origin, vOrigin);
			entity_get_vector(ent, EV_VEC_angles, vAngles);
			entity_get_vector(ent, EV_VEC_maxs, vSizeMax);
			
			size = SMALL;
			fMax = vSizeMax[0] + vSizeMax[1] + vSizeMax[2];
			if (fMax > 36.0) size = POLE;
			if (fMax > 64.0) size = NORMAL;
			if (fMax > 128.0) size = LARGE;
			
			pev(ent, pev_targetname, szNameCreator, 31);
			
			//format block info and save it to file
			formatex(szData, 128, "%c %f %f %f %f %f %f %d %s^n", gBlockSaveIds[blockType], vOrigin[0], vOrigin[1], vOrigin[2], vAngles[0], vAngles[1], vAngles[2], size, szNameCreator);
			fputs(file, szData);
			
			//increment block count
			++blockCount;
		}
		
		//iterate through teleport end entities because you can't have an end without a start
		ent = -1;
		
		while ((ent = find_ent_by_class(ent, gszTeleportEndClassname)))
		{
			//get the id of the start of the teleporter
			new tele = entity_get_int(ent, EV_INT_iuser1);
			
			//check that start of teleport is a valid entity
			if (tele)
			{
				//get the origin of the start of the teleport and save it to file
				entity_get_vector(tele, EV_VEC_origin, vStart);
				entity_get_vector(ent, EV_VEC_origin, vEnd);
				
				formatex(szData, 128, "%c %f %f %f %f %f %f^n", gTeleportSaveId, vStart[0], vStart[1], vStart[2], vEnd[0], vEnd[1], vEnd[2]);
				fputs(file, szData);
				
				//2 teleport entities count as 1 teleporter
				++teleCount;
			}
		}
		
		//get players name
		new szName[32];
		get_user_name(id, szName, 32);
		
		//notify all admins that the player saved blocks to file
		for (new i = 1; i <= 32; ++i)
		{
			//make sure player is connected
			if (is_user_connected(i))
			{
				if (get_user_flags(i) & BM_ADMIN_LEVEL)
				{
					 ColorChat(0, GREEN, "^x03%s^x04'%s'^x01 saved %d^x04 block%s^x01, %d ^x04teleporter%s^x01 Total entites in map:^x04 %d", gszPrefix, szName, blockCount, (blockCount == 1 ? "" : "s"), teleCount, (teleCount == 1 ? "" : "s"), entity_count());
				}
			}
		}
		
		//close file
		fclose(file);
	}
}

loadBlocks(id)
{
	new bool:bAccess = false;
	
	//if this function was called on map load, ID is 0
	if (id == 0)
	{
		bAccess = true;
	}
	//make sure user calling this function has access
	else if (get_user_flags(id) & BM_ADMIN_LEVEL)
	{
		bAccess = true;
	}
	
	if (bAccess)
	{
		//if map file exists
		if (file_exists(gszNewFile))
		{
			//if a player is loading then first delete all the old blocks, teleports and timers
			if (id > 0 && id <= 32)
			{
				deleteAllBlocks(id, false);
				deleteAllTeleports(id, false);
			}
			
			new szData[128];
			new szType[2];
			new sz1[16], sz2[16], sz3[16], sz4[16], sz5[16], sz6[16], sz7[16];
			new Float:vVec1[3];
			new Float:vVec2[3];
			new axis;
			new size;
			new f = fopen(gszNewFile, "rt");
			new blockCount = 0;
			new teleCount = 0;
			new szCreator[32];
			
			//iterate through all the lines in the file
			while (!feof(f))
			{
				szType = "";
				fgets(f, szData, 128);
				parse(szData, szType, 1, sz1, 16, sz2, 16, sz3, 16, sz4, 16, sz5, 16, sz6, 16, sz7, 16, szCreator, 31);
				
				vVec1[0] = str_to_float(sz1);
				vVec1[1] = str_to_float(sz2);
				vVec1[2] = str_to_float(sz3);
				vVec2[0] = str_to_float(sz4);
				vVec2[1] = str_to_float(sz5);
				vVec2[2] = str_to_float(sz6);
				size = str_to_num(sz7);
				
				if (strlen(szType) > 0)
				{
					//if type is not a teleport
					if (szType[0] != gTeleportSaveId)
					{
						//set axis orientation depending on block angles
						if (vVec2[0] == 90.0 && vVec2[1] == 0.0 && vVec2[2] == 0.0)
						{
							axis = X;
						}
						else if (vVec2[0] == 90.0 && vVec2[1] == 0.0 && vVec2[2] == 90.0)
						{
							axis = Y;
						}
						else
						{
							axis = Z;
						}
						
						//increment block counter
						++blockCount;
					}
					
					//create block or teleport depending on type
					switch (szType[0])
					{
						case 'A': createBlock(0, BM_PLATFORM, vVec1, axis, size, szCreator);
						case 'B': createBlock(0, BM_BHOP, vVec1, axis, size, szCreator);
						case 'C': createBlock(0, BM_DAMAGE, vVec1, axis, size, szCreator);
						case 'D': createBlock(0, BM_HEALER, vVec1, axis, size, szCreator);
						case 'E': createBlock(0, BM_INVINCIBILITY, vVec1, axis, size, szCreator);
						case 'F': createBlock(0, BM_STEALTH, vVec1, axis, size, szCreator);
						case 'H': createBlock(0, BM_SPEEDBOOST, vVec1, axis, size, szCreator);
						case 'I': createBlock(0, BM_NOFALLDAMAGE, vVec1, axis, size, szCreator);
						case 'J': createBlock(0, BM_ICE, vVec1, axis, size, szCreator);
						case 'K': createBlock(0, BM_DEATH, vVec1, axis, size, szCreator);
						case 'M': createBlock(0, BM_CAMOUFLAGE, vVec1, axis, size, szCreator);
						case 'N': createBlock(0, BM_LOWGRAVITY, vVec1, axis, size, szCreator);
						case 'O': createBlock(0, BM_FIRE, vVec1, axis, size, szCreator);
						case 'P': createBlock(0, BM_SLAP, vVec1, axis, size, szCreator);
						case 'Q': createBlock(0, BM_RANDOM, vVec1, axis, size, szCreator);
						case 'R': createBlock(0, BM_HONEY, vVec1, axis, size, szCreator);
						case 'S': createBlock(0, BM_BARRIER_CT, vVec1, axis, size, szCreator);
						case 'T': createBlock(0, BM_BARRIER_T, vVec1, axis, size, szCreator);
						case 'U': createBlock(0, BM_BOOTSOFSPEED, vVec1, axis, size, szCreator);
						case 'V': createBlock(0, BM_GLASS, vVec1, axis, size, szCreator);
						case 'X': createBlock(0, BM_GLASS_IGZ, vVec1, axis, size, szCreator);
						case '+': createBlock(0, BM_GLASS_NORMAL, vVec1, axis, size, szCreator);
						case 'W': createBlock(0, BM_BHOP_NOSLOW, vVec1, axis, size, szCreator);
						case '$': createBlock(0, BM_DELAYEDBHOP, vVec1, axis, size, szCreator);
						case '<': createBlock(0, BM_FADE, vVec1, axis, size, szCreator);
						case '#': createBlock(0, BM_DEAGLE, vVec1, axis, size, szCreator);
						case 'Y': createBlock(0, BM_HE, vVec1, axis, size, szCreator);
						case 'Z': createBlock(0, BM_SMOKE, vVec1, axis, size, szCreator);
						case '!': createBlock(0, BM_FLASH, vVec1, axis, size, szCreator);
						case '@': createBlock(0, BM_AWP, vVec1, axis, size, szCreator);
						case 'G': createBlock(0, BM_TRAMPOLINE_MID, vVec1, axis, size, szCreator);
						case '=': createBlock(0, BM_TRAMPOLINE_LOW, vVec1, axis, size, szCreator);
						case ')': createBlock(0, BM_TRAMPOLINE_HIGH, vVec1, axis, size, szCreator);
						case '(': createBlock(0, BM_LIGHT, vVec1, axis, size, szCreator);
						case '4': createBlock(0, BM_NOFALLDAMAGEBHOP, vVec1, axis, size, szCreator);
						case '9': createBlock(0, BM_DUCK, vVec1, axis, size, szCreator);
						case '7': createBlock(0, BM_MONEY, vVec1, axis, size, szCreator);
						case '8': createBlock(0, BM_SUPERMAN, vVec1, axis, size, szCreator);
						case '&': createBlock(0, BM_XP, vVec1, axis, size, szCreator);
						case '2': createBlock(0, BM_SHOTGUN, vVec1, axis, size, szCreator);
						case '3': createBlock(0, BM_SCOUT, vVec1, axis, size, szCreator);
						case '>': createBlock(0, BM_AK47, vVec1, axis, size, szCreator);
						case ']': createBlock(0, BM_M4A1, vVec1, axis, size, szCreator);
						case '[': createBlock(0, BM_USP, vVec1, axis, size, szCreator);
						case '.': createBlock(0, BM_FAMAS, vVec1, axis, size, szCreator);
						case '_': createBlock(0, BM_GLOCK, vVec1, axis, size, szCreator);
						case '?': createBlock(0, BM_P90, vVec1, axis, size, szCreator);
						case '|': createBlock(0, BM_GALIL, vVec1, axis, size, szCreator);
						
						
						
						case gTeleportSaveId:
						{
							createTeleport(0, TELEPORT_START, vVec1);
							createTeleport(0, TELEPORT_END, vVec2);
							
							//increment teleport count
							++teleCount;
						}
						
						default:
						{
							log_amx("%sInvalid block type: %c in: %s", gszPrefix, szType[0], gszFile);
							
							//decrement block counter because a block was not created
							--blockCount;
						}
					}
				}
			}
			
			fclose(f);
			
			//if a player is loading the blocks
			if (id > 0 && id <= 32)
			{
				//get players name
				new szName[32];
				get_user_name(id, szName, 32);
				
				//notify all admins that the player loaded blocks from file
				for (new i = 1; i <= 32; ++i)
				{
					//make sure player is connected
					if (is_user_connected(i))
					{
						if (get_user_flags(i) & BM_ADMIN_LEVEL)
						{
							ColorChat(0, GREEN, "^x03%s^x04'%s'^x01 loaded %d^x04 block%s^x01, %d ^x04teleporter%s^x01 Total entites in map:^x04 %d", gszPrefix, szName, blockCount, (blockCount == 1 ? "" : "s"), teleCount, (teleCount == 1 ? "" : "s"), entity_count());
						}
					}
				}
			}
		}
		else
		{
			//if a player is loading the blocks
			if (id > 0 && id <= 32)
			{
				//notify player that the file could not be found
				client_print(id, print_chat, "%sCouldn't find file: %s", gszPrefix, gszNewFile);
			}
		}
	}
}

/* MISC */
drawLine(Float:vOrigin1[3], Float:vOrigin2[3], life)
{
	message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
	write_byte(TE_BEAMPOINTS);
	write_coord(floatround(vOrigin1[0], floatround_floor));
	write_coord(floatround(vOrigin1[1], floatround_floor));
	write_coord(floatround(vOrigin1[2], floatround_floor));
	write_coord(floatround(vOrigin2[0], floatround_floor));
	write_coord(floatround(vOrigin2[1], floatround_floor));
	write_coord(floatround(vOrigin2[2], floatround_floor));
	write_short(gSpriteIdBeam);		//sprite index
	write_byte(0);				//starting frame
	write_byte(1);				//frame rate in 0.1's
	write_byte(life);			//life in 0.1's
	write_byte(5);				//line width in 0.1's
	write_byte(0);				//noise amplitude in 0.01's
	write_byte(255);			//red
	write_byte(255);			//green
	write_byte(255);			//blue
	write_byte(255);			//brightness
	write_byte(0);				//scroll speed in 0.1's
	message_end();
}

/* XP Information */
native hnsxp_get_user_xp(client);
native hnsxp_set_user_xp(client, xp);

stock hnsxp_add_user_xp(client, xp)
{
	return hnsxp_set_user_xp(client, hnsxp_get_user_xp(client) + xp);
}

Last edited by staNioN; 07-31-2014 at 13:52. Reason: code changed
staNioN is offline
vibbaboy1
New Member
Join Date: Apr 2015
Old 04-23-2015 , 10:23   Re: Blockmaker - Adding Blocks (12 Step) In-Depth | Updated 12/24/2013!
Reply With Quote #290

Get errors

Quote:
//// test.sma
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(54) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(81) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(113) : error 001: expected token: ";", but found "-identifi
er-"
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(140) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(141) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(142) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(143) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(144) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(145) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(146) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(147) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(14 : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(149) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(150) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(151) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(152) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(153) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(154) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(155) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(156) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(157) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(15 : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(159) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(160) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(161) : error 010: invalid function or declaration
// C:\Program Files (x86)\Steam\steamapps\common\Half-Life\cstrike\addons\amxmod
x\scripting\test.sma(162) : error 010: invalid function or declaration
//
// Compilation aborted.
// 26 Errors.
// Could not locate output file compiled\test.amx (compile failed).
//
// Compilation Time: 0,16 sec
// ----------------------------------------
vibbaboy1 is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 12:53.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode