/* ===Freak Fortress 2=== By Rainbolt Dash: programmer, modeller, mapper, painter. Author of Demoman The Pirate: http://www.randomfortress.ru/thepirate/ And one of two creators of Floral Defence: http://www.polycount.com/forum/showthread.php?t=73688 And author of VS Saxton Hale Mode Plugin thread on AlliedMods: http://forums.alliedmods.net/showthread.php?t=182108 Updated by Otokiru, Powerlord, and RavensBro after Rainbolt Dash got sucked into DOTA2 Updated by Wliu, Chris, Lawd, and Carge after Powerlord quit FF2 */ #pragma semicolon 1 #include #include #include #include #include #include #include #include #undef REQUIRE_EXTENSIONS #tryinclude #define REQUIRE_EXTENSIONS #undef REQUIRE_PLUGIN //#tryinclude #tryinclude #tryinclude #tryinclude #tryinclude #define REQUIRE_PLUGIN #define MAJOR_REVISION "1" #define MINOR_REVISION "10" #define STABLE_REVISION "6" #define DEV_REVISION "Beta" #define BUILD_NUMBER "manual" //This gets automagically updated by Jenkins #if !defined DEV_REVISION #define PLUGIN_VERSION MAJOR_REVISION..."."...MINOR_REVISION..."."...STABLE_REVISION //1.10.6 #else #define PLUGIN_VERSION MAJOR_REVISION..."."...MINOR_REVISION..."."...STABLE_REVISION..." "...DEV_REVISION..." (build "...BUILD_NUMBER...")" #endif #define UPDATE_URL "http://ff2.50dkp.com/updater/ff2-official/update.txt" #define MAXENTITIES 2048 #define MAXSPECIALS 64 #define MAXRANDOMS 16 #define SOUNDEXCEPT_MUSIC 0 #define SOUNDEXCEPT_VOICE 1 #define HEALTHBAR_CLASS "monster_resource" #define HEALTHBAR_PROPERTY "m_iBossHealthPercentageByte" #define HEALTHBAR_MAX 255 #define MONOCULUS "eyeball_boss" #define DISABLED_PERKS "toxic,noclip,uber,ammo,instant,jump,tinyplayer" #if defined _steamtools_included new bool:steamtools=false; #endif #if defined _tf2attributes_included new bool:tf2attributes=false; #endif #if defined _goomba_included new bool:goomba=false; #endif new bool:smac=false; new OtherTeam=2; new BossTeam=3; new playing; new healthcheckused; new RedAlivePlayers; new BlueAlivePlayers; new RoundCount; new Special[MAXPLAYERS+1]; new Incoming[MAXPLAYERS+1]; new MusicIndex; new Damage[MAXPLAYERS+1]; new curHelp[MAXPLAYERS+1]; new uberTarget[MAXPLAYERS+1]; new shield[MAXPLAYERS+1]; new detonations[MAXPLAYERS+1]; new FF2flags[MAXPLAYERS+1]; new Boss[MAXPLAYERS+1]; new BossHealthMax[MAXPLAYERS+1]; new BossHealth[MAXPLAYERS+1]; new BossHealthLast[MAXPLAYERS+1]; new BossLives[MAXPLAYERS+1]; new BossLivesMax[MAXPLAYERS+1]; new BossRageDamage[MAXPLAYERS+1]; new Float:BossCharge[MAXPLAYERS+1][8]; new Float:Stabbed[MAXPLAYERS+1]; new Float:Marketed[MAXPLAYERS+1]; new Float:KSpreeTimer[MAXPLAYERS+1]; new KSpreeCount[MAXPLAYERS+1]; new Float:GlowTimer[MAXPLAYERS+1]; new shortname[MAXPLAYERS+1]; new bool:emitRageSound[MAXPLAYERS+1]; new timeleft; new Handle:cvarVersion; new Handle:cvarPointDelay; new Handle:cvarAnnounce; new Handle:cvarEnabled; new Handle:cvarAliveToEnable; new Handle:cvarPointType; new Handle:cvarCrits; new Handle:cvarFirstRound; //DEPRECATED new Handle:cvarArenaRounds; new Handle:cvarCircuitStun; new Handle:cvarSpecForceBoss; new Handle:cvarCountdownPlayers; new Handle:cvarCountdownTime; new Handle:cvarCountdownHealth; new Handle:cvarCountdownResult; new Handle:cvarEnableEurekaEffect; new Handle:cvarForceBossTeam; new Handle:cvarHealthBar; new Handle:cvarLastPlayerGlow; new Handle:cvarBossTeleporter; new Handle:cvarBossSuicide; new Handle:cvarShieldCrits; new Handle:cvarCaberDetonations; new Handle:cvarGoombaDamage; new Handle:cvarGoombaRebound; new Handle:cvarBossRTD; new Handle:cvarUpdater; new Handle:cvarDebug; new Handle:cvarPreroundBossDisconnect; new Handle:FF2Cookies; new Handle:jumpHUD; new Handle:rageHUD; new Handle:livesHUD; new Handle:timeleftHUD; new Handle:abilitiesHUD; new Handle:infoHUD; new bool:Enabled=true; new bool:Enabled2=true; new PointDelay=6; new Float:Announce=120.0; new AliveToEnable=5; new PointType; new bool:BossCrits=true; new arenaRounds; new Float:circuitStun; new countdownPlayers=1; new countdownTime=120; new countdownHealth=2000; new bool:SpecForceBoss; new bool:lastPlayerGlow=true; new bool:bossTeleportation=true; new shieldCrits; new allowedDetonations; new Float:GoombaDamage=0.05; new Float:reboundPower=300.0; new bool:canBossRTD; new Handle:MusicTimer; new Handle:BossInfoTimer[MAXPLAYERS+1][2]; new Handle:DrawGameTimer; new Handle:doorCheckTimer; new botqueuepoints; new Float:HPTime; new String:currentmap[99]; new bool:checkDoors=false; new bool:bMedieval; new bool:firstBlood; new tf_arena_use_queue; new mp_teams_unbalance_limit; new tf_arena_first_blood; new mp_forcecamera; new Handle:cvarNextmap; new bool:areSubPluginsEnabled; new FF2CharSet; new validCharsets[64]; new String:FF2CharSetString[42]; new bool:isCharSetSelected=false; new healthBar=-1; new g_Monoculus=-1; static bool:executed=false; static bool:executed2=false; new changeGamemode; enum Operators { Operator_None=0, Operator_Add, Operator_Subtract, Operator_Multiply, Operator_Divide, Operator_Exponent, }; static const String:ff2versiontitles[][]= { "1.0", "1.01", "1.01", "1.02", "1.03", "1.04", "1.05", "1.05", "1.06", "1.06c", "1.06d", "1.06e", "1.06f", "1.06g", "1.06h", "1.07 beta 1", "1.07 beta 1", "1.07 beta 1", "1.07 beta 1", "1.07 beta 1", "1.07 beta 4", "1.07 beta 5", "1.07 beta 6", "1.07", "1.0.8", "1.0.8", "1.0.8", "1.0.8", "1.0.8", "1.9.0", "1.9.0", "1.9.1", "1.9.2", "1.9.2", "1.9.3", "1.10.0", "1.10.0", "1.10.0", "1.10.0", "1.10.0", "1.10.0", "1.10.0", "1.10.0", "1.10.1", "1.10.1", "1.10.1", "1.10.1", "1.10.1", "1.10.2", "1.10.3", "1.10.3", "1.10.3", "1.10.3", "1.10.3", "1.10.4", "1.10.4", "1.10.4", "1.10.4", "1.10.4", "1.10.5", "1.10.6", "1.10.6" }; static const String:ff2versiondates[][]= { "6 April 2012", //1.0 "14 April 2012", //1.01 "14 April 2012", //1.01 "17 April 2012", //1.02 "19 April 2012", //1.03 "21 April 2012", //1.04 "29 April 2012", //1.05 "29 April 2012", //1.05 "1 May 2012", //1.06 "22 June 2012", //1.06c "3 July 2012", //1.06d "24 Aug 2012", //1.06e "5 Sep 2012", //1.06f "5 Sep 2012", //1.06g "6 Sep 2012", //1.06h "8 Oct 2012", //1.07 beta 1 "8 Oct 2012", //1.07 beta 1 "8 Oct 2012", //1.07 beta 1 "8 Oct 2012", //1.07 beta 1 "8 Oct 2012", //1.07 beta 1 "11 Oct 2012", //1.07 beta 4 "18 Oct 2012", //1.07 beta 5 "9 Nov 2012", //1.07 beta 6 "14 Dec 2012", //1.07 "October 30, 2013", //1.0.8 "October 30, 2013", //1.0.8 "October 30, 2013", //1.0.8 "October 30, 2013", //1.0.8 "October 30, 2013", //1.0.8 "March 6, 2014", //1.9.0 "March 6, 2014", //1.9.0 "March 18, 2014", //1.9.1 "March 22, 2014", //1.9.2 "March 22, 2014", //1.9.2 "April 5, 2014", //1.9.3 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "July 26, 2014", //1.10.0 "August 28, 2014", //1.10.1 "August 28, 2014", //1.10.1 "August 28, 2014", //1.10.1 "August 28, 2014", //1.10.1 "August 28, 2014", //1.10.1 "August 28, 2014", //1.10.2 "November 6, 2014", //1.10.3 "November 6, 2014", //1.10.3 "November 6, 2014", //1.10.3 "November 6, 2014", //1.10.3 "November 6, 2014", //1.10.3 "March 1, 2015", //1.10.4 "March 1, 2015", //1.10.4 "March 1, 2015", //1.10.4 "March 1, 2015", //1.10.4 "March 1, 2015", //1.10.4 "March 13, 2015", //1.10.5 "March 26, 2015", //1.10.6 "March 26, 2015" //1.10.6 }; stock FindVersionData(Handle:panel, versionIndex) { switch(versionIndex) { case 61: //1.10.6 { DrawPanelText(panel, "1) Updated the default health formula to match VSH's (Wliu)"); DrawPanelText(panel, "2) Fixed 'sound_fail' playing even when the boss won (Shadow)"); DrawPanelText(panel, "3) Fixed charset voting again (Wliu from Shadow)"); DrawPanelText(panel, "4) Fixed bravejump sounds not playing (Wliu from Maximilian_)"); DrawPanelText(panel, "5) [Server] Fixed 'UTIL_SetModel not precached' crashes-see #6 for the underlying fix (Shadow/Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 60: //1.10.6 { DrawPanelText(panel, "6) [Dev] FF2_GetBossIndex now makes sure the client index passed is valid (Wliu)"); DrawPanelText(panel, "7) [Dev] Rewrote the health formula parser and fixed a few bugs along the way (WildCard65)"); } case 59: //1.10.5 { DrawPanelText(panel, "1) Fixed slow-mo being extremely buggy (Wliu from various)"); DrawPanelText(panel, "2) Fixed the Festive SMG not getting crits (Wliu from Dalix)"); DrawPanelText(panel, "3) Fixed teleport sounds not being played (Wliu from Dalix)"); DrawPanelText(panel, "4) !ff2_stop_music can now target specific clients (Wliu)"); DrawPanelText(panel, "5) [Server] Fixed multiple sounds not working after TF2 changed the default sound extension type (Wliu)"); DrawPanelText(panel, "6) [Dev] Fixed rage damage not resetting after using FF2_SetBossRageDamage (Wliu from WildCard65)"); } case 58: //1.10.4 { DrawPanelText(panel, "1) Fixed players getting overheal after winning as a boss (Wliu/FlaminSarge)"); DrawPanelText(panel, "2) Rebalanced the Baby Face's Blaster (Shadow)"); DrawPanelText(panel, "3) Fixed the Baby Face's Blaster being unusable when FF2 was disabled (Wliu from Curtgust)"); DrawPanelText(panel, "4) Fixed the Darwin's Danger Shield getting replaced by the SMG (Wliu)"); DrawPanelText(panel, "5) Added the Tide Turner and new festive weapons to the weapon whitelist (Wliu)"); DrawPanelText(panel, "See next page (press 1"); } case 57: //1.10.4 { DrawPanelText(panel, "6) Fixed Market Gardener backstabs (Wliu)"); DrawPanelText(panel, "7) Improved class switching after you finish the round as a boss (Wliu)"); DrawPanelText(panel, "8) Fixed the !ff2 command again (Wliu)"); DrawPanelText(panel, "9) Fixed bosses not ducking when teleporting (CapnDev)"); DrawPanelText(panel, "10) Prevented dead companion bosses from becoming clones (Wliu)"); DrawPanelText(panel, "See next page (press 1)"); } case 56: //1.10.4 { DrawPanelText(panel, "11) [Server] Fixed 'ff2_alive' never being shown (Wliu from various)"); DrawPanelText(panel, "12) [Server] Fixed invalid healthbar errors (Wliu from ClassicGuzzi)"); DrawPanelText(panel, "13) [Server] Fixed OnTakeDamage errors from spell Monoculuses (Wliu from ClassicGuzzi)"); DrawPanelText(panel, "14) [Server] Added 'ff2_arena_rounds' and deprecated 'ff2_first_round' (Wliu from Spyper)"); DrawPanelText(panel, "15) [Server] Added 'ff2_base_jumper_stun' to disable the parachute on stun (Wliu from Shadow)"); DrawPanelText(panel, "See next page (press 1)"); } case 55: //1.10.4 { DrawPanelText(panel, "16) [Server] Prevented FF2 from loading if it gets loaded in the /plugins/freaks/ directory (Wliu)"); DrawPanelText(panel, "17) [Dev] Fixed 'sound_fail' (Wliu from M76030)"); DrawPanelText(panel, "18) [Dev] Allowed companions to emit 'sound_nextlife' if they have it (Wliu from M76030)"); DrawPanelText(panel, "19) [Dev] Added 'sound_last_life' (Wliu from WildCard65)"); DrawPanelText(panel, "20) [Dev] Added FF2_OnAlivePlayersChanged and deprecated FF2_Get{Alive|Boss}Players (Wliu from Shadow)"); DrawPanelText(panel, "See next page (press 1)"); } case 54: //1.10.4 { DrawPanelText(panel, "21) [Dev] Fixed AIOOB errors in FF2_GetBossUserId (Wliu)"); DrawPanelText(panel, "22) [Dev] Improved FF2_OnSpecialSelected so that only part of a boss name is needed (Wliu)"); DrawPanelText(panel, "23) [Dev] Added FF2_{Get|Set}BossRageDamage (Wliu from WildCard65)"); } case 53: //1.10.3 { DrawPanelText(panel, "1) Fixed bosses appearing to be overhealed (War3Evo/Wliu)"); DrawPanelText(panel, "2) Rebalanced many weapons based on misc. feedback (Wliu/various)"); DrawPanelText(panel, "3) Fixed not being able to use strange syringe guns or mediguns (Chris from Spyper)"); DrawPanelText(panel, "4) Fixed the Bread Bite being replaced by the GRU (Wliu from Spyper)"); DrawPanelText(panel, "5) Fixed Mantreads not giving extra rocket jump height (Chdata"); DrawPanelText(panel, "See next page (press 1)"); } case 52: //1.10.3 { DrawPanelText(panel, "6) Prevented bosses from picking up ammo/health by default (friagram)"); DrawPanelText(panel, "7) Fixed a bug with respawning bosses (Wliu from Spyper)"); DrawPanelText(panel, "8) Fixed an issue with displaying boss health in chat (Wliu)"); DrawPanelText(panel, "9) Fixed an edge case where player crits would not be applied (Wliu from Spyper)"); DrawPanelText(panel, "10) Fixed not being able to suicide as boss after round end (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 51: //1.10.3 { DrawPanelText(panel, "11) Updated Russian translations (wasder) and added German translations (CooliMC)"); DrawPanelText(panel, "12) Fixed Dead Ringer deaths being too obvious (Wliu from AliceTaylor12)"); DrawPanelText(panel, "13) Fixed many bosses not voicing their catch phrases (Wliu)"); DrawPanelText(panel, "14) Updated Gentlespy, Easter Bunny, Demopan, and CBS (Wliu, configs need to be updated)"); DrawPanelText(panel, "15) [Server] Added new cvar 'ff2_countdown_result' (Wliu from Shadow)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 50: //1.10.3 { DrawPanelText(panel, "16) [Server] Added new cvar 'ff2_caber_detonations' (Wliu)"); DrawPanelText(panel, "17) [Server] Fixed a bug related to 'cvar_countdown_players' and the countdown timer (Wliu from Spyper)"); DrawPanelText(panel, "18) [Server] Fixed 'nextmap_charset' VFormat errors (Wliu from BBG_Theory)"); DrawPanelText(panel, "19) [Server] Fixed errors when Monoculus was attacking (Wliu from ClassicGuzzi)"); DrawPanelText(panel, "20) [Dev] Added 'sound_first_blood' (Wliu from Mr-Bro)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 49: //1.10.3 { DrawPanelText(panel, "21) [Dev] Added 'pickups' to set what the boss can pick up (Wliu)"); DrawPanelText(panel, "22) [Dev] Added FF2FLAG_ALLOW_{HEALTH|AMMO}_PICKUPS (Powerlord)"); DrawPanelText(panel, "23) [Dev] Added FF2_GetFF2Version (Wliu)"); DrawPanelText(panel, "24) [Dev] Added FF2_ShowSync{Hud}Text wrappers (Wliu)"); DrawPanelText(panel, "25) [Dev] Added FF2_SetAmmo and fixed setting clip (Wliu/friagram for fixing clip)"); DrawPanelText(panel, "26) [Dev] Fixed weapons not being hidden when asked to (friagram)"); DrawPanelText(panel, "27) [Dev] Fixed not being able to set constant health values for bosses (Wliu from braak0405)"); } case 48: //1.10.2 { DrawPanelText(panel, "1) Fixed a critical bug that rendered most bosses as errors without sound (Wliu; thanks to slavko17 for reporting)"); DrawPanelText(panel, "2) Reverted escape sequences change, which is what caused this bug"); } case 47: //1.10.1 { DrawPanelText(panel, "1) Fixed a rare bug where rage could go over 100% (Wliu)"); DrawPanelText(panel, "2) Updated to use Sourcemod 1.6.1 (Powerlord)"); DrawPanelText(panel, "3) Fixed goomba stomp ignoring demoshields (Wliu)"); DrawPanelText(panel, "4) Disabled boss from spectating (Wliu)"); DrawPanelText(panel, "5) Fixed some possible overlapping HUD text (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 46: //1.10.1 { DrawPanelText(panel, "6) Fixed ff2_charset displaying incorrect colors (Wliu)"); DrawPanelText(panel, "7) Boss info text now also displays in the chat area (Wliu)"); DrawPanelText(panel, "--Partially synced with VSH 1.49 (all VSH changes listed courtesy of Chdata)--"); DrawPanelText(panel, "8) VSH: Do not show HUD text if the scoreboard is open"); DrawPanelText(panel, "9) VSH: Added market gardener 'backstab'"); DrawPanelText(panel, "See next page for more (press 1)"); } case 45: //1.10.1 { DrawPanelText(panel, "10) VSH: Removed Darwin's Danger Shield from the blacklist (Chdata) and gave it a +50 health bonus (Wliu)"); DrawPanelText(panel, "11) VSH: Rebalanced Phlogistinator"); DrawPanelText(panel, "12) VSH: Improved backstab code"); DrawPanelText(panel, "13) VSH: Added ff2_shield_crits cvar to control whether or not demomen get crits when using shields"); DrawPanelText(panel, "14) VSH: Reserve Shooter now deals crits to bosses in mid-air"); DrawPanelText(panel, "See next page for more (press 1)"); } case 44: //1.10.1 { DrawPanelText(panel, "15) [Server] Fixed conditions still being added when FF2 was disabled (Wliu)"); DrawPanelText(panel, "16) [Server] Fixed a rare healthbar error (Wliu)"); DrawPanelText(panel, "17) [Server] Added convar ff2_boss_suicide to control whether or not the boss can suicide after the round starts (Wliu)"); DrawPanelText(panel, "18) [Server] Changed ff2_boss_teleporter's default value to 0 (Wliu)"); DrawPanelText(panel, "19) [Server] Updated translations (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 43: //1.10.1 { DrawPanelText(panel, "20) [Dev] Added FF2_GetAlivePlayers and FF2_GetBossPlayers (Wliu/AliceTaylor)"); DrawPanelText(panel, "21) [Dev] Fixed a bug in the main include file (Wliu)"); DrawPanelText(panel, "22) [Dev] Enabled escape sequences in configs (Wliu)"); } case 42: //1.10.0 { DrawPanelText(panel, "1) Rage is now activated by calling for medic (Wliu)"); DrawPanelText(panel, "2) Balanced Goomba Stomp and RTD (WildCard65)"); DrawPanelText(panel, "3) Fixed BGM not stopping if the boss suicides at the beginning of the round (Wliu)"); DrawPanelText(panel, "4) Fixed Jarate, etc. not disappearing immediately on the boss (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 41: //1.10.0 { DrawPanelText(panel, "5) Fixed ability timers not resetting when the round was over (Wliu)"); DrawPanelText(panel, "6) Fixed bosses losing momentum when raging in the air (Wliu)"); DrawPanelText(panel, "7) Fixed bosses losing health if their companion left at round start (Wliu)"); DrawPanelText(panel, "8) Fixed bosses sometimes teleporting to each other if they had a companion (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 40: //1.10.0 { DrawPanelText(panel, "9) Optimized the health calculation system (WildCard65)"); DrawPanelText(panel, "10) Slightly tweaked default boss health formula to be more balanced (Eggman)"); DrawPanelText(panel, "11) Fixed and optimized the leaderboard (Wliu)"); DrawPanelText(panel, "12) Fixed medic minions receiving the medigun (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 39: //1.10.0 { DrawPanelText(panel, "13) Fixed Ninja Spy slow-mo bugs (Wliu/Powerlord)"); DrawPanelText(panel, "14) Prevented players from changing to the incorrect team or class (Powerlord/Wliu)"); DrawPanelText(panel, "15) Fixed bosses immediately dying after using the dead ringer (Wliu)"); DrawPanelText(panel, "16) Fixed a rare bug where you could get notified about being the next boss multiple times (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 38: //1.10.0 { DrawPanelText(panel, "17) Fixed gravity not resetting correctly after a weighdown if using non-standard gravity (Wliu)"); DrawPanelText(panel, "18) [Server] FF2 now properly disables itself when required (Wliu/Powerlord)"); DrawPanelText(panel, "19) [Server] Added ammo, clip, and health arguments to rage_cloneattack (Wliu)"); DrawPanelText(panel, "20) [Server] Changed how BossCrits works...again (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 37: //1.10.0 { DrawPanelText(panel, "21) [Server] Removed convar ff2_halloween (Wliu)"); DrawPanelText(panel, "22) [Server] Moved convar ff2_oldjump to the main config file (Wliu)"); DrawPanelText(panel, "23) [Server] Added convar ff2_countdown_players to control when the timer should appear (Wliu/BBG_Theory)"); DrawPanelText(panel, "24) [Server] Added convar ff2_updater to control whether automatic updating should be turned on (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 36: //1.10.0 { DrawPanelText(panel, "25) [Server] Added convar ff2_goomba_jump to control how high players should rebound after goomba stomping the boss (WildCard65)"); DrawPanelText(panel, "26) [Server] Fixed hale_point_enable/disable being registered twice (Wliu)"); DrawPanelText(panel, "27) [Server] Fixed some convars not executing (Wliu)"); DrawPanelText(panel, "28) [Server] Fixed the chances and charset systems (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 35: //1.10.0 { DrawPanelText(panel, "29) [Dev] Added more natives and one additional forward (Eggman)"); DrawPanelText(panel, "30) [Dev] Added sound_full_rage which plays once the boss is able to rage (Wliu/Eggman)"); DrawPanelText(panel, "31) [Dev] Fixed FF2FLAG_ISBUFFED (Wliu)"); DrawPanelText(panel, "32) [Dev] FF2 now checks for sane values for \"lives\" and \"health_formula\" (Wliu)"); DrawPanelText(panel, "Big thanks to GIANT_CRAB, WildCard65, and kniL for their devotion to this release!"); } case 34: //1.9.3 { DrawPanelText(panel, "1) Fixed a bug in 1.9.2 where the changelog was off by one version (Wliu)"); DrawPanelText(panel, "2) Fixed a bug in 1.9.2 where one dead player would not be cloned in rage_cloneattack (Wliu)"); DrawPanelText(panel, "3) Fixed a bug in 1.9.2 where sentries would be permanently disabled after a rage (Wliu)"); DrawPanelText(panel, "4) [Server] Removed ff2_halloween (Wliu)"); } case 33: //1.9.2 { DrawPanelText(panel, "1) Fixed a bug in 1.9.1 that allowed the same player to be the boss over and over again (Wliu)"); DrawPanelText(panel, "2) Fixed a bug where last player glow was being incorrectly removed on the boss (Wliu)"); DrawPanelText(panel, "3) Fixed a bug where the boss would be assumed dead (Wliu)"); DrawPanelText(panel, "4) Fixed having minions on the boss team interfering with certain rage calculations (Wliu)"); DrawPanelText(panel, "See next page for more (press 1)"); } case 32: //1.9.2 { DrawPanelText(panel, "5) Fixed a rare bug where the rage percentage could go above 100% (Wliu)"); DrawPanelText(panel, "6) [Server] Fixed possible special_noanims errors (Wliu)"); DrawPanelText(panel, "7) [Server] Added new arguments to rage_cloneattack-no updates necessary (friagram/Wliu)"); DrawPanelText(panel, "8) [Server] Certain cvars that SMAC detects are now automatically disabled while FF2 is running (Wliu)"); DrawPanelText(panel, " Servers can now safely have smac_cvars enabled"); } case 31: //1.9.1 { DrawPanelText(panel, "1) Fixed some minor leaderboard bugs and also improved the leaderboard text (Wliu)"); DrawPanelText(panel, "2) Fixed a minor round end bug (Wliu)"); DrawPanelText(panel, "3) [Server] Fixed improper unloading of subplugins (WildCard65)"); DrawPanelText(panel, "4) [Server] Removed leftover console messages (Wliu)"); DrawPanelText(panel, "5) [Server] Fixed sound not precached warnings (Wliu)"); } case 30: //1.9.0 { DrawPanelText(panel, "1) Removed checkFirstHale (Wliu)"); DrawPanelText(panel, "2) [Server] Fixed invalid healthbar entity bug (Wliu)"); DrawPanelText(panel, "3) Changed default medic ubercharge percentage to 40% (Wliu)"); DrawPanelText(panel, "4) Whitelisted festive variants of weapons (Wliu/BBG_Theory)"); DrawPanelText(panel, "5) [Server] Added convars to control last player glow and timer health cutoff (Wliu"); DrawPanelText(panel, "See next page (press 1)"); } case 29: //1.9.0 { DrawPanelText(panel, "6) [Dev] Added new natives/stocks: Debug, FF2_SetClientGlow and FF2_GetClientGlow (Wliu)"); DrawPanelText(panel, "7) Fixed a few minor !whatsnew bugs (BBG_Theory)"); DrawPanelText(panel, "8) Fixed Easter Abilities (Wliu)"); DrawPanelText(panel, "9) Minor grammar/spelling improvements (Wliu)"); DrawPanelText(panel, "10) [Server] Minor subplugin load/unload fixes (Wliu)"); } case 28: //1.0.8 { DrawPanelText(panel, "Wliu, Chris, Lawd, and Carge of 50DKP have taken over FF2 development"); DrawPanelText(panel, "1) Prevented spy bosses from changing disguises (Powerlord)"); DrawPanelText(panel, "2) Added Saxton Hale stab sounds (Powerlord/AeroAcrobat)"); DrawPanelText(panel, "3) Made sure that the boss doesn't have any invalid weapons/items (Powerlord)"); DrawPanelText(panel, "4) Tried fixing the visible weapon bug (Powerlord)"); DrawPanelText(panel, "5) Whitelisted some more action slot items (Powerlord)"); DrawPanelText(panel, "See next page (press 1)"); } case 27: //1.0.8 { DrawPanelText(panel, "6) Festive Huntsman has the same attributes as the Huntsman now (Powerlord)"); DrawPanelText(panel, "7) Medigun now overheals 50% more (Powerlord)"); DrawPanelText(panel, "8) Made medigun transparent if the medic's melee was the Gunslinger (Powerlord)"); DrawPanelText(panel, "9) Slight tweaks to the view hp commands (Powerlord)"); DrawPanelText(panel, "10) Whitelisted the Silver/Gold Botkiller Sniper Rifle Mk.II (Powerlord)"); DrawPanelText(panel, "11) Slight tweaks to boss health calculation (Powerlord)"); DrawPanelText(panel, "See next page (press 1)"); } case 26: //1.0.8 { DrawPanelText(panel, "12) Made sure that spies couldn't quick-backstab the boss (Powerlord)"); DrawPanelText(panel, "13) Made sure the stab animations were correct (Powerlord)"); DrawPanelText(panel, "14) Made sure that healthpacks spawned from the Candy Cane are not respawned once someone uses them (Powerlord)"); DrawPanelText(panel, "15) Healthpacks from the Candy Cane are no longer despawned (Powerlord)"); DrawPanelText(panel, "16) Slight tweaks to removing laughs (Powerlord)"); DrawPanelText(panel, "17) [Dev] Added a clip argument to special_noanims.sp (Powerlord)"); DrawPanelText(panel, "See next page (press 1)"); } case 25: //1.0.8 { DrawPanelText(panel, "18) [Dev] sound_bgm is now precached automagically (Powerlord)"); DrawPanelText(panel, "19) Seeldier's minions can no longer cap (Wliu)"); DrawPanelText(panel, "20) Fixed sometimes getting stuck when teleporting to a ducking player (Powerlord)"); DrawPanelText(panel, "21) Multiple English translation improvements (Wliu/Powerlord)"); DrawPanelText(panel, "22) Fixed Ninja Spy and other bosses that use the matrix ability getting stuck in walls/ceilings (Chris)"); DrawPanelText(panel, "23) [Dev] Updated item attributes code per the TF2Items update (Powerlord)"); DrawPanelText(panel, "See next page (press 1)"); } case 24: //1.0.8 { DrawPanelText(panel, "24) Fixed duplicate sound downloads for Saxton Hale (Wliu)"); DrawPanelText(panel, "25) [Server] FF2 now require morecolors, not colors (Powerlord)"); DrawPanelText(panel, "26) [Server] Added a Halloween mode which will enable characters_halloween.cfg (Wliu)"); DrawPanelText(panel, "27) Hopefully fixed multiple round-related issues (Wliu)"); DrawPanelText(panel, "28) [Dev] Started to clean up/format the code (Wliu)"); DrawPanelText(panel, "29) Changed versioning format to x.y.z and month day, year (Wliu)"); DrawPanelText(panel, "HAPPY HALLOWEEN!"); } case 23: //1.07 { DrawPanelText(panel, "1) [Players] Holiday Punch is now replaced by Fists"); DrawPanelText(panel, "2) [Players] Bosses will have any disguises removed on round start"); DrawPanelText(panel, "3) [Players] Bosses can no longer see all players health, as it wasn't working any more"); DrawPanelText(panel, "4) [Server] ff2_addpoints no longer targets SourceTV or replay"); } case 22: //1.07 beta 6 { DrawPanelText(panel, "1) [Dev] Fixed issue with sound hook not stopping sound when sound_block_vo was in use"); DrawPanelText(panel, "2) [Dev] If ff2_charset was used, don't run the character set vote"); DrawPanelText(panel, "3) [Dev] If a vote is already running, Character set vote will retry every 5 seconds or until map changes "); } case 21: //1.07 beta 5 { DrawPanelText(panel, "1) [Dev] Fixed issue with character sets not working."); DrawPanelText(panel, "2) [Dev] Improved IsValidClient replay check"); DrawPanelText(panel, "3) [Dev] IsValidClient is now called when loading companion bosses"); DrawPanelText(panel, " This should prevent GetEntProp issues with m_iClass"); } case 20: //1.07 beta 4 { DrawPanelText(panel, "1) [Players] Dead Ringers have no cloak defense buff. Normal cloaks do."); DrawPanelText(panel, "2) [Players] Fixed Sniper Rifle reskin behavior"); DrawPanelText(panel, "3) [Players] Boss has small amount of stun resistance after rage"); DrawPanelText(panel, "4) [Players] Various bugfixes and changes 1.7.0 beta 1"); } case 19: //1.07 beta { DrawPanelText(panel, "22) [Dev] Prevent boss rage from being activated if the boss is already taunting or is dead."); DrawPanelText(panel, "23) [Dev] Cache the result of the newer backstab detection"); DrawPanelText(panel, "24) [Dev] Reworked Medic damage code slightly"); } case 18: //1.07 beta { DrawPanelText(panel, "16) [Server] The Boss queue now accepts negative points."); DrawPanelText(panel, "17) [Server] Bosses can be forced to a specific team using the new ff2_force_team cvar."); DrawPanelText(panel, "18) [Server] Eureka Effect can now be enabled using the new ff2_enable_eureka cvar"); DrawPanelText(panel, "19) [Server] Bosses models and sounds are now precached the first time they are loaded."); DrawPanelText(panel, "20) [Dev] Fixed an issue where FF2 was trying to read cvars before config files were executed."); DrawPanelText(panel, " This change should also make the game a little more multi-mod friendly."); DrawPanelText(panel, "21) [Dev] Fixed OnLoadCharacterSet not being fired. This should fix the deadrun plugin."); DrawPanelText(panel, "Continued on next page"); } case 17: //1.07 beta { DrawPanelText(panel, "10) [Players] Heatmaker gains Focus on hit (varies by charge)"); DrawPanelText(panel, "11) [Players] Crusader's Crossbow damage has been adjusted to compensate for its speed increase."); DrawPanelText(panel, "12) [Players] Cozy Camper now gives you an SMG as well, but it has no crits and reduced damage."); DrawPanelText(panel, "13) [Players] Bosses get short defense buff after rage"); DrawPanelText(panel, "14) [Server] Now attempts to integrate tf2items config"); DrawPanelText(panel, "15) [Server] Changing the game description now requires Steam Tools"); DrawPanelText(panel, "Continued on next page"); } case 16: //1.07 beta { DrawPanelText(panel, "6) [Players] Removed crits from sniper rifles, now do 2.9x damage"); DrawPanelText(panel, " Sydney Sleeper does 2.4x damage, 2.9x if boss's rage is >90pct"); DrawPanelText(panel, " Minicrit- less damage, more knockback"); DrawPanelText(panel, "7) [Players] Baby Face's Blaster will fill boost normally, but will hit 100 and drain+minicrits."); DrawPanelText(panel, "8) [Players] Phlogistinator Pyros are invincible while activating the crit-boost taunt."); DrawPanelText(panel, "9) [Players] Can't Eureka+destroy dispenser to insta-teleport"); DrawPanelText(panel, "Continued on next page"); } case 15: //1.07 beta { DrawPanelText(panel, "1) [Players] Reworked the crit code a bit. Should be more reliable."); DrawPanelText(panel, "2) [Players] Help panel should stop repeatedly popping up on round start."); DrawPanelText(panel, "3) [Players] Backstab disguising should be smoother/less obvious"); DrawPanelText(panel, "4) [Players] Scaled sniper rifle glow time a bit better"); DrawPanelText(panel, "5) [Players] Fixed Dead Ringer spy death icon"); DrawPanelText(panel, "Continued on next page"); } case 14: //1.06h { DrawPanelText(panel, "1) [Players] Remove MvM powerup_bottle on Bosses. (RavensBro)"); } case 13: //1.06g { DrawPanelText(panel, "1) [Players] Fixed vote for charset. (RavensBro)"); } case 12: //1.06f { DrawPanelText(panel, "1) [Players] Changelog now divided into [Players] and [Dev] sections. (Otokiru)"); DrawPanelText(panel, "2) [Players] Don't bother reading [Dev] changelogs because you'll have no idea what it's stated. (Otokiru)"); DrawPanelText(panel, "3) [Players] Fixed civilian glitch. (Otokiru)"); DrawPanelText(panel, "4) [Players] Fixed hale HP bar. (Valve) lol?"); DrawPanelText(panel, "5) [Dev] Fixed \"GetEntProp\" reported: Entity XXX (XXX) is invalid on checkFirstHale(). (Otokiru)"); } case 11: //1.06e { DrawPanelText(panel, "1) [Players] Remove MvM water-bottle on hales. (Otokiru)"); DrawPanelText(panel, "2) [Dev] Fixed \"GetEntProp\" reported: Property \"m_iClass\" not found (entity 0/worldspawn) error on checkFirstHale(). (Otokiru)"); DrawPanelText(panel, "3) [Dev] Change how FF2 check for player weapons. Now also checks when spawned in the middle of the round. (Otokiru)"); DrawPanelText(panel, "4) [Dev] Changed some FF2 warning messages color such as \"First-Hale Checker\" and \"Change class exploit\". (Otokiru)"); } case 10: //1.06d { DrawPanelText(panel, "1) Fix first boss having missing health or abilities. (Otokiru)"); DrawPanelText(panel, "2) Health bar now goes away if the boss wins the round. (Powerlord)"); DrawPanelText(panel, "3) Health bar cedes control to Monoculus if he is summoned. (Powerlord)"); DrawPanelText(panel, "4) Health bar instantly updates if enabled or disabled via cvar mid-game. (Powerlord)"); } case 9: //1.06c { DrawPanelText(panel, "1) Remove weapons if a player tries to switch classes when they become boss to prevent an exploit. (Otokiru)"); DrawPanelText(panel, "2) Reset hale's queue points to prevent the 'retry' exploit. (Otokiru)"); DrawPanelText(panel, "3) Better detection of backstabs. (Powerlord)"); DrawPanelText(panel, "4) Boss now has optional life meter on screen. (Powerlord)"); } case 8: //1.06 { DrawPanelText(panel, "1) Fixed attributes key for weaponN block. Now 1 space needed for explode string."); DrawPanelText(panel, "2) Disabled vote for charset when there is only 1 not hidden chatset."); DrawPanelText(panel, "3) Fixed \"Invalid key value handle 0 (error 4)\" when when round starts."); DrawPanelText(panel, "4) Fixed ammo for special_noanims.ff2\\rage_new_weapon ability."); DrawPanelText(panel, "Coming soon: weapon balance will be moved into config file."); } case 7: //1.05 { DrawPanelText(panel, "1) Added \"hidden\" key for charsets."); DrawPanelText(panel, "2) Added \"sound_stabbed\" key for characters."); DrawPanelText(panel, "3) Mantread stomp deals 5x damage to Boss."); DrawPanelText(panel, "4) Minicrits will not play loud sound to all players"); DrawPanelText(panel, "5-11) See next page..."); } case 6: //1.05 { DrawPanelText(panel, "6) For mappers: Add info_target with name 'hale_no_music'"); DrawPanelText(panel, " to prevent Boss' music."); DrawPanelText(panel, "7) FF2 renames *.smx from plugins/freaks/ to *.ff2 by itself."); DrawPanelText(panel, "8) Third Degree hit adds uber to healers."); DrawPanelText(panel, "9) Fixed hard \"ghost_appearation\" in default_abilities.ff2."); DrawPanelText(panel, "10) FF2FLAG_HUDDISABLED flag blocks EVERYTHING of FF2's HUD."); DrawPanelText(panel, "11) Changed FF2_PreAbility native to fix bug about broken Boss' abilities."); } case 5: //1.04 { DrawPanelText(panel, "1) Seeldier's minions have protection (teleport) from pits for first 4 seconds after spawn."); DrawPanelText(panel, "2) Seeldier's minions correctly dies when owner-Seeldier dies."); DrawPanelText(panel, "3) Added multiplier for brave jump ability in char.configs (arg3, default is 1.0)."); DrawPanelText(panel, "4) Added config key sound_fail. It calls when Boss fails, but still alive"); DrawPanelText(panel, "4) Fixed potential exploits associated with feign death."); DrawPanelText(panel, "6) Added ff2_reload_subplugins command to reload FF2's subplugins."); } case 4: //1.03 { DrawPanelText(panel, "1) Finally fixed exploit about queue points."); DrawPanelText(panel, "2) Fixed non-regular bug with 'UTIL_SetModel: not precached'."); DrawPanelText(panel, "3) Fixed potential bug about reducing of Boss' health by healing."); DrawPanelText(panel, "4) Fixed Boss' stun when round begins."); } case 3: //1.02 { DrawPanelText(panel, "1) Added isNumOfSpecial parameter into FF2_GetSpecialKV and FF2_GetBossSpecial natives"); DrawPanelText(panel, "2) Added FF2_PreAbility forward. Plz use it to prevent FF2_OnAbility only."); DrawPanelText(panel, "3) Added FF2_DoAbility native."); DrawPanelText(panel, "4) Fixed exploit about queue points...ow wait, it done in 1.01"); DrawPanelText(panel, "5) ff2_1st_set_abilities.ff2 sets kac_enabled to 0."); DrawPanelText(panel, "6) FF2FLAG_HUDDISABLED flag disables Boss' HUD too."); DrawPanelText(panel, "7) Added FF2_GetQueuePoints and FF2_SetQueuePoints natives."); } case 2: //1.01 { DrawPanelText(panel, "1) Fixed \"classmix\" bug associated with Boss' class restoring."); DrawPanelText(panel, "3) Fixed other little bugs."); DrawPanelText(panel, "4) Fixed bug about instant kill of Seeldier's minions."); DrawPanelText(panel, "5) Now you can use name of Boss' file for \"companion\" Boss' keyvalue."); DrawPanelText(panel, "6) Fixed exploit when dead Boss can been respawned after his reconnect."); DrawPanelText(panel, "7-10) See next page..."); } case 1: //1.01 { DrawPanelText(panel, "7) I've missed 2nd item."); DrawPanelText(panel, "8) Fixed \"Random\" charpack, there is no vote if only one charpack."); DrawPanelText(panel, "9) Fixed bug when boss' music have a chance to DON'T play."); DrawPanelText(panel, "10) Fixed bug associated with ff2_enabled in cfg/sourcemod/FreakFortress2.cfg and disabling of pugin."); } case 0: //1.0 { DrawPanelText(panel, "1) Boss' health devided by 3,6 in medieval mode"); DrawPanelText(panel, "2) Restoring player's default class, after his round as Boss"); DrawPanelText(panel, "===UPDATES OF VS SAXTON HALE MODE==="); DrawPanelText(panel, "1) Added !ff2_resetqueuepoints command (also there is admin version)"); DrawPanelText(panel, "2) Medic is credited 100% of damage done during ubercharge"); DrawPanelText(panel, "3) If map changes mid-round, queue points not lost"); DrawPanelText(panel, "4) Dead Ringer will not be able to activate for 2s after backstab"); DrawPanelText(panel, "5) Added ff2_spec_force_boss cvar"); } default: { DrawPanelText(panel, "-- Somehow you've managed to find a glitched version page!"); DrawPanelText(panel, "-- Congratulations. Now go and fight!"); } } } static const maxVersion=sizeof(ff2versiontitles)-1; new Specials; new Handle:BossKV[MAXSPECIALS]; new Handle:PreAbility; new Handle:OnAbility; new Handle:OnMusic; new Handle:OnTriggerHurt; new Handle:OnSpecialSelected; new Handle:OnAddQueuePoints; new Handle:OnLoadCharacterSet; new Handle:OnLoseLife; new Handle:OnAlivePlayersChanged; new bool:bBlockVoice[MAXSPECIALS]; new Float:BossSpeed[MAXSPECIALS]; //new Float:BossRageDamage[MAXSPECIALS]; new String:ChancesString[512]; new chances[MAXSPECIALS*2]; //This is multiplied by two because it has to hold both the boss indices and chances new chancesIndex; public Plugin:myinfo= { name="Freak Fortress 2", author="Rainbolt Dash, FlaminSarge, Powerlord, the 50DKP team", description="RUUUUNN!! COWAAAARRDSS!", version=PLUGIN_VERSION, }; public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) { decl String:plugin[PLATFORM_MAX_PATH]; GetPluginFilename(myself, plugin, sizeof(plugin)); if(!StrContains(plugin, "freaks/")) //Prevent plugins/freaks/freak_fortress_2.ff2 from loading if it exists -.- { strcopy(error, err_max, "There is a duplicate copy of Freak Fortress 2 inside the /plugins/freaks folder. Please remove it"); return APLRes_Failure; } CreateNative("FF2_IsFF2Enabled", Native_IsEnabled); CreateNative("FF2_GetFF2Version", Native_FF2Version); CreateNative("FF2_GetBossUserId", Native_GetBoss); CreateNative("FF2_GetBossIndex", Native_GetIndex); CreateNative("FF2_GetBossTeam", Native_GetTeam); CreateNative("FF2_GetBossSpecial", Native_GetSpecial); CreateNative("FF2_GetBossHealth", Native_GetBossHealth); CreateNative("FF2_SetBossHealth", Native_SetBossHealth); CreateNative("FF2_GetBossMaxHealth", Native_GetBossMaxHealth); CreateNative("FF2_SetBossMaxHealth", Native_SetBossMaxHealth); CreateNative("FF2_GetBossLives", Native_GetBossLives); CreateNative("FF2_SetBossLives", Native_SetBossLives); CreateNative("FF2_GetBossMaxLives", Native_GetBossMaxLives); CreateNative("FF2_SetBossMaxLives", Native_SetBossMaxLives); CreateNative("FF2_GetBossCharge", Native_GetBossCharge); CreateNative("FF2_SetBossCharge", Native_SetBossCharge); CreateNative("FF2_GetBossRageDamage", Native_GetBossRageDamage); CreateNative("FF2_SetBossRageDamage", Native_SetBossRageDamage); CreateNative("FF2_GetClientDamage", Native_GetDamage); CreateNative("FF2_GetRoundState", Native_GetRoundState); CreateNative("FF2_GetSpecialKV", Native_GetSpecialKV); CreateNative("FF2_StopMusic", Native_StopMusic); CreateNative("FF2_GetRageDist", Native_GetRageDist); CreateNative("FF2_HasAbility", Native_HasAbility); CreateNative("FF2_DoAbility", Native_DoAbility); CreateNative("FF2_GetAbilityArgument", Native_GetAbilityArgument); CreateNative("FF2_GetAbilityArgumentFloat", Native_GetAbilityArgumentFloat); CreateNative("FF2_GetAbilityArgumentString", Native_GetAbilityArgumentString); CreateNative("FF2_RandomSound", Native_RandomSound); CreateNative("FF2_GetFF2flags", Native_GetFF2flags); CreateNative("FF2_SetFF2flags", Native_SetFF2flags); CreateNative("FF2_GetQueuePoints", Native_GetQueuePoints); CreateNative("FF2_SetQueuePoints", Native_SetQueuePoints); CreateNative("FF2_GetClientGlow", Native_GetClientGlow); CreateNative("FF2_SetClientGlow", Native_SetClientGlow); CreateNative("FF2_GetAlivePlayers", Native_GetAlivePlayers); //TODO: Deprecated, remove in 2.0.0 CreateNative("FF2_GetBossPlayers", Native_GetBossPlayers); //TODO: Deprecated, remove in 2.0.0 CreateNative("FF2_Debug", Native_Debug); PreAbility=CreateGlobalForward("FF2_PreAbility", ET_Hook, Param_Cell, Param_String, Param_String, Param_Cell, Param_CellByRef); //Boss, plugin name, ability name, slot, enabled OnAbility=CreateGlobalForward("FF2_OnAbility", ET_Hook, Param_Cell, Param_String, Param_String, Param_Cell); //Boss, plugin name, ability name, status OnMusic=CreateGlobalForward("FF2_OnMusic", ET_Hook, Param_String, Param_FloatByRef); OnTriggerHurt=CreateGlobalForward("FF2_OnTriggerHurt", ET_Hook, Param_Cell, Param_Cell, Param_FloatByRef); OnSpecialSelected=CreateGlobalForward("FF2_OnSpecialSelected", ET_Hook, Param_Cell, Param_CellByRef, Param_String); //Boss, character index, character name OnAddQueuePoints=CreateGlobalForward("FF2_OnAddQueuePoints", ET_Hook, Param_Array); OnLoadCharacterSet=CreateGlobalForward("FF2_OnLoadCharacterSet", ET_Hook, Param_CellByRef, Param_String); OnLoseLife=CreateGlobalForward("FF2_OnLoseLife", ET_Hook, Param_Cell, Param_CellByRef, Param_Cell); //Boss, lives left, max lives OnAlivePlayersChanged=CreateGlobalForward("FF2_OnAlivePlayersChanged", ET_Hook, Param_Cell, Param_Cell); //Players, bosses RegPluginLibrary("freak_fortress_2"); AskPluginLoad_VSH(); #if defined _steamtools_included MarkNativeAsOptional("Steam_SetGameDescription"); #endif #if defined _tf2attributes_included MarkNativeAsOptional("TF2Attrib_SetByDefIndex"); MarkNativeAsOptional("TF2Attrib_RemoveByDefIndex"); #endif return APLRes_Success; } public OnPluginStart() { LogMessage("===Freak Fortress 2 Initializing-v%s===", PLUGIN_VERSION); cvarVersion=CreateConVar("ff2_version", PLUGIN_VERSION, "Freak Fortress 2 Version", FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_DONTRECORD); cvarPointType=CreateConVar("ff2_point_type", "0", "0-Use ff2_point_alive, 1-Use ff2_point_time", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarPointDelay=CreateConVar("ff2_point_delay", "6", "Seconds to add to the point delay per player", FCVAR_PLUGIN); cvarAliveToEnable=CreateConVar("ff2_point_alive", "5", "The control point will only activate when there are this many people or less left alive", FCVAR_PLUGIN); cvarAnnounce=CreateConVar("ff2_announce", "120", "Amount of seconds to wait until FF2 info is displayed again. 0 to disable", FCVAR_PLUGIN, true, 0.0); cvarEnabled=CreateConVar("ff2_enabled", "1", "0-Disable FF2 (WHY?), 1-Enable FF2", FCVAR_PLUGIN|FCVAR_DONTRECORD, true, 0.0, true, 1.0); cvarCrits=CreateConVar("ff2_crits", "1", "Can Boss get crits?", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarFirstRound=CreateConVar("ff2_first_round", "-1", "This cvar is deprecated. Please use 'ff2_arena_rounds' instead by setting this cvar to -1", FCVAR_PLUGIN, true, -1.0, true, 1.0); //DEPRECATED cvarArenaRounds=CreateConVar("ff2_arena_rounds", "1", "Number of rounds to make arena before switching to FF2 (helps for slow-loading players)", FCVAR_PLUGIN, true, 0.0); cvarCircuitStun=CreateConVar("ff2_circuit_stun", "2", "Amount of seconds the Short Circuit stuns the boss for. 0 to disable", FCVAR_PLUGIN, true, 0.0); cvarCountdownPlayers=CreateConVar("ff2_countdown_players", "1", "Amount of players until the countdown timer starts (0 to disable)", FCVAR_PLUGIN, true, 0.0); cvarCountdownTime=CreateConVar("ff2_countdown", "120", "Amount of seconds until the round ends in a stalemate", FCVAR_PLUGIN); cvarCountdownHealth=CreateConVar("ff2_countdown_health", "2000", "Amount of health the Boss has remaining until the countdown stops", FCVAR_PLUGIN, true, 0.0); cvarCountdownResult=CreateConVar("ff2_countdown_result", "0", "0-Kill players when the countdown ends, 1-End the round in a stalemate", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarSpecForceBoss=CreateConVar("ff2_spec_force_boss", "0", "0-Spectators are excluded from the queue system, 1-Spectators are counted in the queue system", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarEnableEurekaEffect=CreateConVar("ff2_enable_eureka", "0", "0-Disable the Eureka Effect, 1-Enable the Eureka Effect", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarForceBossTeam=CreateConVar("ff2_force_team", "0", "0-Boss is always on Blu, 1-Boss is on a random team each round, 2-Boss is always on Red", FCVAR_PLUGIN, true, 0.0, true, 3.0); cvarHealthBar=CreateConVar("ff2_health_bar", "0", "0-Disable the health bar, 1-Show the health bar", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarLastPlayerGlow=CreateConVar("ff2_last_player_glow", "1", "0-Don't outline the last player, 1-Outline the last player alive", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarBossTeleporter=CreateConVar("ff2_boss_teleporter", "0", "-1 to disallow all bosses from using teleporters, 0 to use TF2 logic, 1 to allow all bosses", FCVAR_PLUGIN, true, -1.0, true, 1.0); cvarBossSuicide=CreateConVar("ff2_boss_suicide", "0", "Allow the boss to suicide after the round starts?", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarPreroundBossDisconnect=CreateConVar("ff2_replace_disconnected_boss", "1", "If a boss disconnects before the round starts, use the next player in line instead? 0 - No, 1 - Yes", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarCaberDetonations=CreateConVar("ff2_caber_detonations", "5", "Amount of times somebody can detonate the Ullapool Caber", FCVAR_PLUGIN); cvarShieldCrits=CreateConVar("ff2_shield_crits", "0", "0 to disable grenade launcher crits when equipping a shield, 1 for minicrits, 2 for crits", FCVAR_PLUGIN, true, 0.0, true, 2.0); cvarGoombaDamage=CreateConVar("ff2_goomba_damage", "0.05", "How much the Goomba damage should be multipled by when goomba stomping the boss (requires Goomba Stomp)", FCVAR_PLUGIN, true, 0.01, true, 1.0); cvarGoombaRebound=CreateConVar("ff2_goomba_jump", "300.0", "How high players should rebound after goomba stomping the boss (requires Goomba Stomp)", FCVAR_PLUGIN, true, 0.0); cvarBossRTD=CreateConVar("ff2_boss_rtd", "0", "Can the boss use rtd? 0 to disallow boss, 1 to allow boss (requires RTD)", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarUpdater=CreateConVar("ff2_updater", "1", "0-Disable Updater support, 1-Enable automatic updating (recommended, requires Updater)", FCVAR_PLUGIN, true, 0.0, true, 1.0); cvarDebug=CreateConVar("ff2_debug", "0", "0-Disable FF2 debug output, 1-Enable debugging (not recommended)", FCVAR_PLUGIN, true, 0.0, true, 1.0); //The following are used in various subplugins CreateConVar("ff2_oldjump", "0", "Use old Saxton Hale jump equations", FCVAR_PLUGIN, true, 0.0, true, 1.0); CreateConVar("ff2_base_jumper_stun", "0", "Whether or not the Base Jumper should be disabled when a player gets stunned", FCVAR_PLUGIN, true, 0.0, true, 1.0); HookEvent("teamplay_round_start", event_round_start); HookEvent("teamplay_round_win", event_round_end); HookEvent("player_spawn", event_player_spawn, EventHookMode_Pre); HookEvent("player_death", OnPlayerDeath, EventHookMode_Pre); HookEvent("player_chargedeployed", event_uber_deployed); HookEvent("player_hurt", event_hurt, EventHookMode_Pre); HookEvent("object_destroyed", event_destroy, EventHookMode_Pre); HookEvent("object_deflected", event_deflect, EventHookMode_Pre); HookEvent("deploy_buff_banner", OnDeployBackup); HookEvent("rocket_jump", OnRocketJump); HookEvent("rocket_jump_landed", OnRocketJump); AddCommandListener(OnCallForMedic, "voicemenu"); //Used to activate rages AddCommandListener(OnSuicide, "explode"); //Used to stop boss from suiciding AddCommandListener(OnSuicide, "kill"); //Used to stop boss from suiciding AddCommandListener(OnJoinTeam, "jointeam"); //Used to make sure players join the right team AddCommandListener(OnChangeClass, "joinclass"); //Used to make sure bosses don't change class HookConVarChange(cvarEnabled, CvarChange); HookConVarChange(cvarPointDelay, CvarChange); HookConVarChange(cvarAnnounce, CvarChange); HookConVarChange(cvarPointType, CvarChange); HookConVarChange(cvarPointDelay, CvarChange); HookConVarChange(cvarAliveToEnable, CvarChange); HookConVarChange(cvarCrits, CvarChange); HookConVarChange(cvarCircuitStun, CvarChange); HookConVarChange(cvarHealthBar, HealthbarEnableChanged); HookConVarChange(cvarCountdownPlayers, CvarChange); HookConVarChange(cvarCountdownTime, CvarChange); HookConVarChange(cvarCountdownHealth, CvarChange); HookConVarChange(cvarLastPlayerGlow, CvarChange); HookConVarChange(cvarSpecForceBoss, CvarChange); HookConVarChange(cvarBossTeleporter, CvarChange); HookConVarChange(cvarShieldCrits, CvarChange); HookConVarChange(cvarCaberDetonations, CvarChange); HookConVarChange(cvarGoombaDamage, CvarChange); HookConVarChange(cvarGoombaRebound, CvarChange); HookConVarChange(cvarBossRTD, CvarChange); HookConVarChange(cvarUpdater, CvarChange); HookConVarChange(cvarNextmap=FindConVar("sm_nextmap"), CvarChangeNextmap); RegConsoleCmd("ff2", FF2Panel); RegConsoleCmd("ff2_hp", Command_GetHPCmd); RegConsoleCmd("ff2hp", Command_GetHPCmd); RegConsoleCmd("ff2_next", QueuePanelCmd); RegConsoleCmd("ff2next", QueuePanelCmd); RegConsoleCmd("ff2_classinfo", Command_HelpPanelClass); RegConsoleCmd("ff2classinfo", Command_HelpPanelClass); RegConsoleCmd("ff2_new", NewPanelCmd); RegConsoleCmd("ff2new", NewPanelCmd); RegConsoleCmd("ff2music", MusicTogglePanelCmd); RegConsoleCmd("ff2_music", MusicTogglePanelCmd); RegConsoleCmd("ff2voice", VoiceTogglePanelCmd); RegConsoleCmd("ff2_voice", VoiceTogglePanelCmd); RegConsoleCmd("ff2_resetpoints", ResetQueuePointsCmd); RegConsoleCmd("ff2resetpoints", ResetQueuePointsCmd); RegConsoleCmd("hale", FF2Panel); RegConsoleCmd("hale_hp", Command_GetHPCmd); RegConsoleCmd("halehp", Command_GetHPCmd); RegConsoleCmd("hale_next", QueuePanelCmd); RegConsoleCmd("halenext", QueuePanelCmd); RegConsoleCmd("hale_classinfo", Command_HelpPanelClass); RegConsoleCmd("haleclassinfo", Command_HelpPanelClass); RegConsoleCmd("hale_new", NewPanelCmd); RegConsoleCmd("halenew", NewPanelCmd); RegConsoleCmd("halemusic", MusicTogglePanelCmd); RegConsoleCmd("hale_music", MusicTogglePanelCmd); RegConsoleCmd("halevoice", VoiceTogglePanelCmd); RegConsoleCmd("hale_voice", VoiceTogglePanelCmd); RegConsoleCmd("hale_resetpoints", ResetQueuePointsCmd); RegConsoleCmd("haleresetpoints", ResetQueuePointsCmd); RegConsoleCmd("nextmap", Command_Nextmap); RegConsoleCmd("say", Command_Say); RegConsoleCmd("say_team", Command_Say); RegAdminCmd("ff2_special", Command_SetNextBoss, ADMFLAG_CHEATS, "Usage: ff2_special . Forces next round to use that boss"); RegAdminCmd("ff2_addpoints", Command_Points, ADMFLAG_CHEATS, "Usage: ff2_addpoints . Adds queue points to any player"); RegAdminCmd("ff2_point_enable", Command_Point_Enable, ADMFLAG_CHEATS, "Enable the control point if ff2_point_type is 0"); RegAdminCmd("ff2_point_disable", Command_Point_Disable, ADMFLAG_CHEATS, "Disable the control point if ff2_point_type is 0"); RegAdminCmd("ff2_stop_music", Command_StopMusic, ADMFLAG_CHEATS, "Stop any currently playing Boss music"); RegAdminCmd("ff2_resetqueuepoints", ResetQueuePointsCmd, ADMFLAG_CHEATS, "Reset a player's queue points"); RegAdminCmd("ff2_resetq", ResetQueuePointsCmd, ADMFLAG_CHEATS, "Reset a player's queue points"); RegAdminCmd("ff2_charset", Command_Charset, ADMFLAG_CHEATS, "Usage: ff2_charset . Forces FF2 to use a given character set"); RegAdminCmd("ff2_reload_subplugins", Command_ReloadSubPlugins, ADMFLAG_RCON, "Reload FF2's subplugins."); RegAdminCmd("hale_select", Command_SetNextBoss, ADMFLAG_CHEATS, "Usage: hale_select . Forces next round to use that boss"); RegAdminCmd("hale_special", Command_SetNextBoss, ADMFLAG_CHEATS, "Usage: hale_select . Forces next round to use that boss"); RegAdminCmd("hale_addpoints", Command_Points, ADMFLAG_CHEATS, "Usage: hale_addpoints . Adds queue points to any player"); RegAdminCmd("hale_point_enable", Command_Point_Enable, ADMFLAG_CHEATS, "Enable the control point if ff2_point_type is 0"); RegAdminCmd("hale_point_disable", Command_Point_Disable, ADMFLAG_CHEATS, "Disable the control point if ff2_point_type is 0"); RegAdminCmd("hale_stop_music", Command_StopMusic, ADMFLAG_CHEATS, "Stop any currently playing Boss music"); RegAdminCmd("hale_resetqueuepoints", ResetQueuePointsCmd, ADMFLAG_CHEATS, "Reset a player's queue points"); RegAdminCmd("hale_resetq", ResetQueuePointsCmd, ADMFLAG_CHEATS, "Reset a player's queue points"); AutoExecConfig(true, "FreakFortress2"); FF2Cookies=RegClientCookie("ff2_cookies_mk2", "", CookieAccess_Protected); jumpHUD=CreateHudSynchronizer(); rageHUD=CreateHudSynchronizer(); livesHUD=CreateHudSynchronizer(); abilitiesHUD=CreateHudSynchronizer(); timeleftHUD=CreateHudSynchronizer(); infoHUD=CreateHudSynchronizer(); decl String:oldVersion[64]; GetConVarString(cvarVersion, oldVersion, sizeof(oldVersion)); if(strcmp(oldVersion, PLUGIN_VERSION, false)) { PrintToServer("[FF2] Warning: Your config may be outdated. Back up tf/cfg/sourcemod/FreakFortress2.cfg and delete it, and this plugin will generate a new one that you can then modify to your original values."); } LoadTranslations("freak_fortress_2.phrases"); LoadTranslations("common.phrases"); AddNormalSoundHook(HookSound); AddMultiTargetFilter("@hale", BossTargetFilter, "all current Bosses", false); AddMultiTargetFilter("@!hale", BossTargetFilter, "all non-Boss players", false); AddMultiTargetFilter("@boss", BossTargetFilter, "all current Bosses", false); AddMultiTargetFilter("@!boss", BossTargetFilter, "all non-Boss players", false); #if defined _steamtools_included steamtools=LibraryExists("SteamTools"); #endif #if defined _goomba_included goomba=LibraryExists("goomba"); #endif #if defined _tf2attributes_included tf2attributes=LibraryExists("tf2attributes"); #endif } public bool:BossTargetFilter(const String:pattern[], Handle:clients) { new bool:non=StrContains(pattern, "!", false)!=-1; for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client) && FindValueInArray(clients, client)==-1) { if(Enabled && IsBoss(client)) { if(!non) { PushArrayCell(clients, client); } } else if(non) { PushArrayCell(clients, client); } } } return true; } public OnLibraryAdded(const String:name[]) { #if defined _steamtools_included if(!strcmp(name, "SteamTools", false)) { steamtools=true; } #endif #if defined _tf2attributes_included if(!strcmp(name, "tf2attributes", false)) { tf2attributes=true; } #endif #if defined _goomba_included if(!strcmp(name, "goomba", false)) { goomba=true; } #endif if(!strcmp(name, "smac", false)) { smac=true; } #if defined _updater_included && !defined DEV_REVISION if(StrEqual(name, "updater") && GetConVarBool(cvarUpdater)) { Updater_AddPlugin(UPDATE_URL); } #endif } public OnLibraryRemoved(const String:name[]) { #if defined _steamtools_included if(!strcmp(name, "SteamTools", false)) { steamtools=false; } #endif #if defined _tf2attributes_included if(!strcmp(name, "tf2attributes", false)) { tf2attributes=false; } #endif #if defined _goomba_included if(!strcmp(name, "goomba", false)) { goomba=false; } #endif if(!strcmp(name, "smac", false)) { smac=false; } #if defined _updater_included if(StrEqual(name, "updater")) { Updater_RemovePlugin(); } #endif } public OnConfigsExecuted() { tf_arena_use_queue=GetConVarInt(FindConVar("tf_arena_use_queue")); mp_teams_unbalance_limit=GetConVarInt(FindConVar("mp_teams_unbalance_limit")); tf_arena_first_blood=GetConVarInt(FindConVar("tf_arena_first_blood")); mp_forcecamera=GetConVarInt(FindConVar("mp_forcecamera")); if(IsFF2Map() && GetConVarBool(cvarEnabled)) { EnableFF2(); } else { DisableFF2(); } #if defined _updater_included && !defined DEV_REVISION if(LibraryExists("updater") && GetConVarBool(cvarUpdater)) { Updater_AddPlugin(UPDATE_URL); } #endif } public OnMapStart() { HPTime=0.0; MusicTimer=INVALID_HANDLE; doorCheckTimer=INVALID_HANDLE; RoundCount=0; for(new client; client<=MaxClients; client++) { KSpreeTimer[client]=0.0; FF2flags[client]=0; Incoming[client]=-1; } for(new specials; specials1.0) { CreateTimer(time, Timer_Announce, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); } CheckToChangeMapDoors(); MapHasMusic(true); FindCharacters(); strcopy(FF2CharSetString, 2, ""); if(smac && FindPluginByFile("smac_cvars.smx")!=INVALID_HANDLE) { ServerCommand("smac_removecvar sv_cheats"); ServerCommand("smac_removecvar host_timescale"); } bMedieval=FindEntityByClassname(-1, "tf_logic_medieval")!=-1 || bool:GetConVarInt(FindConVar("tf_medieval")); FindHealthBar(); #if defined _steamtools_included if(steamtools) { decl String:gameDesc[64]; Format(gameDesc, sizeof(gameDesc), "Freak Fortress 2 (%s)", PLUGIN_VERSION); Steam_SetGameDescription(gameDesc); } #endif changeGamemode=0; } public DisableFF2() { Enabled=false; Enabled2=false; DisableSubPlugins(); SetConVarInt(FindConVar("tf_arena_use_queue"), tf_arena_use_queue); SetConVarInt(FindConVar("mp_teams_unbalance_limit"), mp_teams_unbalance_limit); SetConVarInt(FindConVar("tf_arena_first_blood"), tf_arena_first_blood); SetConVarInt(FindConVar("mp_forcecamera"), mp_forcecamera); if(doorCheckTimer!=INVALID_HANDLE) { KillTimer(doorCheckTimer); doorCheckTimer=INVALID_HANDLE; } for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client)) { if(BossInfoTimer[client][1]!=INVALID_HANDLE) { KillTimer(BossInfoTimer[client][1]); BossInfoTimer[client][1]=INVALID_HANDLE; } } } if(MusicTimer!=INVALID_HANDLE) { KillTimer(MusicTimer); MusicTimer=INVALID_HANDLE; } if(smac && FindPluginByFile("smac_cvars.smx")!=INVALID_HANDLE) { ServerCommand("smac_addcvar sv_cheats replicated ban 0 0"); ServerCommand("smac_addcvar host_timescale replicated ban 1.0 1.0"); } #if defined _steamtools_included if(steamtools) { Steam_SetGameDescription("Team Fortress"); } #endif changeGamemode=0; } public FindCharacters() //TODO: Investigate KvGotoFirstSubKey; KvGotoNextKey { decl String:config[PLATFORM_MAX_PATH], String:key[4], String:charset[42]; Specials=0; BuildPath(Path_SM, config, PLATFORM_MAX_PATH, "configs/freak_fortress_2/characters.cfg"); if(!FileExists(config)) { LogError("[FF2] Freak Fortress 2 disabled-can not find characters.cfg!"); Enabled2=false; return; } new Handle:Kv=CreateKeyValues(""); FileToKeyValues(Kv, config); new NumOfCharSet=FF2CharSet; new Action:action=Plugin_Continue; Call_StartForward(OnLoadCharacterSet); Call_PushCellRef(NumOfCharSet); strcopy(charset, sizeof(charset), FF2CharSetString); Call_PushStringEx(charset, sizeof(charset), SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); Call_Finish(action); if(action==Plugin_Changed) { new i=-1; if(strlen(charset)) { KvRewind(Kv); for(i=0; ; i++) { KvGetSectionName(Kv, config, sizeof(config)); if(!strcmp(config, charset, false)) { FF2CharSet=i; strcopy(FF2CharSetString, PLATFORM_MAX_PATH, charset); KvGotoFirstSubKey(Kv); break; } if(!KvGotoNextKey(Kv)) { i=-1; break; } } } if(i==-1) { FF2CharSet=NumOfCharSet; for(i=0; i1.0 && Enabled2) { switch(announcecount) { case 1: { CPrintToChatAll("{olive}[FF2]{default} VS Saxton Hale/Freak Fortress 2 group: {olive}http://steamcommunity.com/groups/vssaxtonhale{default}"); } case 3: { CPrintToChatAll("{default} === Freak Fortress 2 v%s (based on VS Saxton Hale Mode by {olive}RainBolt Dash{default} and {olive}FlaminSarge{default}) === ", PLUGIN_VERSION); } case 4: { CPrintToChatAll("{olive}[FF2]{default} %t", "type_ff2_to_open_menu"); } case 5: { announcecount=0; CPrintToChatAll("{olive}[FF2]{default} %t", "ff2_last_update", PLUGIN_VERSION, ff2versiondates[maxVersion]); } default: { CPrintToChatAll("{olive}[FF2]{default} %t", "type_ff2_to_open_menu"); } } } return Plugin_Continue; } stock bool:IsFF2Map() { decl String:config[PLATFORM_MAX_PATH]; GetCurrentMap(currentmap, sizeof(currentmap)); if(FileExists("bNextMapToFF2")) { return true; } BuildPath(Path_SM, config, PLATFORM_MAX_PATH, "configs/freak_fortress_2/maps.cfg"); if(!FileExists(config)) { LogError("[FF2] Unable to find %s, disabling plugin.", config); return false; } new Handle:file=OpenFile(config, "r"); if(file==INVALID_HANDLE) { LogError("[FF2] Error reading maps from %s, disabling plugin.", config); return false; } new tries; while(ReadFileLine(file, config, sizeof(config)) && tries<100) { tries++; if(tries==100) { LogError("[FF2] Breaking infinite loop when trying to check the map."); return false; } Format(config, strlen(config)-1, config); if(!strncmp(config, "//", 2, false)) { continue; } if(!StrContains(currentmap, config, false) || !StrContains(config, "all", false)) { CloseHandle(file); return true; } } CloseHandle(file); return false; } stock bool:MapHasMusic(bool:forceRecalc=false) //SAAAAAARGE { static bool:hasMusic; static bool:found; if(forceRecalc) { found=false; hasMusic=false; } if(!found) { new entity=-1; decl String:name[64]; while((entity=FindEntityByClassname2(entity, "info_target"))!=-1) { GetEntPropString(entity, Prop_Data, "m_iName", name, sizeof(name)); if(!strcmp(name, "hale_no_music", false)) { hasMusic=true; } } found=true; } return hasMusic; } stock bool:CheckToChangeMapDoors() { if(!Enabled || !Enabled2) { return; } decl String:config[PLATFORM_MAX_PATH]; checkDoors=false; BuildPath(Path_SM, config, PLATFORM_MAX_PATH, "configs/freak_fortress_2/doors.cfg"); if(!FileExists(config)) { if(!strncmp(currentmap, "vsh_lolcano_pb1", 15, false)) { checkDoors=true; } return; } new Handle:file=OpenFile(config, "r"); if(file==INVALID_HANDLE) { if(!strncmp(currentmap, "vsh_lolcano_pb1", 15, false)) { checkDoors=true; } return; } while(!IsEndOfFile(file) && ReadFileLine(file, config, sizeof(config))) { Format(config, strlen(config)-1, config); if(!strncmp(config, "//", 2, false)) { continue; } if(StrContains(currentmap, config, false)!=-1 || !StrContains(config, "all", false)) { CloseHandle(file); checkDoors=true; return; } } CloseHandle(file); } public Action:event_round_start(Handle:event, const String:name[], bool:dontBroadcast) { if(changeGamemode==1) { EnableFF2(); } else if(changeGamemode==2) { DisableFF2(); } if(!GetConVarBool(cvarEnabled)) { #if defined _steamtools_included if(steamtools) { Steam_SetGameDescription("Team Fortress"); } #endif Enabled2=false; } Enabled=Enabled2; if(!Enabled) { return Plugin_Continue; } if(FileExists("bNextMapToFF2")) { DeleteFile("bNextMapToFF2"); } new bool:blueBoss; switch(GetConVarInt(cvarForceBossTeam)) { case 1: { blueBoss=bool:GetRandomInt(0, 1); } case 2: { blueBoss=false; } default: { blueBoss=true; } } if(blueBoss) { SetTeamScore(_:TFTeam_Red, GetTeamScore(OtherTeam)); SetTeamScore(_:TFTeam_Blue, GetTeamScore(BossTeam)); OtherTeam=_:TFTeam_Red; BossTeam=_:TFTeam_Blue; } else { SetTeamScore(_:TFTeam_Red, GetTeamScore(BossTeam)); SetTeamScore(_:TFTeam_Blue, GetTeamScore(OtherTeam)); OtherTeam=_:TFTeam_Blue; BossTeam=_:TFTeam_Red; } playing=0; for(new client=1; client<=MaxClients; client++) { Damage[client]=0; uberTarget[client]=-1; emitRageSound[client]=true; if(IsValidClient(client) && GetClientTeam(client)>_:TFTeam_Spectator) { playing++; } } if(GetClientCount()<=1 || playing<=1) //Not enough players D: { CPrintToChatAll("{olive}[FF2]{default} %t", "needmoreplayers"); Enabled=false; DisableSubPlugins(); SetControlPoint(true); return Plugin_Continue; } else if(RoundCountTFTeam_Spectator) { SetEntProp(client, Prop_Send, "m_lifeState", 2); if(toRed && team!=TFTeam_Red) { ChangeClientTeam(client, _:TFTeam_Red); } else if(!toRed && team!=TFTeam_Blue) { ChangeClientTeam(client, _:TFTeam_Blue); } SetEntProp(client, Prop_Send, "m_lifeState", 0); TF2_RespawnPlayer(client); toRed=!toRed; } } return Plugin_Continue; } for(new client; client<=MaxClients; client++) { Boss[client]=0; if(IsValidClient(client) && IsPlayerAlive(client) && !(FF2flags[client] & FF2FLAG_HASONGIVED)) { TF2_RespawnPlayer(client); } } Enabled=true; EnableSubPlugins(); CheckArena(); new bool:omit[MaxClients+1]; Boss[0]=GetClientWithMostQueuePoints(omit); omit[Boss[0]]=true; new bool:teamHasPlayers[TFTeam]; for(new client=1; client<=MaxClients; client++) //Find out if each team has at least one player on it { if(IsValidClient(client)) { new TFTeam:team=TFTeam:GetClientTeam(client); if(team>TFTeam_Spectator) { teamHasPlayers[team]=true; } if(teamHasPlayers[TFTeam_Blue] && teamHasPlayers[TFTeam_Red]) { break; } } } if(!teamHasPlayers[TFTeam_Blue] || !teamHasPlayers[TFTeam_Red]) //If there's an empty team make sure it gets populated { if(IsValidClient(Boss[0]) && GetClientTeam(Boss[0])!=BossTeam) { SetEntProp(Boss[0], Prop_Send, "m_lifeState", 2); ChangeClientTeam(Boss[0], BossTeam); SetEntProp(Boss[0], Prop_Send, "m_lifeState", 0); TF2_RespawnPlayer(Boss[0]); } for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client) && !IsBoss(client) && GetClientTeam(client)!=OtherTeam) { CreateTimer(0.1, MakeNotBoss, GetClientUserId(client)); } } return Plugin_Continue; } PickCharacter(0, 0); if((Special[0]<0) || !BossKV[Special[0]]) { LogError("[FF2] I just don't know what went wrong"); return Plugin_Continue; } KvRewind(BossKV[Special[0]]); BossRageDamage[0]=KvGetNum(BossKV[Special[0]], "ragedamage", 1900); if(BossRageDamage[0]<=0) { decl String:bossName[64]; KvGetString(BossKV[Special[0]], "name", bossName, sizeof(bossName)); PrintToServer("[FF2 Bosses] Warning: Boss %s's rage damage is 0 or below, setting to 1900", bossName); BossRageDamage[0]=1900; } BossLivesMax[0]=KvGetNum(BossKV[Special[0]], "lives", 1); if(BossLivesMax[0]<=0) { decl String:bossName[64]; KvGetString(BossKV[Special[0]], "name", bossName, sizeof(bossName)); PrintToServer("[FF2 Bosses] Warning: Boss %s has an invalid amount of lives, setting to 1", bossName); BossLivesMax[0]=1; } FindCompanion(0, playing, omit); //Find companions for the boss! for(new boss; boss<=MaxClients; boss++) { BossInfoTimer[boss][0]=INVALID_HANDLE; BossInfoTimer[boss][1]=INVALID_HANDLE; if(Boss[boss]) { BossInfoTimer[boss][0]=CreateTimer(30.2, BossInfoTimer_Begin, boss, TIMER_FLAG_NO_MAPCHANGE); } } CreateTimer(0.3, MakeBoss, 0, TIMER_FLAG_NO_MAPCHANGE); CreateTimer(3.5, StartResponseTimer, _, TIMER_FLAG_NO_MAPCHANGE); CreateTimer(9.1, StartBossTimer, _, TIMER_FLAG_NO_MAPCHANGE); CreateTimer(9.6, MessageTimer, _, TIMER_FLAG_NO_MAPCHANGE); for(new entity=MaxClients+1; entity=Damage[top[0]]) { top[2]=top[1]; top[1]=top[0]; top[0]=client; } else if(Damage[client]>=Damage[top[1]]) { top[2]=top[1]; top[1]=client; } else if(Damage[client]>=Damage[top[2]]) { top[2]=client; } } if(Damage[top[0]]>9000) { CreateTimer(1.0, Timer_NineThousand, _, TIMER_FLAG_NO_MAPCHANGE); } decl String:leaders[3][32]; for(new i; i<=2; i++) { if(IsValidClient(top[i])) { GetClientName(top[i], leaders[i], 32); } else { Format(leaders[i], 32, "---"); top[i]=0; } } SetHudTextParams(-1.0, 0.2, 10.0, 255, 255, 255, 255); PrintCenterTextAll(""); new String:text[128]; for(new client; client<=MaxClients; client++) { if(IsValidClient(client)) { SetGlobalTransTarget(client); //TODO: Clear HUD text here if(IsBoss(client)) { FF2_ShowSyncHudText(client, infoHUD, "%s\n%t:\n1) %i-%s\n2) %i-%s\n3) %i-%s\n\n%t", text, "top_3", Damage[top[0]], leaders[0], Damage[top[1]], leaders[1], Damage[top[2]], leaders[2], (bossWin ? "boss_win" : "boss_lose")); } else { FF2_ShowSyncHudText(client, infoHUD, "%s\n%t:\n1) %i-%s\n2) %i-%s\n3) %i-%s\n\n%t\n%t", text, "top_3", Damage[top[0]], leaders[0], Damage[top[1]], leaders[1], Damage[top[2]], leaders[2], "damage_fx", Damage[client], "scores", RoundFloat(Damage[client]/600.0)); } } } CreateTimer(3.0, Timer_CalcQueuePoints, _, TIMER_FLAG_NO_MAPCHANGE); UpdateHealthBar(); return Plugin_Continue; } public Action:Timer_NineThousand(Handle:timer) { EmitSoundToAll("saxton_hale/9000.wav", _, _, _, _, _, _, _, _, _, false); EmitSoundToAllExcept(SOUNDEXCEPT_VOICE, "saxton_hale/9000.wav", _, SNDCHAN_VOICE, _, _, _, _, _, _, _, false); EmitSoundToAllExcept(SOUNDEXCEPT_VOICE, "saxton_hale/9000.wav", _, SNDCHAN_VOICE, _, _, _, _, _, _, _, false); return Plugin_Continue; } public Action:Timer_CalcQueuePoints(Handle:timer) { new damage; botqueuepoints+=5; new add_points[MaxClients+1]; new add_points2[MaxClients+1]; for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client)) { damage=Damage[client]; new Handle:event=CreateEvent("player_escort_score", true); SetEventInt(event, "player", client); new points; while(damage-600>0) { damage-=600; points++; } SetEventInt(event, "points", points); FireEvent(event); if(IsBoss(client)) { if(IsFakeClient(client)) { botqueuepoints=0; } else { add_points[client]=-GetClientQueuePoints(client); add_points2[client]=add_points[client]; } } else if(!IsFakeClient(client) && (GetClientTeam(client)>_:TFTeam_Spectator || SpecForceBoss)) { add_points[client]=10; add_points2[client]=10; } } } new Action:action; Call_StartForward(OnAddQueuePoints); Call_PushArrayEx(add_points2, MaxClients+1, SM_PARAM_COPYBACK); Call_Finish(action); switch(action) { case Plugin_Stop, Plugin_Handled: { return; } case Plugin_Changed: { for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client)) { if(add_points2[client]>0) { CPrintToChat(client, "{olive}[FF2]{default} %t", "add_points", add_points2[client]); } SetClientQueuePoints(client, GetClientQueuePoints(client)+add_points2[client]); } } } default: { for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client)) { if(add_points[client]>0) { CPrintToChat(client, "{olive}[FF2]{default} %t", "add_points", add_points[client]); } SetClientQueuePoints(client, GetClientQueuePoints(client)+add_points[client]); } } } } } public Action:StartResponseTimer(Handle:timer) { decl String:sound[PLATFORM_MAX_PATH]; if(RandomSound("sound_begin", sound, PLATFORM_MAX_PATH)) { EmitSoundToAll(sound); EmitSoundToAll(sound); } return Plugin_Continue; } public Action:StartBossTimer(Handle:timer) { CreateTimer(0.1, Timer_Move, _, TIMER_FLAG_NO_MAPCHANGE); new bool:isBossAlive; for(new boss; boss<=MaxClients; boss++) { if(IsValidClient(Boss[boss]) && IsPlayerAlive(Boss[boss])) { isBossAlive=true; SetEntityMoveType(Boss[boss], MOVETYPE_NONE); } } if(!isBossAlive) { return Plugin_Continue; } playing=0; for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client) && !IsBoss(client) && IsPlayerAlive(client)) { playing++; CreateTimer(0.15, MakeNotBoss, GetClientUserId(client)); //TODO: Is this needed? } } for(new boss; boss<=MaxClients; boss++) { if(Boss[boss] && IsValidEdict(Boss[boss]) && IsPlayerAlive(Boss[boss])) { BossHealthMax[boss]=ParseFormula(boss, "health_formula", "(((760.8+n)*(n-1))^1.0341)+2046", RoundFloat(Pow((760.8+Float:playing)*(Float:playing-1.0), 1.0341)+2046.0)); BossLives[boss]=BossLivesMax[boss]; BossHealth[boss]=BossHealthMax[boss]*BossLivesMax[boss]; BossHealthLast[boss]=BossHealth[boss]; } } CreateTimer(0.2, BossTimer, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); CreateTimer(0.2, CheckAlivePlayers, _, TIMER_FLAG_NO_MAPCHANGE); CreateTimer(0.2, StartRound, _, TIMER_FLAG_NO_MAPCHANGE); CreateTimer(0.2, ClientTimer, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); CreateTimer(2.0, Timer_MusicPlay, 0, TIMER_FLAG_NO_MAPCHANGE); if(!PointType) { SetControlPoint(false); } return Plugin_Continue; } public Action:Timer_MusicPlay(Handle:timer, any:client) { if(CheckRoundState()!=1) { return Plugin_Continue; } if(MusicTimer!=INVALID_HANDLE) { KillTimer(MusicTimer); MusicTimer=INVALID_HANDLE; } if(timer!=INVALID_HANDLE && MapHasMusic()) { MusicIndex=-1; return Plugin_Continue; } KvRewind(BossKV[Special[0]]); if(KvJumpToKey(BossKV[Special[0]], "sound_bgm")) { decl String:music[PLATFORM_MAX_PATH]; MusicIndex=0; do { MusicIndex++; Format(music, 10, "time%i", MusicIndex); } while(KvGetFloat(BossKV[Special[0]], music, 0.0)>1); MusicIndex=GetRandomInt(1, MusicIndex-1); Format(music, 10, "time%i", MusicIndex); new Float:time=KvGetFloat(BossKV[Special[0]], music); Format(music, 10, "path%i", MusicIndex); KvGetString(BossKV[Special[0]], music, music, PLATFORM_MAX_PATH); new Action:action=Plugin_Continue; Call_StartForward(OnMusic); decl String:sound2[PLATFORM_MAX_PATH]; new Float:time2=time; strcopy(sound2, PLATFORM_MAX_PATH, music); Call_PushStringEx(sound2, PLATFORM_MAX_PATH, 0, SM_PARAM_COPYBACK); Call_PushFloatRef(time2); Call_Finish(action); switch(action) { case Plugin_Stop, Plugin_Handled: { strcopy(music, sizeof(music), ""); time=-1.0; } case Plugin_Changed: { strcopy(music, PLATFORM_MAX_PATH, sound2); time=time2; } } if(strlen(music[0])>5) { if(!client) { EmitSoundToAllExcept(SOUNDEXCEPT_MUSIC, music); } else if(CheckSoundException(client, SOUNDEXCEPT_MUSIC)) { EmitSoundToClient(client, music); } new userid; if(!client) { userid=0; } else { userid=GetClientUserId(client); } if(time>1) { MusicTimer=CreateTimer(time, Timer_MusicTheme, userid, TIMER_FLAG_NO_MAPCHANGE); } } } return Plugin_Continue; } public Action:Timer_MusicTheme(Handle:timer, any:userid) { MusicTimer=INVALID_HANDLE; if(Enabled && CheckRoundState()==1) { KvRewind(BossKV[Special[0]]); if(KvJumpToKey(BossKV[Special[0]], "sound_bgm")) { new client=GetClientOfUserId(userid); decl String:music[PLATFORM_MAX_PATH]; MusicIndex=0; do { MusicIndex++; Format(music, 10, "time%i",MusicIndex); } while(KvGetFloat(BossKV[Special[0]], music)>1); MusicIndex=GetRandomInt(1, MusicIndex-1); Format(music, 10, "time%i", MusicIndex); new Float:time=KvGetFloat(BossKV[Special[0]], music); Format(music, 10, "path%i", MusicIndex); KvGetString(BossKV[Special[0]], music, music, PLATFORM_MAX_PATH); new Action:action=Plugin_Continue; Call_StartForward(OnMusic); decl String:sound2[PLATFORM_MAX_PATH]; new Float:time2=time; strcopy(sound2, PLATFORM_MAX_PATH, music); Call_PushStringEx(sound2, PLATFORM_MAX_PATH, 0, SM_PARAM_COPYBACK); Call_PushFloatRef(time2); Call_Finish(action); switch(action) { case Plugin_Stop, Plugin_Handled: { strcopy(music, sizeof(music), ""); time=-1.0; } case Plugin_Changed: { strcopy(music, PLATFORM_MAX_PATH, sound2); time=time2; } } if(strlen(music[0])>5) { if(!client) { EmitSoundToAllExcept(SOUNDEXCEPT_MUSIC, music, _, _, _, _, _, _, _, _, _, false); } else if(CheckSoundException(client, SOUNDEXCEPT_MUSIC)) { EmitSoundToClient(client, music); } if(time>1) { MusicTimer=CreateTimer(time, Timer_MusicTheme, userid, TIMER_FLAG_NO_MAPCHANGE); } } } } else { return Plugin_Stop; } return Plugin_Continue; } StopMusic(target=0) { if(!BossKV[Special[0]]) { return; } KvRewind(BossKV[Special[0]]); if(KvJumpToKey(BossKV[Special[0]], "sound_bgm")) { decl String:music[PLATFORM_MAX_PATH]; Format(music, sizeof(music), "path%i", MusicIndex); KvGetString(BossKV[Special[0]], music, music, sizeof(music)); if(!target || target<0) //Stop music for all clients { for(target=1; target<=MaxClients; target++) { if(IsValidClient(target)) { StopSound(target, SNDCHAN_AUTO, music); StopSound(target, SNDCHAN_AUTO, music); } } } else { StopSound(target, SNDCHAN_AUTO, music); StopSound(target, SNDCHAN_AUTO, music); } } } stock EmitSoundToAllExcept(exceptiontype=SOUNDEXCEPT_MUSIC, const String:sample[], entity=SOUND_FROM_PLAYER, channel=SNDCHAN_AUTO, level=SNDLEVEL_NORMAL, flags=SND_NOFLAGS, Float:volume=SNDVOL_NORMAL, pitch=SNDPITCH_NORMAL, speakerentity=-1, const Float:origin[3]=NULL_VECTOR, const Float:dir[3]=NULL_VECTOR, bool:updatePos=true, Float:soundtime=0.0) { new clients[MaxClients], total; for(new client=1; client<=MaxClients; client++) { if(IsValidClient(client) && IsClientInGame(client)) { if(CheckSoundException(client, exceptiontype)) { clients[total++]=client; } } } if(!total) { return; } EmitSound(clients, total, sample, entity, channel, level, flags, volume, pitch, speakerentity, origin, dir, updatePos, soundtime); } stock CheckInfoCookies(client,infonum) { if(!IsValidClient(client)) return false; if(IsFakeClient(client)) return true; if(!AreClientCookiesCached(client)) return true; decl String:s[24]; decl String:ff2cookies_values[8][5]; GetClientCookie(client, FF2Cookies, s, 24); ExplodeString(s, " ", ff2cookies_values,8,5); new see=StringToInt(ff2cookies_values[4+infonum]); return (see>0 ? see : 0); } stock SetInfoCookies(client,infonum,value) { if(!IsValidClient(client)) return ; if(IsFakeClient(client)) return ; if(!AreClientCookiesCached(client)) return ; decl String:s[24]; decl String:ff2cookies_values[8][5]; GetClientCookie(client, FF2Cookies, s, 24); ExplodeString(s, " ", ff2cookies_values,8,5); Format(s,24,"%s %s %s %s",ff2cookies_values[0],ff2cookies_values[1],ff2cookies_values[2],ff2cookies_values[3]); for(new i;i