#include #include #include #include #include #pragma semicolon 1; #define PLUGIN "Block Maker" #define VERSION "3.51" #define AUTHOR "Necro" #define MAIN_MENU_KEYS (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9) #define BLOCKSELECTION_MENU_KEYS (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9) #define OPTIONS_MENU_KEYS (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9) #define TELEPORT_MENU_KEYS (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<5)|(1<<6)|(1<<7)|(1<<9) #define CHOICE_MENU_KEYS (1<<0)|(1<<1)|(1<<9) #define BM_ADMIN_LEVEL ADMIN_MENU //admin access level to use this plugin. ADMIN_MENU = flag 'u' 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 options with YES/NO confirmation */ enum { CHOICE_LOAD, CHOICE_DEL_BLOCKS, CHOICE_DEL_TELEPORTS }; /* hud message values */ const gHudRed = 10; const gHudGreen = 30; const gHudBlue = 200; const Float:gfTextX = -1.0; const Float:gfTextY = 0.84; const gHudEffects = 0; const Float:gfHudFxTime = 0.0; const Float:gfHudHoldTime = 0.25; const Float:gfHudFadeInTime = 0.0; const Float:gfHudFadeOutTime = 0.0; const gHudChannel = 2; /* Task ID offsets */ const TASK_GRAB = 1000; const TASK_BHOPSOLID = 2000; const TASK_BHOPSOLIDNOT = 3000; const TASK_INVINCIBLE = 4000; const TASK_STEALTH = 5000; const TASK_ICE = 6000; const TASK_SPRITE = 7000; const TASK_CAMOUFLAGE = 8000; const TASK_HONEY = 9000; const TASK_FIRE = 10000; const TASK_BOOTSOFSPEED = 11000; const TASK_TELEPORT = 12000; /* Strings */ new const gszPrefix[] = "[BM] "; new const gszInfoTarget[] = "info_target"; new const gszHelpFilenameFormat[] = "blockmaker_v%s.txt"; new gszFile[128]; new gszNewFile[128]; new gszMenu[256]; new gszOptionsMenu[256]; new gszTeleportMenu[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 }; /* Block models */ new const gszBlockModelDefault[] = "models/blockmaker/bm_block_default.mdl"; new const gszBlockModelPlatform[] = "models/blockmaker/bm_block_platform.mdl"; new const gszBlockModelBhop[] = "models/blockmaker/bm_block_bhop.mdl"; new const gszBlockModelDamage[] = "models/blockmaker/bm_block_damage.mdl"; new const gszBlockModelHealer[] = "models/blockmaker/bm_block_healer.mdl"; new const gszBlockModelInvincibility[] = "models/blockmaker/bm_block_invincibility.mdl"; new const gszBlockModelSpeedBoost[] = "models/blockmaker/bm_block_speedboost.mdl"; new const gszBlockModelNoFallDamage[] = "models/blockmaker/bm_block_nofalldamage.mdl"; new const gszBlockModelIce[] = "models/blockmaker/bm_block_ice.mdl"; new const gszBlockModelDeath[] = "models/blockmaker/bm_block_death.mdl"; new const gszBlockModelNuke[] = "models/blockmaker/bm_block_nuke.mdl"; new const gszBlockModelCamouflage[] = "models/blockmaker/bm_block_camouflage.mdl"; new const gszBlockModelLowGravity[] = "models/blockmaker/bm_block_lowgravity.mdl"; new const gszBlockModelFire[] = "models/blockmaker/bm_block_fire.mdl"; new const gszBlockModelRandom[] = "models/blockmaker/bm_block_random.mdl"; new const gszBlockModelSlap[] = "models/blockmaker/bm_block_slap.mdl"; new const gszBlockModelHoney[] = "models/blockmaker/bm_block_honey.mdl"; new const gszBlockModelBarrierCT[] = "models/blockmaker/bm_block_barrier_ct.mdl"; new const gszBlockModelBarrierT[] = "models/blockmaker/bm_block_barrier_t.mdl"; new const gszBlockModelBootsOfSpeed[] = "models/blockmaker/bm_block_bootsofspeed.mdl"; /* Block sprites */ new const gszBlockSpriteFire[] = "sprites/blockmaker/bm_block_fire.spr"; //custom new const gszBlockSpriteTrampoline[] = "sprites/blockmaker/bm_block_trampoline.spr"; //custom new const gszBlockSpriteSpeedBoost[] = "sprites/blockmaker/bm_block_speedboost.spr"; //custom new const gszFireSprite[] = "sprites/blockmaker/bm_block_fire_flame.spr"; //custom /* Block sounds */ new const gszNukeExplosion[] = "weapons/c4_explode1.wav"; //from CS 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 /* Variables */ new gSpriteIdBeam; new gSpriteIdFire; new gMsgScreenFade; new gMenuBeforeOptions[33]; new gChoiceOption[33]; new gBlockMenuPage[33]; new gTeleportStart[33]; new gGrabbed[33]; new gGroupedBlocks[33][256]; new gGroupCount[33]; new bool:gbSnapping[33]; new bool:gbNoFallDamage[33]; new bool:gbOnIce[33]; new bool:gbLowGravity[33]; new bool:gbOnFire[33]; new bool:gbJustDeleted[33]; new bool:gbAdminGodmode[33]; new bool:gbAdminNoclip[33]; new Float:gfSnappingGap[33]; new Float:gfOldMaxSpeed[33]; new Float:gfGrabOffset[33][3]; 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:gfNukeNextUse[33]; new Float:gfCamouflageNextUse[33]; new Float:gfCamouflageTimeOut[33]; new Float:gfRandomNextUse[33]; new Float:gfBootsOfSpeedTimeOut[33]; new Float:gfBootsOfSpeedNextUse[33]; new gszCamouflageOldModel[33][32]; /* BLOCK & TELEPORT TYPES */ const gBlockMax = 21; new gSelectedBlockType[gBlockMax]; new const gszBlockClassname[] = "bm_block"; new const gszSpriteClassname[] = "bm_sprite"; new const gszTeleportStartClassname[32] = "bm_teleportstart"; new const gszTeleportEndClassname[32] = "bm_teleportend"; 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_TRAMPOLINE, //G BM_SPEEDBOOST, //H BM_INVINCIBILITY, //E BM_STEALTH, //F BM_DEATH, //K BM_NUKE, //L 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 }; new const gszBlockNames[gBlockMax][32] = { "Platform", "Bunnyhop", "Damage", "Healer", "No Fall Damage", "Ice", "Trampoline", "Speed Boost", "Invincibility", "Stealth", "Death", "Nuke", "Camouflage", "Low Gravity", "Fire", "Slap", "Random", "Honey", "CT Barrier", "T Barrier", "Boots Of Speed" }; new const gBlockSaveIds[gBlockMax] = { 'A', 'B', 'C', 'D', 'I', 'J', 'G', 'H', 'E', 'F', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U' }; //array of blocks that the random block can be const gRandomBlocksMax = 6; new const gRandomBlocks[gRandomBlocksMax] = { BM_INVINCIBILITY, BM_STEALTH, BM_DEATH, BM_CAMOUFLAGE, BM_SLAP, BM_BOOTSOFSPEED }; //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 client commands register_clcmd("say /bm", "cmdShowMainMenu"); register_clcmd("+bmgrab", "cmdGrab", BM_ADMIN_LEVEL, "bind a key to +bmgrab"); register_clcmd("-bmgrab", "cmdRelease", BM_ADMIN_LEVEL); //register menus register_menucmd(register_menuid("bmMainMenu"), MAIN_MENU_KEYS, "handleMainMenu"); register_menucmd(register_menuid("bmBlockSelectionMenu"), BLOCKSELECTION_MENU_KEYS, "handleBlockSelectionMenu"); register_menucmd(register_menuid("bmOptionsMenu"), OPTIONS_MENU_KEYS, "handleOptionsMenu"); register_menucmd(register_menuid("bmTeleportMenu"), TELEPORT_MENU_KEYS, "handleTeleportMenu"); register_menucmd(register_menuid("bmChoiceMenu"), CHOICE_MENU_KEYS, "handleChoiceMenu"); //register CVARs register_cvar("bm_telefrags", "1"); //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_nukecooldown", "60.0"); //someone might have been invincible when it was used 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 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"); //make config folder if it doesn't already exist new szConfigsDir[64]; new szMap[32]; get_configsdir(szConfigsDir, 64); add(szConfigsDir, 64, "/blockmaker"); if (!dir_exists(szConfigsDir)) { mkdir(szConfigsDir); } get_mapname(szMap, 32); formatex(gszFile, 96, "%s/%s.cfg", szConfigsDir, szMap); //make save folder in basedir (new saving/loading method) new szDir[64]; get_basedir(szDir, 64); add(szDir, 64, "/blockmaker"); if (!dir_exists(szDir)) { mkdir(szDir); } get_mapname(szMap, 32); formatex(gszNewFile, 96, "%s/%s.bm", szDir, szMap); } public plugin_precache() { //precache blocks precache_model(gszBlockModelDefault); precache_model(gszBlockModelDamage); precache_model(gszBlockModelIce); precache_model(gszBlockModelInvincibility); precache_model(gszBlockModelSpeedBoost); precache_model(gszBlockModelBhop); precache_model(gszBlockModelHealer); precache_model(gszBlockModelPlatform); precache_model(gszBlockModelNoFallDamage); precache_model(gszBlockModelDeath); precache_model(gszBlockModelNuke); precache_model(gszBlockModelCamouflage); precache_model(gszBlockModelLowGravity); precache_model(gszBlockModelFire); precache_model(gszBlockModelSlap); precache_model(gszBlockModelRandom); precache_model(gszBlockModelHoney); precache_model(gszBlockModelBarrierCT); precache_model(gszBlockModelBarrierT); precache_model(gszBlockModelBootsOfSpeed); //precache sprites precache_model(gszBlockSpriteFire); precache_model(gszBlockSpriteTrampoline); precache_model(gszBlockSpriteSpeedBoost); precache_model(gszTeleportSpriteStart); precache_model(gszTeleportSpriteEnd); gSpriteIdFire = precache_model(gszFireSprite); gSpriteIdBeam = precache_model("sprites/zbeam4.spr"); //precache sounds precache_sound(gszTeleportSound); precache_sound(gszNukeExplosion); precache_sound(gszInvincibleSound); precache_sound(gszCamouflageSound); precache_sound(gszStealthSound); precache_sound(gszFireSoundFlame); precache_sound(gszBootsOfSpeedSound); } public plugin_cfg() { //format help text filename format(gszHelpFilename, 32, gszHelpFilenameFormat, VERSION); //create main menu new size = sizeof(gszMenu); add(gszMenu, size, "\yBlock Maker Menu^n^n"); add(gszMenu, size, "\r1. \wBlock Type: \y%s^n"); add(gszMenu, size, "\r2. %sCreate Block^n"); add(gszMenu, size, "\r3. %sConvert Block^n"); add(gszMenu, size, "\r4. %sDelete Block^n"); add(gszMenu, size, "\r5. %sRotate Block^n^n"); add(gszMenu, size, "\r6. %sNoclip: %s^n"); add(gszMenu, size, "\r7. %sGodmode: %s^n^n"); add(gszMenu, size, "\r8. \wOptions Menu^n"); add(gszMenu, size, "\r9. \wTeleport menu^n^n"); add(gszMenu, size, "\r0. \wClose"); //calculate maximum number of block menu pages from maximum amount of blocks gBlockMenuPagesMax = floatround((float(gBlockMax) / 8.0), floatround_ceil); //create the options menu size = sizeof(gszOptionsMenu); add(gszOptionsMenu, size, "\yOptions Menu^n^n"); add(gszOptionsMenu, size, "\r1. %sSnapping: %s^n"); add(gszOptionsMenu, size, "\r2. %sSnapping gap: \y%.1f^n"); add(gszOptionsMenu, size, "\r3. %sAdd to group^n"); add(gszOptionsMenu, size, "\r4. %sClear group^n^n"); add(gszOptionsMenu, size, "\r5. %sDelete all blocks^n"); add(gszOptionsMenu, size, "\r6. %sDelete all teleports^n^n"); add(gszOptionsMenu, size, "\r7. %sSave to file^n"); add(gszOptionsMenu, size, "\r8. %sLoad from file^n^n"); add(gszOptionsMenu, size, "\r9. \wShow help^n"); add(gszOptionsMenu, size, "\r0. \wBack"); //create teleport menu size = sizeof(gszTeleportMenu); add(gszTeleportMenu, size, "\yTeleporter Menu^n^n"); add(gszTeleportMenu, size, "\r1. %sTeleport Start^n"); add(gszTeleportMenu, size, "\r2. %sTeleport Destination^n"); add(gszTeleportMenu, size, "\r3. %sShow Teleport Path^n"); add(gszTeleportMenu, size, "\r4. %sDelete Teleport^n^n^n"); add(gszTeleportMenu, size, "\r6. %sNoclip: %s^n"); add(gszTeleportMenu, size, "\r7. %sGodmode: %s^n^n"); add(gszTeleportMenu, size, "\r8. \wOptions Menu^n^n^n"); add(gszTeleportMenu, size, "\r0. \wBack"); //create choice (YES/NO) menu size = sizeof(gszChoiceMenu); add(gszChoiceMenu, size, "\y%s^n^n"); add(gszChoiceMenu, size, "\r1. \wYes^n"); add(gszChoiceMenu, size, "\r2. \wNo^n^n^n^n^n^n^n^n^n^n^n"); add(gszChoiceMenu, size, "\r0. \wBack"); //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 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); } /***** 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; //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 block 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) { //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) { //set bhop block to be SOLID_NOT after 0.1 seconds set_task(0.1, "taskSolidNot", TASK_BHOPSOLIDNOT + ent); } } } } } } return PLUGIN_CONTINUE; } public server_frame() { new ent; new Float:vOrigin[3]; new bool:entNear = false; new tele; //iterate through all players and remove slow down after jumping for any 'on ice' for (new i = 1; i <= 32; ++i) { if (is_user_alive(i) && gbOnIce[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 new 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 new 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, 320); if (isBlock(ent)) { new blockType = entity_get_int(ent, EV_INT_body); set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel); show_hudmessage(id, "Block Type: %s", gszBlockNames[blockType]); } //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_TRAMPOLINE: actionTrampoline(id); case BM_SPEEDBOOST: actionSpeedBoost(id); case BM_DEATH: actionDeath(id); case BM_NUKE: actionNuke(id, false); 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); } } } //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: actionTrampoline(id); case BM_NOFALLDAMAGE: actionNoFallDamage(id); case BM_ICE: actionOnIce(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 aliev if (is_user_alive(id)) { //if player is set no not get fall damage if (gbNoFallDamage[id]) { entity_set_int(id, EV_INT_watertype, -3); gbNoFallDamage[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); } } 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; gfNukeNextUse[id] = 0.0; gbOnFire[id] = false; gfRandomNextUse[id] = 0.0; gfBootsOfSpeedTimeOut[id] = 0.0; gfBootsOfSpeedNextUse[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]); } //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); } } 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 *****/ 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 { set_user_health(id, 100); } 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); } } actionTrampoline(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; } } 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; } 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); } } actionNuke(id, OverrideTimer) { //get game time new Float:fTime = halflife_time(); //make sure player is alive if (is_user_alive(id) && (fTime >= gfNukeNextUse[id] || OverrideTimer)) { //get players team new CsTeams:playerTeam = cs_get_user_team(id); new CsTeams:team; //iterate through all players for (new i = 1; i <= 32; ++i) { //make sure player is alive if (is_user_alive(i)) { team = cs_get_user_team(i); //if this player is on a different team to the player that used the nuke if ((team == CS_TEAM_T && playerTeam == CS_TEAM_CT) || (team == CS_TEAM_CT && playerTeam == CS_TEAM_T)) { //slay player fakedamage(i, "a nuke", 10000.0, DMG_BLAST); } } //make sure player is connected if (is_user_connected(i)) { //make the screen flash for a nuke effect message_begin(MSG_ONE, gMsgScreenFade, {0, 0, 0}, i); write_short(1024); //duration write_short(1024); //hold time write_short(4096); //type (in / out) write_byte(255); //red write_byte(255); //green write_byte(255); //blue write_byte(255); //alpha message_end(); } } //play explosion sound emit_sound(0, CHAN_STATIC, gszNukeExplosion, 1.0, ATTN_NORM, 0, PITCH_NORM); //set the time when the player can use the nuke again (someone might have been invincible) gfNukeNextUse[id] = fTime + get_cvar_float("bm_nukecooldown"); //get the name of the player that used the nuke new szPlayerName[32]; get_user_name(id, szPlayerName, 32); //setup hud message to show who nuked what team set_hudmessage(255, 255, 0, -1.0, 0.35, 0, 6.0, 10.0, 1.0, 1.0); //show message saying player nuked the other team if (playerTeam == CS_TEAM_T) { show_hudmessage(0, "%s just nuked the Counter-Terrorists", szPlayerName); } else { show_hudmessage(0, "%s just nuked the Terrorists", szPlayerName); } } else { set_hudmessage(gHudRed, gHudGreen, gHudBlue, gfTextX, gfTextY, gHudEffects, gfHudFxTime, gfHudHoldTime, gfHudFadeInTime, gfHudFadeOutTime, gHudChannel); show_hudmessage(id, "Nuke next use: %.1f", gfNukeNextUse[id] - fTime); } } 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(255, 255, 255, -1.0, 0.20, 0, 6.0, 12.0, 0.0, 1.0, 3); show_hudmessage(id, "GET OFF MY FACE!!!"); } 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); } //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); } } 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); //play teleport sound emit_sound(id, CHAN_STATIC, gszTeleportSound, 1.0, ATTN_NORM, 0, PITCH_NORM); } } /***** TASKS *****/ 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 (is_valid_ent(ent)) { 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 { set_rendering(ent, kRenderFxNone, 0, 0, 0, kRenderNormal, 255); } } } 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 set_user_maxspeed(id, 250.0); } 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(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(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 client_print(id, print_chat, "%sYou cannot copy a block that is in a stuck position!", 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); //get if the entity is a block or a teleport 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; gfGrabOffset[id][0] = fbOrigin[0] - iAiming[0]; gfGrabOffset[id][1] = fbOrigin[1] - iAiming[1]; gfGrabOffset[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 client_print(id, print_chat, "%sGroup deleted because all the blocks were stuck!", 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 client_print(id, print_chat, "%sBlock deleted because it was stuck!", 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 cmdShowMainMenu(id) { new col[3]; new szMenu[256]; new szGodmode[6]; new szNoclip[6]; col = (get_user_flags(id) & BM_ADMIN_LEVEL ? "\w" : "\d"); szGodmode = (get_user_godmode(id) ? "\yOn" : "\rOff"); szNoclip = (get_user_noclip(id) ? "\yOn" : "\rOff"); //format the main menu format(szMenu, 256, gszMenu, gszBlockNames[gSelectedBlockType[id]], col, col, col, col, col, szNoclip, col, szGodmode); //show the main menu to the player show_menu(id, MAIN_MENU_KEYS, szMenu, -1, "bmMainMenu"); 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, BLOCKSELECTION_MENU_KEYS, szBlockMenu, -1, "bmBlockSelectionMenu"); } 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 player show_menu(id, OPTIONS_MENU_KEYS, szMenu, -1, "bmOptionsMenu"); } showTeleportMenu(id) { new col[3]; new szMenu[256]; new szGodmode[6]; new szNoclip[6]; col = (get_user_flags(id) & BM_ADMIN_LEVEL ? "\w" : "\d"); szGodmode = (get_user_godmode(id) ? "\yOn" : "\rOff"); szNoclip = (get_user_noclip(id) ? "\yOn" : "\rOff"); //format teleport menu format(szMenu, sizeof(szMenu), gszTeleportMenu, col, (gTeleportStart[id] ? "\w" : "\d"), col, col, col, szNoclip, col, szGodmode); show_menu(id, TELEPORT_MENU_KEYS, szMenu, -1, "bmTeleportMenu"); } 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, TELEPORT_MENU_KEYS, szMenu, -1, "bmChoiceMenu"); } public handleMainMenu(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: { showOptionsMenu(id, 1); } case N9: { showTeleportMenu(id); } case N0: { return; } } //selections 1, 8 and 9 show different menus if (num != N1 && num != N8 && num != N9) { //display menu again cmdShowMainMenu(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 main menu if player goes back too far if (gBlockMenuPage[id] < 1) { cmdShowMainMenu(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; cmdShowMainMenu(id); } else { showBlockSelectionMenu(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: { if (gMenuBeforeOptions[id] == 1) { cmdShowMainMenu(id); } else if (gMenuBeforeOptions[id] == 2) { showTeleportMenu(id); } else { //for some reason the players 'gMenuBeforeOptions' number is invalid 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 handleTeleportMenu(id, num) { switch (num) { case N1: { createTeleportAiming(id, TELEPORT_START); } case N2: { createTeleportAiming(id, TELEPORT_END); } case N3: { showTeleportPath(id); } case N4: { deleteTeleportAiming(id); } case N6: { toggleNoclip(id); } case N7: { toggleGodmode(id); } case N8: { showOptionsMenu(id, 2); } case N0: { cmdShowMainMenu(id); } } //selections 6, 8 and 9 show different menus if (num != N6 && num != N8 && num != N0) { showTeleportMenu(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) { //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; } else { //turn on godmode for player set_user_godmode(id, 1); gbAdminGodmode[id] = true; } } } toggleNoclip(id) { //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; } else { //turn on noclip for player set_user_noclip(id, 1); gbAdminNoclip[id] = true; } } } 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:fNukeCooldown = get_cvar_float("bm_nukecooldown"); new Float:fRandomCooldown = get_cvar_float("bm_randomcooldown"); new Float:fBootsOfSpeedTime = get_cvar_float("bm_bootsofspeedtime"); new Float:fBootsOfSpeedCooldown = get_cvar_float("bm_bootsofspeedcooldown"); //format the help text format(szHelpText, sizeof(szHelpText), gszHelpText, Telefrags, fFireDamageAmount, fDamageAmount, fHealAmount, fInvincibleTime, fInvincibleCooldown, fStealthTime, fStealthCooldown, fCamouflageTime, fCamouflageCooldown, fNukeCooldown, fRandomCooldown, fBootsOfSpeedTime, fBootsOfSpeedCooldown); //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 client_print(id, print_chat, "%sA line has been drawn to show the teleport path. Distance: %f units.", gszPrefix, fDist); } } } /* GROUPING BLOCKS */ groupBlockAiming(id) { //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 client_print(id, print_chat, "%sBlock is already in a group by: %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); } //get block type new blockType = entity_get_int(ent, EV_INT_body); //if block is a stealth block if (blockType == BM_STEALTH) { //make block transparent and red set_rendering(ent, kRenderFxGlowShell, 255, 0, 0, kRenderTransAdd, 16); } else { //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); //if block is an invincibility block if (blockType == BM_INVINCIBILITY) { //set block to glow white set_rendering(ent, kRenderFxGlowShell, 255, 255, 255, kRenderNormal, 16); } else if (blockType == BM_STEALTH) { //set block to be transparent set_rendering(ent, kRenderFxNone, 255, 255, 255, kRenderTransAdd, 180); } else { //remove glow on block set_rendering(ent, kRenderFxNone, 0, 0, 0, kRenderNormal, 255); } } } 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 client_print(id, print_chat, "%sRemoved %d blocks from group, deleted %d stuck blocks", gszPrefix, blockCount, blocksDeleted); } else { //notify player how many blocks were cleared from group client_print(id, print_chat, "%sRemoved %d blocks 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) + gfGrabOffset[id][0]; vMoveTo[1] = (fOrigin[1] + fDirection[1] * gfGrablength[id] / fLength) + gfGrabOffset[id][1]; vMoveTo[2] = (fOrigin[2] + fDirection[2] * gfGrablength[id] / fLength) + gfGrabOffset[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) { //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); } } /* TELEPORTS */ createTeleportAiming(id, 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(id, 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); } } 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) { client_print(id, print_chat, "%sTeleport deleted!", gszPrefix); } } } bool:deleteTeleport(id, ent) { //iterate through the different types of teleport (start and end) for (new i = 0; i < 2; ++i) { //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; } 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; } } //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) { client_print(i, print_chat, "%s'%s' deleted all the blocks from the map. Total blocks: %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 block bDeleted = deleteTeleport(id, ent); //if block was successfully deleted if (bDeleted) { //increment counter for how many blocks have been deleted ++teleCount; } } //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) { client_print(i, print_chat, "%s'%s' deleted all the teleports from the map. Total teleports: %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]; //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, gfDefaultBlockAngles, gfBlockSizeMinForZ, gfBlockSizeMaxForZ); } } createBlock(const id, const blockType, Float:vOrigin[3], Float:vAngles[3], Float:vSizeMin[3], Float:vSizeMax[3]) { 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); switch (blockType) { case BM_PLATFORM: entity_set_model(ent, gszBlockModelPlatform); case BM_BHOP: entity_set_model(ent, gszBlockModelBhop); case BM_DAMAGE: entity_set_model(ent, gszBlockModelDamage); case BM_HEALER: entity_set_model(ent, gszBlockModelHealer); case BM_INVINCIBILITY: entity_set_model(ent, gszBlockModelInvincibility); case BM_STEALTH: entity_set_model(ent, gszBlockModelDefault); case BM_TRAMPOLINE: entity_set_model(ent, gszBlockModelDefault); case BM_SPEEDBOOST: entity_set_model(ent, gszBlockModelSpeedBoost); case BM_NOFALLDAMAGE: entity_set_model(ent, gszBlockModelNoFallDamage); case BM_ICE: entity_set_model(ent, gszBlockModelIce); case BM_DEATH: entity_set_model(ent, gszBlockModelDeath); case BM_NUKE: entity_set_model(ent, gszBlockModelNuke); case BM_CAMOUFLAGE: entity_set_model(ent, gszBlockModelCamouflage); case BM_LOWGRAVITY: entity_set_model(ent, gszBlockModelLowGravity); case BM_FIRE: entity_set_model(ent, gszBlockModelFire); case BM_SLAP: entity_set_model(ent, gszBlockModelSlap); case BM_RANDOM: entity_set_model(ent, gszBlockModelRandom); case BM_HONEY: entity_set_model(ent, gszBlockModelHoney); case BM_BARRIER_CT: entity_set_model(ent, gszBlockModelBarrierCT); case BM_BARRIER_T: entity_set_model(ent, gszBlockModelBarrierT); case BM_BOOTSOFSPEED: entity_set_model(ent, gszBlockModelBootsOfSpeed); default: 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); //setup special properties on some blocks switch (blockType) { case BM_STEALTH: set_rendering(ent, kRenderFxNone, 255, 255, 255, kRenderTransAdd, 180); case BM_INVINCIBILITY: set_rendering(ent, kRenderFxGlowShell, 255, 255, 255, kRenderNormal, 16); case 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]); } } //if blocktype is one which requires an additional sprite if (blockType == BM_FIRE || blockType == BM_TRAMPOLINE || blockType == BM_SPEEDBOOST) { //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 sprite model depending on block type switch (blockType) { case BM_TRAMPOLINE: entity_set_model(sprite, gszBlockSpriteTrampoline); case BM_SPEEDBOOST: entity_set_model(sprite, gszBlockSpriteSpeedBoost); case BM_FIRE: entity_set_model(sprite, gszBlockSpriteFire); } //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 || blockType == BM_TRAMPOLINE) { 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); //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) { client_print(id, print_chat, "%sCouldn't convert %d blocks!", gszPrefix, blockCount); } } else { newBlock = convertBlock(id, ent, convertTo); //if block was not converted if (newBlock == 0) { //get the block type new blockType = entity_get_int(ent, EV_INT_body); client_print(id, print_chat, "%sYou cannot convert a %s block into a %s block while it is rotated!", 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 client_print(id, print_chat, "%s%s currently has this block in their group!", gszPrefix, szName); } } } } } convertBlock(id, ent, const convertTo) { new blockType = entity_get_int(ent, EV_INT_body); //if block to convert to is different to block to convert if (blockType != convertTo) { new Float:vOrigin[3]; new Float:vAngles[3]; new Float:vSizeMin[3]; new Float:vSizeMax[3]; //get block information from block player is aiming at 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); //if block is rotated and we're trying to convert it to a block that cannot be rotated if (vAngles[0] == 90.0 && (convertTo == BM_FIRE || convertTo == BM_TRAMPOLINE || convertTo == BM_SPEEDBOOST)) { return 0; } else { //delete old block and create new one of given type deleteBlock(ent); return createBlock(id, convertTo, vOrigin, vAngles, vSizeMin, vSizeMax); } } 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 client_print(id, print_chat, "%s%s currently has this block in their group!", 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 client_print(id, print_chat, "%sYour group contains blocks that cannot be rotated!", 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 client_print(id, print_chat, "%s%s blocks cannot be rotated!", 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 client_print(id, print_chat, "%s%s currently has this block in their group!", 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]; //get block information entity_get_vector(ent, EV_VEC_angles, vAngles); //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; vSizeMin = gfBlockSizeMinForX; vSizeMax = gfBlockSizeMaxForX; } else if (vAngles[0] == 90.0 && vAngles[2] == 0.0) { vAngles[0] = 90.0; vAngles[2] = 90.0; vSizeMin = gfBlockSizeMinForY; vSizeMax = gfBlockSizeMaxForY; } else { vAngles = gfDefaultBlockAngles; vSizeMin = gfBlockSizeMinForZ; vSizeMax = gfBlockSizeMaxForZ; } entity_set_vector(ent, EV_VEC_angles, vAngles); entity_set_size(ent, vSizeMin, vSizeMax); return true; } } return false; } copyBlock(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 blockType; //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); //create a block of the same type in the same location return createBlock(0, blockType, vOrigin, vAngles, vSizeMin, vSizeMax); } return 0; } /* 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 && blockType != BM_TRAMPOLINE && blockType != BM_SPEEDBOOST) { 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]; 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); //format block info and save it to file formatex(szData, 128, "%c %f %f %f %f %f %f^n", gBlockSaveIds[blockType], vOrigin[0], vOrigin[1], vOrigin[2], vAngles[0], vAngles[1], vAngles[2]); 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, "%s %f %f %f %f %f %f^n", "*", 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) { client_print(i, print_chat, "%s'%s' saved %d block%s and %d teleporter%s to file! Total entites in map: %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 the blocks then first delete all the old blocks and teleports 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]; new Float:vVec1[3]; new Float:vVec2[3]; new Float:fSizeMin[3]; new Float:fSizeMax[3]; new f = fopen(gszNewFile, "rt"); new blockCount = 0; new teleCount = 0; //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); 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); if (strlen(szType) > 0) { //if type is not a teleport if (szType[0] != '*') { //set block size depending on block angles if (vVec2[0] == 90.0 && vVec2[1] == 0.0 && vVec2[2] == 0.0) { fSizeMin = gfBlockSizeMinForX; fSizeMax = gfBlockSizeMaxForX; } else if (vVec2[0] == 90.0 && vVec2[1] == 0.0 && vVec2[2] == 90.0) { fSizeMin = gfBlockSizeMinForY; fSizeMax = gfBlockSizeMaxForY; } else { fSizeMin = gfBlockSizeMinForZ; fSizeMax = gfBlockSizeMaxForZ; } //increment block counter ++blockCount; } //create block or teleport depending on type switch (szType[0]) { case 'A': createBlock(0, BM_PLATFORM, vVec1, vVec2, fSizeMin, fSizeMax); case 'B': createBlock(0, BM_BHOP, vVec1, vVec2, fSizeMin, fSizeMax); case 'C': createBlock(0, BM_DAMAGE, vVec1, vVec2, fSizeMin, fSizeMax); case 'D': createBlock(0, BM_HEALER, vVec1, vVec2, fSizeMin, fSizeMax); case 'E': createBlock(0, BM_INVINCIBILITY, vVec1, vVec2, fSizeMin, fSizeMax); case 'F': createBlock(0, BM_STEALTH, vVec1, vVec2, fSizeMin, fSizeMax); case 'G': createBlock(0, BM_TRAMPOLINE, vVec1, vVec2, fSizeMin, fSizeMax); case 'H': createBlock(0, BM_SPEEDBOOST, vVec1, vVec2, fSizeMin, fSizeMax); case 'I': createBlock(0, BM_NOFALLDAMAGE, vVec1, vVec2, fSizeMin, fSizeMax); case 'J': createBlock(0, BM_ICE, vVec1, vVec2, fSizeMin, fSizeMax); case 'K': createBlock(0, BM_DEATH, vVec1, vVec2, fSizeMin, fSizeMax); case 'L': createBlock(0, BM_NUKE, vVec1, vVec2, fSizeMin, fSizeMax); case 'M': createBlock(0, BM_CAMOUFLAGE, vVec1, vVec2, fSizeMin, fSizeMax); case 'N': createBlock(0, BM_LOWGRAVITY, vVec1, vVec2, fSizeMin, fSizeMax); case 'O': createBlock(0, BM_FIRE, vVec1, vVec2, fSizeMin, fSizeMax); case 'P': createBlock(0, BM_SLAP, vVec1, vVec2, fSizeMin, fSizeMax); case 'Q': createBlock(0, BM_RANDOM, vVec1, vVec2, fSizeMin, fSizeMax); case 'R': createBlock(0, BM_HONEY, vVec1, vVec2, fSizeMin, fSizeMax); case 'S': createBlock(0, BM_BARRIER_CT, vVec1, vVec2, fSizeMin, fSizeMax); case 'T': createBlock(0, BM_BARRIER_T, vVec1, vVec2, fSizeMin, fSizeMax); case 'U': createBlock(0, BM_BOOTSOFSPEED, vVec1, vVec2, fSizeMin, fSizeMax); case '*': { 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) { client_print(i, print_chat, "%s'%s' loaded %d block%s and %d teleporter%s from file! Total entites in map: %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); } else { log_amx("%sCant find blocks file. Trying old save/load method...", gszPrefix); } //check old saving/loading method loadBlocksOld(); } } } loadBlocksOld() { if (!file_exists(gszFile)) { return; } new szData[128]; new szType[2]; new oX[13], oY[13], oZ[13]; new aX[13], aY[13], aZ[13]; new Float:vOrigin[3]; new Float:vAngles[3]; new f = fopen(gszFile, "rt"); new blockType; //iterate through all the lines in the file while (!feof(f)) { szType = ""; fgets(f, szData, 128); parse(szData, szType, 2, oX, 12, oY, 12, oZ, 12, aX, 12, aY, 12, aZ, 12); vOrigin[0] = str_to_float(oX); vOrigin[1] = str_to_float(oY); vOrigin[2] = str_to_float(oZ); vAngles[0] = str_to_float(aX); vAngles[1] = str_to_float(aY); vAngles[2] = str_to_float(aZ); blockType = -1; if (strlen(szType) > 0) { //first check if type is a teleporter if (szType[0] == 'S') { createTeleport(0, TELEPORT_START, vOrigin); } else if (szType[0] == 'D') { createTeleport(0, TELEPORT_END, vOrigin); } else { blockType = str_to_num(szType); //if blockType number is valid, create the block if (blockType >= 0 && blockType < gBlockMax) { //create clipping vectors from angles if (vAngles[0] == 90.0 && vAngles[1] == 0.0 && vAngles[2] == 0.0) { createBlock(0, blockType, vOrigin, vAngles, gfBlockSizeMinForX, gfBlockSizeMaxForX); } else if (vAngles[0] == 90.0 && vAngles[1] == 0.0 && vAngles[2] == 90.0) { createBlock(0, blockType, vOrigin, vAngles, gfBlockSizeMinForY, gfBlockSizeMaxForY); } else { createBlock(0, blockType, vOrigin, gfDefaultBlockAngles, gfBlockSizeMinForZ, gfBlockSizeMaxForZ); } } else { log_amx("%sInvalid box type: %c in: %s", gszPrefix, szType[0], gszFile); } } } } fclose(f); } /* 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(); }