├── .gitignore ├── scripts ├── version.sh ├── install.sh ├── package.sh └── makememsearch.idc ├── .github └── workflows │ ├── compile.yml │ └── package.yml ├── configs └── randomizer │ ├── controls.cfg │ ├── reskins.cfg │ ├── weapons.cfg │ └── huds.cfg ├── README.md ├── scripting └── randomizer │ ├── weapons.sp │ ├── viewmodels.sp │ ├── group.sp │ ├── convar.sp │ ├── sdkcall.sp │ ├── controls.sp │ ├── event.sp │ ├── patch.sp │ ├── commands.sp │ ├── properties.sp │ ├── huds.sp │ ├── sdkhook.sp │ └── stocks.sp └── translations ├── ru └── randomizer.phrases.txt ├── randomizer.phrases.txt └── pt └── randomizer.phrases.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.smx 2 | *.bat 3 | *.exe 4 | build/* -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | # Go to build scripting folder with randomizer.sp 2 | cd build/addons/sourcemod/scripting 3 | 4 | # Get plugin version 5 | export PLUGIN_VERSION=$(sed -En '/#define PLUGIN_VERSION\W/p' randomizer.sp) 6 | echo "PLUGIN_VERSION<> $GITHUB_ENV 7 | echo $PLUGIN_VERSION | grep -o '[0-9]*\.[0-9]*\.[0-9]*' >> $GITHUB_ENV 8 | echo 'EOF' >> $GITHUB_ENV 9 | 10 | # Set revision to randomizer.sp 11 | sed -i -e 's/#define PLUGIN_VERSION_REVISION.*".*"/#define PLUGIN_VERSION_REVISION "'$PLUGIN_VERSION_REVISION'"/g' randomizer.sp -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | # Create build folder 2 | mkdir build 3 | cd build 4 | 5 | # Install SourceMod 6 | wget --input-file=http://sourcemod.net/smdrop/$SM_VERSION/sourcemod-latest-linux 7 | tar -xzf $(cat sourcemod-latest-linux) 8 | 9 | # Copy sp and compiler to build dir 10 | cp -r ../scripting addons/sourcemod 11 | cd addons/sourcemod/scripting 12 | 13 | # Install Dependencies 14 | wget "https://raw.githubusercontent.com/FlaminSarge/tf2attributes/master/scripting/include/tf2attributes.inc" -O include/tf2attributes.inc 15 | wget "https://raw.githubusercontent.com/nosoop/SM-TFEconData/master/scripting/include/tf_econ_data.inc" -O include/tf_econ_data.inc -------------------------------------------------------------------------------- /scripts/package.sh: -------------------------------------------------------------------------------- 1 | # Go to build dir 2 | cd build 3 | 4 | # Create package dir 5 | mkdir -p package/addons/sourcemod/plugins 6 | mkdir -p package/addons/sourcemod/configs 7 | mkdir -p package/addons/sourcemod/gamedata 8 | 9 | # Copy all required stuffs to package 10 | cp -r addons/sourcemod/plugins/randomizer.smx package/addons/sourcemod/plugins 11 | cp -r ../configs/randomizer package/addons/sourcemod/configs 12 | cp -r ../gamedata/randomizer.txt package/addons/sourcemod/gamedata 13 | cp -r ../translations package/addons/sourcemod 14 | cp -r ../LICENSE package 15 | 16 | # Create ZIP file 17 | cd package 18 | zip -r ../Randomizer-$PLUGIN_VERSION.$PLUGIN_VERSION_REVISION.zip * -------------------------------------------------------------------------------- /.github/workflows/compile.yml: -------------------------------------------------------------------------------- 1 | name: Compile 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | version: ["1.11"] 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v1 16 | 17 | - name: Environments 18 | run: | 19 | echo "SM_VERSION=${{ matrix.version }}" >> $GITHUB_ENV 20 | 21 | - name: Install 22 | run: | 23 | bash scripts/install.sh 24 | 25 | - name: Compile 26 | run: | 27 | cd build/addons/sourcemod/scripting 28 | ./spcomp -E randomizer.sp -o ../plugins/randomizer.smx -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: Package 2 | 3 | on: 4 | push: 5 | branches: master 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v1 14 | 15 | - name: Environments 16 | run: | 17 | echo "SM_VERSION=1.11" >> $GITHUB_ENV 18 | echo "PLUGIN_VERSION_REVISION<> $GITHUB_ENV 19 | git rev-list --count HEAD >> $GITHUB_ENV 20 | echo 'EOF' >> $GITHUB_ENV 21 | 22 | - name: Install 23 | run: | 24 | bash scripts/install.sh 25 | 26 | - name: Set Version 27 | run: | 28 | bash scripts/version.sh 29 | 30 | - name: Compile 31 | run: | 32 | cd build/addons/sourcemod/scripting 33 | ./spcomp randomizer.sp -o ../plugins/randomizer.smx 34 | 35 | - name: Package 36 | run: | 37 | bash scripts/package.sh 38 | 39 | - name: Release 40 | uses: softprops/action-gh-release@v2 41 | with: 42 | files: build/Randomizer-${{ env.PLUGIN_VERSION }}.${{ env.PLUGIN_VERSION_REVISION }}.zip 43 | tag_name: ${{ env.PLUGIN_VERSION }}.${{ env.PLUGIN_VERSION_REVISION }} 44 | generate_release_notes: true 45 | -------------------------------------------------------------------------------- /configs/randomizer/controls.cfg: -------------------------------------------------------------------------------- 1 | "Controls" 2 | { 3 | "attack2" 4 | { 5 | //List of weapons that does something on pessing attack2 (right click) 6 | //This is used to determine whenever if weapons in "passive" should or should not use attack2 7 | 8 | "classname" "tf_weapon_soda_popper" 9 | "classname" "tf_weapon_cleaver" 10 | "classname" "tf_weapon_bat_wood" 11 | "classname" "tf_weapon_bat_giftwrap" 12 | "classname" "tf_weapon_particle_cannon" 13 | "classname" "tf_weapon_flamethrower" 14 | "classname" "tf_weapon_rocketlauncher_fireball" 15 | "classname" "tf_weapon_flaregun_revenge" 16 | "classname" "tf_weapon_rocketpack" 17 | "classname" "tf_weapon_pipebomblauncher" 18 | "classname" "tf_wearable_demoshield" 19 | "classname" "tf_weapon_minigun" 20 | "classname" "tf_weapon_lunchbox" 21 | "classname" "tf_weapon_fists" 22 | "classname" "tf_weapon_shotgun_building_rescue" 23 | "classname" "tf_weapon_laser_pointer" 24 | "classname" "tf_weapon_mechanical_arm" 25 | "classname" "tf_weapon_builder" 26 | "classname" "tf_weapon_medigun" 27 | "classname" "tf_weapon_bonesaw" 28 | "classname" "tf_weapon_sniperrifle" 29 | "classname" "tf_weapon_compound_bow" 30 | "classname" "tf_weapon_sniperrifle_decap" 31 | "classname" "tf_weapon_sniperrifle_classic" 32 | "classname" "tf_weapon_charged_smg" 33 | "classname" "tf_weapon_grapplinghook" 34 | } 35 | 36 | "attack3" 37 | { 38 | //Weapons that does something on pessing attack3 (middle click) 39 | } 40 | 41 | "reload" 42 | { 43 | //Weapons that does something on pessing reload 44 | 45 | "attrib" "rage on kill" //Hitman Heatmaker 46 | "attrib" "medigun charge is resists" //Vaccinator 47 | "classname" "tf_weapon_pda_spy" //Disguise Kit 48 | } 49 | 50 | "passive" 51 | { 52 | //If player's active weapon is one of attack2 list, 53 | //These weapons can only be trigged by using given button instead of attack2 54 | 55 | "tf_weapon_pipebomblauncher" 56 | { 57 | "button" "reload" 58 | 59 | "textmain" "Controls_Stickybomb_Attack2" 60 | "textalt" "Controls_Stickybomb_Reload" 61 | "textnone" "Controls_Stickybomb_Unable" 62 | } 63 | 64 | "tf_wearable_demoshield" 65 | { 66 | "button" "reload" 67 | 68 | "textmain" "Controls_Shield_Attack2" 69 | "textalt" "Controls_Shield_Reload" 70 | "textnone" "Controls_Shield_Unable" 71 | } 72 | 73 | "tf_weapon_pda_engineer_build" 74 | { 75 | "button" "attack3" 76 | "cooldown" "0.5" 77 | 78 | "textmain" "Controls_Build_Attack2" 79 | "textalt" "Controls_Build_Attack3" 80 | "textnone" "Controls_Build_Unable" 81 | } 82 | 83 | "tf_weapon_invis" 84 | { 85 | "button" "attack3" 86 | "cooldown" "0.5" 87 | "invis" "1" 88 | 89 | "textmain" "Controls_InvisWatch_Attack2" 90 | "textalt" "Controls_InvisWatch_Attack3" 91 | "textnone" "Controls_InvisWatch_Unable" 92 | } 93 | 94 | "tf_weapon_grapplinghook" 95 | { 96 | //No passive button, need to activate it while holding it 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /configs/randomizer/reskins.cfg: -------------------------------------------------------------------------------- 1 | "Reskins" 2 | { 3 | //Multi-class 4 | //Shotgun 5 | "199" "1141 15003 15016 15044 15047 15085 15109 15132 15133 15152" 6 | 7 | //Pistol 8 | "209" "22 23 160 294 15013 15018 15035 15041 15046 15056 15060 15061 15100 15101 15102 15126 15148 30666" 9 | 10 | //Scout 11 | //Scattergun 12 | "13" "200 669 799 808 888 897 906 915 964 973 15015 15021 15029 15036 15053 15065 15069 15106 15107 15131 15151 15157" 13 | 14 | //Force-A-Nature 15 | "45" "1078" 16 | 17 | //Bonk! Atomic Punch 18 | "46" "1145" 19 | 20 | //Flying Guillotine 21 | "812" "833" 22 | 23 | //Bat 24 | "0" "190 660 30667" 25 | 26 | //Holy Mackerel 27 | "221" "572 999" 28 | 29 | //Boston Basher 30 | "325" "452" 31 | 32 | //Soldier 33 | //Rocket Launcher 34 | "18" "205 658 800 809 889 898 907 916 965 974 15006 15014 15028 15043 15052 15057 15081 15104 15105 15129 15130 15150" 35 | 36 | //Black Box 37 | "228" "1085" 38 | 39 | //Buff Banner 40 | "129" "1001" 41 | 42 | //Shovel 43 | "6" "196" 44 | 45 | //Pyro 46 | //Flame Thrower 47 | "21" "208 659 741 798 807 887 896 905 914 963 972 15005 15017 15030 15034 15049 15054 15066 15067 15068 15089 15090 15115 15141 30474" 48 | 49 | //Backburner 50 | "40" "1146" 51 | 52 | //Flare Gun 53 | "39" "1081" 54 | 55 | //Fire Axe 56 | "2" "192 739" 57 | 58 | //Axtinguisher 59 | "38" "457 1000" 60 | 61 | //Homewrecker 62 | "153" "466" 63 | 64 | //Demoman 65 | //Grenade Launcher 66 | "19" "206 1007 15077 15079 15091 15092 15116 15117 15142 15158" 67 | 68 | //Ali Baba Wee Booties 69 | "405" "608" 70 | 71 | //Stickybomb Launcher 72 | "20" "207 661 797 806 886 895 904 913 962 971 15009 15012 15024 15038 15045 15048 15082 15083 15084 15113 15137 15138 15155" 73 | 74 | //Bottle 75 | "1" "191 609" 76 | 77 | //Eyelander 78 | "132" "266 482 1082" 79 | 80 | //Heavy 81 | //Minigun 82 | "15" "202 298 654 793 802 882 891 900 909 958 967 15004 15020 15026 15031 15040 15055 15086 15087 15088 15098 15099 15123 15124 15125 15147" 83 | 84 | //Huo-Long Heater 85 | "811" "832" 86 | 87 | //Sandwich 88 | "42" "863 1002" 89 | 90 | //Dalokohs 91 | "159" "433" 92 | 93 | //Gloves of Running Urgently 94 | "239" "1084 1100" 95 | 96 | //Engineer 97 | //Wrangler 98 | "140" "1086 30668" 99 | 100 | //Wrench 101 | "7" "169 197 662 795 804 884 893 902 911 960 969 15073 15074 15075 15114 15139 15140 15156" 102 | 103 | //Medic 104 | //Syringe Gun 105 | "17" "204" 106 | 107 | //Crusader's Crossbow 108 | "305" "1079" 109 | 110 | //Medigun 111 | "29" "211 663 796 805 885 894 903 912 961 970 15008 15010 15025 15039 15050 15078 15097 15120 15121 15122 15145 15146" 112 | 113 | //Bonesaw 114 | "8" "198 1143" 115 | 116 | //Übersaw 117 | "37" "1003" 118 | 119 | //Sniper 120 | //Sniper Rifle 121 | "14" "201 664 792 801 851 881 890 899 908 957 966 15000 15007 15019 15023 15033 15059 15070 15071 15072 15111 15112 15135 15136 15154" 122 | 123 | //Huntsman 124 | "56" "1005 1092" 125 | 126 | //Machina 127 | "526" "30665" 128 | 129 | //SMG 130 | "16" "203 1149 15001 15022 15032 15037 15058 15076 15110 15134 15153" 131 | 132 | //Jarate 133 | "58" "1083 1105" 134 | 135 | //Kukri 136 | "3" "193" 137 | 138 | //Spy 139 | //Revolver 140 | "24" "210 161 1142 15011 15027 15042 15051 15062 15063 15064 15103 15127 15128 15149" 141 | 142 | //Ambassador 143 | "61" "1006" 144 | 145 | //Sapper 146 | "735" "736 933 1080 1102" 147 | 148 | //Red-Tape Recorder 149 | "810" "831" 150 | 151 | //Knife 152 | "4" "194 638 665 727 794 803 883 892 901 910 959 968 15080 15094 15095 15096 15118 15119 15143 15144" 153 | 154 | //Your Eternal Reward 155 | "225" "574" 156 | } 157 | -------------------------------------------------------------------------------- /scripts/makememsearch.idc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* makesig.idc: IDA script to automatically create and wildcard a function signature. 4 | * Copyright 2014, Asher Baker 5 | * 6 | * This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 9 | * 10 | * 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 11 | * 12 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 13 | * 14 | * 3. This notice may not be removed or altered from any source distribution. 15 | */ 16 | 17 | /* 18 | * This is originally makesig.idc, but modified by 42 (FortyTwoFortyTwo) as makememsearch.idc to automatically create and wildcard a memory signature. 19 | */ 20 | 21 | static main() 22 | { 23 | Wait(); // We won't work until autoanalysis is complete 24 | 25 | SetStatus(IDA_STATUS_WORK); 26 | 27 | auto pAddress = ScreenEA(); 28 | 29 | auto sig = "", found = 0; 30 | auto pAddressEnd = pAddress + 0x1F; 31 | 32 | while (pAddress != BADADDR) { 33 | auto pInfo = DecodeInstruction(pAddress); 34 | if (!pInfo) { 35 | Warning("Something went terribly wrong D:"); 36 | SetStatus(IDA_STATUS_READY); 37 | return; 38 | } 39 | 40 | auto i = 0, itemSize = ItemSize(pAddress), dtSize = GetDTSize(pInfo.Op0.dtype); 41 | 42 | if (pInfo.Op0.offb > 0 && pInfo.Op0.offb + dtSize <= itemSize) 43 | { 44 | //Store the instruction 45 | for (i = 0; i < pInfo.Op0.offb; i++) { 46 | sig = sig + sprintf("%02X ", Byte(pAddress + i)); 47 | } 48 | 49 | //Store operand value as wildcard 50 | sig = sig + PrintWildcards(dtSize); 51 | 52 | //Store any remaining 53 | for (i = pInfo.Op0.offb + dtSize; i < itemSize; i++) { 54 | sig = sig + sprintf("%02X ", Byte(pAddress + i)); 55 | } 56 | } 57 | else if (pInfo.n + dtSize == itemSize) 58 | { 59 | //Likely just n amount of instruction and dtsize amount of values 60 | for (i = 0; i < pInfo.n; i++) { 61 | sig = sig + sprintf("%02X ", Byte(pAddress + i)); 62 | } 63 | 64 | sig = sig + PrintWildcards(dtSize); 65 | } 66 | else 67 | { 68 | //Unknown, just wildcard addresses 69 | for (i = 0; i < itemSize; i++) { 70 | auto pLoc = pAddress + i; 71 | if ((GetFixupTgtType(pLoc) & FIXUP_MASK) == FIXUP_OFF32) { 72 | sig = sig + PrintWildcards(4); 73 | i = i + 3; 74 | } else { 75 | sig = sig + sprintf("%02X ", Byte(pLoc)); 76 | } 77 | } 78 | } 79 | 80 | if (IsGoodSig(sig)) { 81 | found = 1; 82 | break; 83 | } 84 | 85 | pAddress = NextHead(pAddress, pAddressEnd); 86 | } 87 | 88 | if (found == 0) { 89 | Warning("Ran out of bytes to create unique signature."); 90 | SetStatus(IDA_STATUS_READY); 91 | return; 92 | } 93 | 94 | auto len = strlen(sig) - 1, smsig = "\\x"; 95 | for (i = 0; i < len; i++) { 96 | auto c = substr(sig, i, i + 1); 97 | if (c == " ") { 98 | smsig = smsig + "\\x"; 99 | } else if (c == "?") { 100 | smsig = smsig + "2A"; 101 | } else { 102 | smsig = smsig + c; 103 | } 104 | } 105 | 106 | Message("Memory Search:\n%s\n%s\n", sig, smsig); 107 | 108 | SetStatus(IDA_STATUS_READY); 109 | return; 110 | } 111 | 112 | static GetDTSize(dtype) 113 | { 114 | if (dtype == dt_byte) { 115 | return 1; 116 | } else if (dtype == dt_word) { 117 | return 2; 118 | } else if (dtype == dt_dword) { 119 | return 4; 120 | } else if (dtype == dt_float) { 121 | return 4; 122 | } else if (dtype == dt_double) { 123 | return 8; 124 | } else if (dtype == dt_byte16) { 125 | return 16; 126 | } else { 127 | Warning("Unknown type size (%d)", dtype); 128 | return -1; 129 | } 130 | } 131 | 132 | static PrintWildcards(count) 133 | { 134 | auto i = 0, string = ""; 135 | for (i = 0; i < count; i++) { 136 | string = string + "? "; 137 | } 138 | 139 | return string; 140 | } 141 | 142 | static IsGoodSig(sig) 143 | { 144 | 145 | auto count = 0, addr; 146 | addr = FindBinary(addr, SEARCH_DOWN|SEARCH_NEXT, sig); 147 | while (count <= 2 && addr != BADADDR) { 148 | count = count + 1; 149 | addr = FindBinary(addr, SEARCH_DOWN|SEARCH_NEXT, sig); 150 | } 151 | 152 | //Message("%s(%d)\n", sig, count); 153 | 154 | return (count == 1); 155 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Randomizer [![Action Status](https://github.com/FortyTwoFortyTwo/Randomizer/workflows/Package/badge.svg)](https://github.com/FortyTwoFortyTwo/Randomizer/actions?query=workflow%3APackage+branch%3Amaster) 2 | 3 | Team Fortress 2 plugin that randomizes player loadout in any imaginable combinations, a rewritten of [TF2Items randomizer](https://forums.alliedmods.net/showthread.php?p=1308831). 4 | Supports all TF2 stock gamemodes excluding Mann Vs Machine, allow servers using ConVars to randomize specific players by whatever loadouts from whatever events. 5 | 6 | ## Builds 7 | All builds can be found in [releases](https://github.com/FortyTwoFortyTwo/Randomizer/releases) page, auto-built on every commits done in master branch. 8 | 9 | ## Requirements 10 | - SourceMod 1.11 11 | - [tf2attributes](https://forums.alliedmods.net/showthread.php?t=210221) 12 | - [tf_econ_data](https://forums.alliedmods.net/showthread.php?t=315011) 13 | 14 | ## ConVars 15 | - `randomizer_version`: Plugin version number, don't touch. 16 | - `randomizer_enabled`: Enable/Disable entire randomizer plugin, another option for load/unload plugins. 17 | - `randomizer_debug`: Enable/Disable debugging infos, not recommended to enable it. 18 | - `randomizer_class`: How should class be randomized. 19 | - `randomizer_weapons`: How should weapons be randomized. 20 | - `randomizer_cosmetics`: How should cosmetics be randomized. 21 | - `randomizer_rune`: How should mannpower rune be randomized. 22 | - `randomizer_spells`: How should halloween spells be randomized. 23 | - `randomizer_droppedweapons`: Enable/Disable dropped weapons. 24 | - `randomizer_fix_taunt`: Enable/Disable `CTFPlayer::Taunt` detour fix. 25 | - `randomizer_huds`: Mode to display weapons from hud. 26 | - 0: No hud display 27 | - 1: Hud text 28 | - 2: Menu 29 | 30 | For the convars that enables randomizations, several parameters can be set on how it should be randomized: 31 | - `trigger`: Who can trigger the reroll. 32 | - `group`: Group of players that will get rerolled. 33 | - `action`: How triggered player can reroll loadout. 34 | - `same`: Whenever everyone in group can get same loadout. (Does not work on cosmetics) 35 | - `count`: How many items at a minimum to give. (weapons and cosmetics only) 36 | - `count-primary`: How many primary items at a minimum to give. (weapons only) 37 | - `count-secondary`: How many secondary items at a minimum to give. (weapons only) 38 | - `count-melee`: How many melee items at a minimum to give. (weapons only) 39 | - `defaultclass`: Whenever items to give is only for default classes. (weapons only) 40 | - `conflicts`: Whenever to not generate item that conflicts with equipped items. (cosmetics only) 41 | 42 | List of possible ways to reroll loadout using `reroll` param: 43 | - `death`: Any deaths 44 | - `death-kill`: Death from player kill 45 | - `death-env`: Death from environment 46 | - `death-suicide`: Death from suicide 47 | - `kill`: Player kill 48 | - `assist`: Player assist 49 | - `round`: Round start 50 | - `round-full`: Full round start 51 | - `cp-capture`: Control point capture 52 | - `flag-capture`: Flag capture 53 | - `pass-score`: Passtime score 54 | 55 | Examples: 56 | - `randomizer_class "trigger=@all group=@me action=kill action=assist"`: Everyone's player kill or assist would reroll it's class. 57 | - `randomizer_weapons "trigger=@all group=@me action=death-kill action=round count-primary=1 count-secondary=1 count-melee=1"`: Everyone's death from player kill or round start would reroll it's weapons, one weapon for each slot. 58 | - `randomizer_cosmetics "trigger=@blue group=@blue action=death count=5"`: Everyone in blue team on any deaths would reroll it's cosmetics, having 5 total cosmetics equipped. 59 | - `randomizer_rune "trigger=@humans group=@bots action=cp-capture same=1"`: Every humans on capture would reroll all bot's mannpower runes to have same rune. 60 | - `randomizer_spells "trigger=@red group=@blue action=death, trigger=@blue group=@red action=cp-capture"`: On any red team's death, blue team spells is rerolled. And on blue team's capture, red team spells is rerolled. 61 | 62 | ## Commands 63 | - `sm_rndclass`: Set specified player a given class, admin only 64 | - `sm_rndsetweapon`: Replaces specified player all weapons to given weapons 65 | - `sm_rndsetslotweapon`: Replaces specified player weapons to given weapons based on slot 66 | - `sm_rndgiveweapon`: Gives specified player weapons 67 | - `sm_rndrune`: Set specified player a rune type 68 | - `sm_rndgenerate`: Rerolls specified player class and weapon def index 69 | 70 | Examples on parameters that is valid to use for `sm_rndsetweapon`, `sm_rndsetslotweapon` and `sm_rndgiveweapon`: 71 | - `220` for Shortstop (item def index) 72 | - `direct hit` for Direct Hit (name based in translations) 73 | - `backburner, loch, 424` for Backburner, Loch-n-Load and Tomislav 74 | - `primary shotgun, secondary panic attack` for Shotgun from primary slot and Panic Attack from secondary slot 75 | 76 | ## Configs 77 | There currently 5 [configs](https://github.com/FortyTwoFortyTwo/Randomizer/tree/master/configs/randomizer) able to easily change for future TF2 updates: 78 | - `controls.cfg`: Manages how weapons with `attack2` passive button should be handled, should it be `attack3` or `reload` instead. 79 | - `huds.cfg`: Lists all of the netprop meters to display in hud for many weapons. 80 | - `reskins.cfg`: List of reskins for players to equip weapon from loadout instead of default weapon. 81 | - `weapons.cfg`: Whitelist of weapon indexs to select from random pool, along with weapon name to display in HUD. 82 | 83 | ## TF2 Updates 84 | This plugin itself aims to not not require modifications when valve releases TF2 updates. 85 | Gamedatas is likely to break if TF2 update were to also break other plugin's gamedata (e.g. TF2Items). 86 | If weapon additions or rebalances were to happen, it's possible configs need an update, or possibly even plugin itself if TF2 update were to make changes only work for one class. -------------------------------------------------------------------------------- /configs/randomizer/weapons.cfg: -------------------------------------------------------------------------------- 1 | "Weapons" 2 | { 3 | "AllClass" 4 | { 5 | "199" "Weapon_Shotgun" 6 | "415" "Weapon_ReserveShooter" 7 | "1153" "Weapon_PanicAttack" 8 | "209" "Weapon_Pistol" 9 | 10 | "154" "Weapon_PainTrain" 11 | "357" "Weapon_HalfZatoichi" 12 | 13 | "13" "Weapon_Scattergun" 14 | "45" "Weapon_ForceANature" 15 | "220" "Weapon_Shortstop" 16 | "448" "Weapon_SodaPopper" 17 | "772" "Weapon_BabyFaceBlaster" 18 | "1103" "Weapon_BackScatter" 19 | 20 | "46" "Weapon_BonkAtomicPunch" 21 | "163" "Weapon_CritaCola" 22 | "222" "Weapon_MadMilk" 23 | "449" "Weapon_Winger" 24 | "773" "Weapon_PrettyBoyPocketPistol" 25 | "812" "Weapon_FlyingGuillotine" 26 | 27 | "0" "Weapon_Bat" 28 | "44" "Weapon_Sandman" 29 | "221" "Weapon_HolyMackerel" 30 | "317" "Weapon_CandyCane" 31 | "325" "Weapon_BostonBasher" 32 | "349" "Weapon_SunonaStick" 33 | "355" "Weapon_FanOWar" 34 | "450" "Weapon_Atomizer" 35 | "648" "Weapon_WrapAssassin" 36 | 37 | "18" "Weapon_RocketLauncher" 38 | "127" "Weapon_DirectHit" 39 | "228" "Weapon_BlackBox" 40 | "237" "Weapon_RocketJumper" 41 | "414" "Weapon_LibertyLauncher" 42 | "441" "Weapon_CowMangler5000" 43 | "513" "Weapon_Original" 44 | "730" "Weapon_BeggarBazooka" 45 | "1104" "Weapon_AirStrike" 46 | 47 | "129" "Weapon_BuffBanner" 48 | "133" "Weapon_Gunboats" 49 | "226" "Weapon_BattalionBackup" 50 | "354" "Weapon_Concheror" 51 | "442" "Weapon_RighteousBison" 52 | "444" "Weapon_Mantreads" 53 | "1101" "Weapon_BASEJumper" 54 | 55 | "6" "Weapon_Shovel" 56 | "128" "Weapon_Equalizer" 57 | "416" "Weapon_MarketGardener" 58 | "447" "Weapon_DisciplinaryAction" 59 | "775" "Weapon_EscapePlan" 60 | 61 | "21" "Weapon_FlameThrower" 62 | "40" "Weapon_Backburner" 63 | "215" "Weapon_Degreaser" 64 | "594" "Weapon_Phlogistinator" 65 | "1178" "Weapon_DragonFury" 66 | 67 | "39" "Weapon_FlareGun" 68 | "351" "Weapon_Detonator" 69 | "595" "Weapon_Manmelter" 70 | "740" "Weapon_ScorchShot" 71 | "1179" "Weapon_ThermalThruster" 72 | "1180" "Weapon_GasPasser" 73 | 74 | "2" "Weapon_FireAxe" 75 | "38" "Weapon_Axtinguisher" 76 | "153" "Weapon_Homewrecker" 77 | "214" "Weapon_Powerjack" 78 | "326" "Weapon_BackScratcher" 79 | "348" "Weapon_SharpenedVolcanoFragment" 80 | "593" "Weapon_ThirdDegree" 81 | "813" "Weapon_NeonAnnihilator" 82 | "1181" "Weapon_HotHand" 83 | 84 | "19" "Weapon_GrenadeLauncher" 85 | "308" "Weapon_LochnLoad" 86 | "405" "Weapon_AliBabaWeeBooties" 87 | "996" "Weapon_LooseCannon" 88 | "1101" "Weapon_BASEJumper" 89 | "1151" "Weapon_IronBomber" 90 | 91 | "20" "Weapon_StickybombLauncher" 92 | "130" "Weapon_ScottishResistance" 93 | "131" "Weapon_CharginTarge" 94 | "265" "Weapon_StickyJumper" 95 | "406" "Weapon_SplendidScreen" 96 | "1099" "Weapon_TideTurner" 97 | "1150" "Weapon_QuickiebombLauncher" 98 | 99 | "1" "Weapon_Bottle" 100 | "132" "Weapon_Eyelander" 101 | "172" "Weapon_ScotsmanSkullcutter" 102 | "307" "Weapon_UllapoolCaber" 103 | "327" "Weapon_ClaidheamhMor" 104 | "404" "Weapon_PersianPersuader" 105 | 106 | "15" "Weapon_Minigun" 107 | "41" "Weapon_Natascha" 108 | "312" "Weapon_BrassBeast" 109 | "424" "Weapon_Tomislav" 110 | "811" "Weapon_HuoLongHeater" 111 | 112 | "42" "Weapon_Sandvich" 113 | "159" "Weapon_DalokohsBar" 114 | "311" "Weapon_BuffaloSteakSandvich" 115 | "425" "Weapon_FamilyBusiness" 116 | "1190" "Weapon_SecondBanana" 117 | 118 | "5" "Weapon_Fists" 119 | "43" "Weapon_KillingGlovesofBoxing" 120 | "239" "Weapon_GlovesofRunningUrgently" 121 | "310" "Weapon_WarriorSpirit" 122 | "331" "Weapon_FistsofSteel" 123 | "426" "Weapon_EvictionNotice" 124 | "656" "Weapon_HolidayPunch" 125 | 126 | "141" "Weapon_FrontierJustice" 127 | "527" "Weapon_Widowmaker" 128 | "588" "Weapon_Pomson6000" 129 | "997" "Weapon_RescueRanger" 130 | "1153" "Weapon_PanicAttack" 131 | 132 | "140" "Weapon_Wrangler" 133 | "528" "Weapon_ShortCircuit" 134 | 135 | "7" "Weapon_Wrench" 136 | "142" "Weapon_Gunslinger" 137 | "155" "Weapon_SouthernHospitality" 138 | "329" "Weapon_Jag" 139 | "589" "Weapon_EurekaEffect" 140 | 141 | "17" "Weapon_SyringeGun" 142 | "36" "Weapon_Blutsauger" 143 | "305" "Weapon_CrusaderCrossbow" 144 | "412" "Weapon_Overdose" 145 | 146 | "29" "Weapon_MediGun" 147 | "35" "Weapon_Kritzkrieg" 148 | "411" "Weapon_QuickFix" 149 | "998" "Weapon_Vaccinator" 150 | 151 | "8" "Weapon_Bonesaw" 152 | "37" "Weapon_Ubersaw" 153 | "173" "Weapon_VitaSaw" 154 | "304" "Weapon_Amputator" 155 | "413" "Weapon_SolemnVow" 156 | 157 | "14" "Weapon_SniperRifle" 158 | "56" "Weapon_Huntsman" 159 | "230" "Weapon_SydneySleeper" 160 | "402" "Weapon_BazaarBargain" 161 | "526" "Weapon_Machina" 162 | "752" "Weapon_HitmanHeatmaker" 163 | "1098" "Weapon_Classic" 164 | 165 | "16" "Weapon_SMG" 166 | "57" "Weapon_Razorback" 167 | "58" "Weapon_Jarate" 168 | "231" "Weapon_DarwinDangerShield" 169 | "642" "Weapon_CozyCamper" 170 | "751" "Weapon_CleanerCarbine" 171 | 172 | "3" "Weapon_Kukri" 173 | "171" "Weapon_TribalmanShiv" 174 | "232" "Weapon_Bushwacka" 175 | "401" "Weapon_Shahanshah" 176 | 177 | "24" "Weapon_Revolver" 178 | "61" "Weapon_Ambassador" 179 | "224" "Weapon_LEtranger" 180 | "460" "Weapon_Enforcer" 181 | "525" "Weapon_Diamondback" 182 | 183 | "735" "Weapon_Sapper" 184 | "810" "Weapon_RedTapeRecorder" 185 | 186 | "4" "Weapon_Knife" 187 | "225" "Weapon_YourEternalReward" 188 | "356" "Weapon_ConniverKunai" 189 | "461" "Weapon_BigEarner" 190 | "649" "Weapon_Spycicle" 191 | } 192 | 193 | "DefaultClass" 194 | { 195 | "25" "Weapon_ConstructionPDA" 196 | 197 | "26" "Weapon_DestructionPDA" 198 | 199 | "28" "Weapon_Toolbox" 200 | 201 | "27" "Weapon_DisguiseKit" 202 | 203 | "30" "Weapon_InvisWatch" 204 | "59" "Weapon_DeadRinger" 205 | "60" "Weapon_CloakandDagger" 206 | } 207 | } -------------------------------------------------------------------------------- /scripting/randomizer/weapons.sp: -------------------------------------------------------------------------------- 1 | #define FILEPATH_CONFIG_WEAPONS "configs/randomizer/weapons.cfg" 2 | #define FILEPATH_CONFIG_RESKINS "configs/randomizer/reskins.cfg" 3 | 4 | static ArrayList g_aWeapons; 5 | static ArrayList g_aWeaponsClass[CLASS_MAX+1]; 6 | static ArrayList g_aWeaponsSlot[WeaponSlot_Building+1]; 7 | static ArrayList g_aWeaponsClassSlot[CLASS_MAX+1][WeaponSlot_Building+1]; 8 | 9 | static StringMap g_mWeaponsName; 10 | static StringMap g_mWeaponsReskins; 11 | 12 | public void Weapons_Init() 13 | { 14 | g_mWeaponsName = new StringMap(); 15 | g_mWeaponsReskins = new StringMap(); 16 | } 17 | 18 | public void Weapons_Refresh() 19 | { 20 | KeyValues kv = LoadConfig(FILEPATH_CONFIG_WEAPONS, "Weapons"); 21 | if (!kv) 22 | return; 23 | 24 | delete g_aWeapons; 25 | 26 | for (int i = 0; i < sizeof(g_aWeaponsClass); i++) 27 | delete g_aWeaponsClass[i]; 28 | 29 | for (int i = 0; i < sizeof(g_aWeaponsSlot); i++) 30 | delete g_aWeaponsSlot[i]; 31 | 32 | for (int i = 0; i < sizeof(g_aWeaponsClassSlot); i++) 33 | for (int j = 0; j < sizeof(g_aWeaponsClassSlot[]); j++) 34 | delete g_aWeaponsClassSlot[i][j]; 35 | 36 | g_mWeaponsName.Clear(); 37 | 38 | Weapons_LoadList(kv, "AllClass", true); 39 | Weapons_LoadList(kv, "DefaultClass", false); 40 | 41 | delete kv; 42 | 43 | kv = LoadConfig(FILEPATH_CONFIG_RESKINS, "Reskins"); 44 | if (!kv) 45 | return; 46 | 47 | g_mWeaponsReskins.Clear(); 48 | if (kv.GotoFirstSubKey(false)) 49 | { 50 | do 51 | { 52 | char sIndex[12]; 53 | kv.GetSectionName(sIndex, sizeof(sIndex)); 54 | 55 | int iIndex; 56 | if (!StringToIntEx(sIndex, iIndex)) 57 | { 58 | LogError("Randomizer Reskins config have invalid integer index: %s", sIndex); 59 | continue; 60 | } 61 | 62 | char sValue[512]; 63 | kv.GetString(NULL_STRING, sValue, sizeof(sValue)); 64 | 65 | char sValueExploded[64][12]; 66 | int iCount = ExplodeString(sValue, " ", sValueExploded, sizeof(sValueExploded), sizeof(sValueExploded[])); 67 | 68 | for (int i = 0; i < iCount; i++) 69 | g_mWeaponsReskins.SetValue(sValueExploded[i], iIndex); 70 | } 71 | while (kv.GotoNextKey(false)); 72 | } 73 | 74 | delete kv; 75 | } 76 | 77 | void Weapons_LoadList(KeyValues kv, const char[] sSection, bool bAllClass) 78 | { 79 | if (kv.JumpToKey(sSection)) 80 | { 81 | if (kv.GotoFirstSubKey(false)) 82 | { 83 | do 84 | { 85 | char sIndex[16], sName[256]; 86 | kv.GetSectionName(sIndex, sizeof(sIndex)); 87 | kv.GetString(NULL_STRING, sName, sizeof(sName)); 88 | 89 | int iIndex; 90 | if (!StringToIntEx(sIndex, iIndex)) 91 | { 92 | LogError("Randomizer Weapons config have invalid integer index: %s", sIndex); 93 | continue; 94 | } 95 | 96 | if (!TranslationPhraseExists(sName)) 97 | { 98 | LogError("Found weapon index '%d' but translation '%s' doesn't exist", iIndex, sName); 99 | continue; 100 | } 101 | 102 | if (bAllClass) 103 | Weapons_AddList(g_aWeapons, iIndex); 104 | 105 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 106 | { 107 | int iSlot = TF2_GetSlotFromIndex(iIndex, view_as(iClass)); 108 | if (WeaponSlot_Primary <= iSlot <= WeaponSlot_Building) 109 | { 110 | Weapons_AddList(g_aWeaponsClassSlot[iClass][iSlot], iIndex); 111 | 112 | if (bAllClass) 113 | { 114 | Weapons_AddList(g_aWeaponsClass[iClass], iIndex); 115 | Weapons_AddList(g_aWeaponsSlot[iSlot], iIndex); 116 | } 117 | } 118 | } 119 | 120 | g_mWeaponsName.SetString(sIndex, sName); 121 | } 122 | while (kv.GotoNextKey(false)); 123 | kv.GoBack(); 124 | } 125 | kv.GoBack(); 126 | } 127 | else 128 | { 129 | LogError("Randomizer Weapons config does not have section '%s'", sSection); 130 | } 131 | } 132 | 133 | void Weapons_AddList(ArrayList &aList, int iIndex) 134 | { 135 | if (!aList) 136 | aList = new ArrayList(); 137 | 138 | if (aList.FindValue(iIndex) == -1) //Dont put multiple same index 139 | aList.Push(iIndex); 140 | } 141 | 142 | int Weapons_GetRandomIndex(TFClassType nClass = TFClass_Unknown, int iSlot = -1) 143 | { 144 | ArrayList aList; 145 | 146 | if (nClass == TFClass_Unknown) 147 | { 148 | if (iSlot == -1) 149 | aList = g_aWeapons; 150 | else 151 | aList = g_aWeaponsSlot[iSlot]; 152 | } 153 | else 154 | { 155 | if (iSlot == -1) 156 | aList = g_aWeaponsClass[nClass]; 157 | else 158 | aList = g_aWeaponsClassSlot[nClass][iSlot]; 159 | } 160 | 161 | if (!aList) 162 | return -1; 163 | 164 | int iLength = aList.Length; 165 | return aList.Get(GetRandomInt(0, iLength - 1)); 166 | } 167 | 168 | bool Weapons_GetName(int iIndex, char[] sBuffer, int iLength) 169 | { 170 | char sIndex[16]; 171 | IntToString(iIndex, sIndex, sizeof(sIndex)); 172 | return g_mWeaponsName.GetString(sIndex, sBuffer, iLength); 173 | } 174 | 175 | int Weapons_GetReskinIndex(int iIndex) 176 | { 177 | char sIndex[12]; 178 | IntToString(iIndex, sIndex, sizeof(sIndex)); 179 | g_mWeaponsReskins.GetValue(sIndex, iIndex); 180 | return iIndex; 181 | } 182 | 183 | int Weapons_GetIndexFromName(const char[] sName) 184 | { 185 | //Only g_aWeaponsClassSlot have all weapons listed 186 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 187 | { 188 | for (int iSlot = WeaponSlot_Primary; iSlot <= WeaponSlot_Building; iSlot++) 189 | { 190 | if (!g_aWeaponsClassSlot[iClass][iSlot]) 191 | continue; 192 | 193 | int iLength = g_aWeaponsClassSlot[iClass][iSlot].Length; 194 | for (int i = 0; i < iLength; i++) 195 | { 196 | int iIndex = g_aWeaponsClassSlot[iClass][iSlot].Get(i); 197 | 198 | char sBuffer[64]; 199 | Weapons_GetName(iIndex, sBuffer, sizeof(sBuffer)); 200 | Format(sBuffer, sizeof(sBuffer), "%T", sBuffer, LANG_SERVER); 201 | if (StrContains(sBuffer, sName, false) != -1) 202 | return iIndex; 203 | } 204 | } 205 | } 206 | 207 | return -1; 208 | } -------------------------------------------------------------------------------- /configs/randomizer/huds.cfg: -------------------------------------------------------------------------------- 1 | "Huds" 2 | { 3 | //- weapon - If specified, only display if have atleast any one of it, can be classname, attribute or index 4 | //- entity - Is netprop from weapon or client? 5 | //- type - Is value int, float or time? 6 | //- element - If netprop is an array, which element to search? 7 | //- add - Add by netprop value 8 | //- multiply - Multiply value 9 | //- min - Min possible value to not display 10 | //- max - Max possible value to not display 11 | //- text - Text to add after value 12 | 13 | "m_flEffectBarRegenTime" //Bonk, Milk, Cleaver, Sandman, Wrap Assassin and Jarate 14 | { 15 | "entity" "weapon" 16 | "type" "time" 17 | "min" "0.5" 18 | "text" "Huds_EffectBarRegenTime" 19 | } 20 | 21 | "m_iDecapitations" //Airstrike, Eyelander and Bazzar Bargin 22 | { 23 | "weapon" 24 | { 25 | "classname" "tf_weapon_rocketlauncher_airstrike" 26 | "classname" "tf_weapon_sword" 27 | "classname" "tf_weapon_sniperrifle_decap" 28 | } 29 | 30 | "entity" "client-seperate" 31 | "type" "int" 32 | "min" "0" 33 | "text" "Huds_Decapitations" 34 | } 35 | 36 | "m_flEnergy" //Cow Mangler, Bison and Pomson 37 | { 38 | "weapon" 39 | { 40 | "classname" "tf_weapon_particle_cannon" 41 | "classname" "tf_weapon_raygun" 42 | "classname" "tf_weapon_drg_pomson" 43 | } 44 | 45 | "entity" "weapon" 46 | "type" "float" 47 | "multiply" "5.0" 48 | "text" "Huds_Energy" 49 | } 50 | 51 | "m_flRageMeter" //Banners, Phlogistinator and Hitman Heatmaker 52 | { 53 | "weapon" 54 | { 55 | "classname" "tf_weapon_buff_item" 56 | "classname" "tf_weapon_flamethrower" 57 | "classname" "tf_weapon_sniperrifle" 58 | } 59 | 60 | "entity" "client-seperate" 61 | "type" "float" 62 | "min" "0.0" 63 | "text" "Huds_RageMeter" 64 | } 65 | 66 | "m_iRevengeCrits" //Manmelter, Frontier Justice and Diamondback 67 | { 68 | "weapon" 69 | { 70 | "classname" "tf_weapon_flaregun_revenge" 71 | "classname" "tf_weapon_sentry_revenge" 72 | "attrib" "sapper kills collect crits" 73 | } 74 | 75 | "entity" "client-seperate" 76 | "type" "int" 77 | "min" "0" 78 | "text" "Huds_RevengeCrits" 79 | } 80 | 81 | "m_flHypeMeter" //Soda Popper and Baby Face Blaster 82 | { 83 | "weapon" 84 | { 85 | "classname" "tf_weapon_soda_popper" 86 | "classname" "tf_weapon_pep_brawler_blaster" 87 | } 88 | 89 | "entity" "client-seperate" 90 | "type" "float" 91 | "min" "0.0" 92 | "text" "Huds_HypeMeter" 93 | } 94 | 95 | "m_flItemChargeMeter" //Thermal Thruster, Gas Passer, Sandvich and Razorback 96 | { 97 | "weapon" 98 | { 99 | "classname" "tf_weapon_rocketpack" 100 | "classname" "tf_weapon_jar_gas" 101 | "classname" "tf_weapon_lunchbox" 102 | "classname" "tf_wearable_razorback" 103 | } 104 | 105 | "entity" "client-seperate" 106 | "type" "float" 107 | "element" "1" 108 | "text" "Huds_ItemChargeMeter" 109 | } 110 | 111 | "m_iPipebombCount" //Stickybomb 112 | { 113 | "weapon" 114 | { 115 | "classname" "tf_weapon_pipebomblauncher" 116 | } 117 | 118 | "entity" "weapon" 119 | "type" "int" 120 | "text" "Huds_PipebombCount" 121 | } 122 | 123 | "m_flChargeBeginTime" //Huntsman 124 | { 125 | "weapon" 126 | { 127 | "classname" "tf_weapon_compound_bow" 128 | } 129 | 130 | "entity" "weapon" 131 | "type" "time" 132 | "multiply" "-100.0" 133 | "min" "0.0" 134 | "max" "100.0" 135 | "text" "Huds_ChargeBeginTime" 136 | } 137 | 138 | "m_flChargeMeter" //Chargin Targe 139 | { 140 | "weapon" 141 | { 142 | "classname" "tf_wearable_demoshield" 143 | } 144 | 145 | "entity" "client" 146 | "type" "float" 147 | "max" "100.0" 148 | "text" "Huds_ChargeMeter" 149 | } 150 | 151 | "m_iAmmo" //Widowmaker, Rescue Ranger, Short Circuit, Wrench and Gunslinger 152 | { 153 | "weapon" 154 | { 155 | "classname" "tf_weapon_shotgun_building_rescue" 156 | "classname" "tf_weapon_wrench" 157 | "classname" "tf_weapon_robot_arm" 158 | "attrib" "mod use metal ammo type" 159 | } 160 | 161 | "entity" "client" 162 | "type" "int" 163 | "element" "3" 164 | "text" "Huds_Ammo" 165 | } 166 | 167 | "m_flChargeLevel" //Medigun 168 | { 169 | "entity" "weapon" 170 | "type" "float" 171 | "multiply" "100.0" 172 | "text" "Huds_ChargeLevel" 173 | } 174 | 175 | "m_nChargeResistType" //Vaccinator 176 | { 177 | "weapon" 178 | { 179 | "attrib" "medigun charge is resists" 180 | } 181 | 182 | "entity" "weapon" 183 | "type" "int" 184 | "text" 185 | { 186 | "0" "Huds_ChargeResistType_Bullet" 187 | "1" "Huds_ChargeResistType_Blast" 188 | "2" "Huds_ChargeResistType_Fire" 189 | } 190 | } 191 | 192 | "m_flMinicritCharge" //Cleaner's Carbine 193 | { 194 | "weapon" 195 | { 196 | "classname" "tf_weapon_charged_smg" 197 | } 198 | 199 | "entity" "weapon" 200 | "type" "float" 201 | "min" "0.0" 202 | "text" "Huds_MinicritCharge" 203 | } 204 | 205 | "m_flKnifeMeltTimestamp" //Spy-cicle 206 | { 207 | "weapon" 208 | { 209 | "attrib" "melts in fire" 210 | } 211 | 212 | "entity" "weapon" 213 | "type" "time" 214 | "add" "15.0" 215 | "min" "0.0" 216 | "text" "Huds_KnifeMeltTimestamp" 217 | } 218 | 219 | "m_nDisguiseTeam" //Your Eternal Reward, Disguise kit 220 | { 221 | "weapon" 222 | { 223 | "classname" "tf_weapon_pda_spy" 224 | "attrib" "disguise on backstab" 225 | } 226 | 227 | "entity" "client" 228 | "type" "int" 229 | "min" "2" 230 | "max" "3" 231 | "text" 232 | { 233 | "2" "Huds_DisguiseTeam_Red" 234 | "3" "Huds_DisguiseTeam_Blue" 235 | } 236 | } 237 | 238 | "m_nDisguiseClass" //Your Eternal Reward, Disguise kit 239 | { 240 | "weapon" 241 | { 242 | "classname" "tf_weapon_pda_spy" 243 | "attrib" "disguise on backstab" 244 | } 245 | 246 | "entity" "client" 247 | "type" "int" 248 | "min" "1" 249 | "max" "9" 250 | "text" 251 | { 252 | "1" "Huds_DisguiseClass_Scout" 253 | "2" "Huds_DisguiseClass_Sniper" 254 | "3" "Huds_DisguiseClass_Soldier" 255 | "4" "Huds_DisguiseClass_Demoman" 256 | "5" "Huds_DisguiseClass_Medic" 257 | "6" "Huds_DisguiseClass_Heavy" 258 | "7" "Huds_DisguiseClass_Pyro" 259 | "8" "Huds_DisguiseClass_Spy" 260 | "9" "Huds_DisguiseClass_Engineer" 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /scripting/randomizer/viewmodels.sp: -------------------------------------------------------------------------------- 1 | char g_sViewModelsArms[][PLATFORM_MAX_PATH] = { 2 | "", 3 | "models/weapons/c_models/c_scout_arms.mdl", 4 | "models/weapons/c_models/c_sniper_arms.mdl", 5 | "models/weapons/c_models/c_soldier_arms.mdl", 6 | "models/weapons/c_models/c_demo_arms.mdl", 7 | "models/weapons/c_models/c_medic_arms.mdl", 8 | "models/weapons/c_models/c_heavy_arms.mdl", 9 | "models/weapons/c_models/c_pyro_arms.mdl", 10 | "models/weapons/c_models/c_spy_arms.mdl", 11 | "models/weapons/c_models/c_engineer_arms.mdl", 12 | }; 13 | 14 | enum ViewModels 15 | { 16 | ViewModels_Arm, 17 | ViewModels_Weapon, 18 | 19 | ViewModels_MAX, 20 | } 21 | 22 | static int g_iViewModels[MAXPLAYERS + 1][ViewModels_MAX]; 23 | 24 | int ViewModels_GetFromClient(int iClient, ViewModels nViewModels, int iModelIndex, int iWeapon = INVALID_ENT_REFERENCE) 25 | { 26 | if (!g_iViewModels[iClient][nViewModels] || !IsValidEntity(g_iViewModels[iClient][nViewModels])) 27 | g_iViewModels[iClient][nViewModels] = INVALID_ENT_REFERENCE; 28 | 29 | if (g_iViewModels[iClient][nViewModels] != INVALID_ENT_REFERENCE && GetEntProp(g_iViewModels[iClient][nViewModels], Prop_Send, "m_nModelIndex") != iModelIndex) 30 | { 31 | RemoveEntity(g_iViewModels[iClient][nViewModels]); 32 | g_iViewModels[iClient][nViewModels] = INVALID_ENT_REFERENCE; 33 | } 34 | 35 | if (g_iViewModels[iClient][nViewModels] == INVALID_ENT_REFERENCE) 36 | g_iViewModels[iClient][nViewModels] = ViewModels_CreateWearable(iClient, iModelIndex, iWeapon); 37 | 38 | return g_iViewModels[iClient][nViewModels]; 39 | } 40 | 41 | void ViewModels_DeleteFromClient(int iClient, ViewModels nViewModels) 42 | { 43 | if (g_iViewModels[iClient][nViewModels] && IsValidEntity(g_iViewModels[iClient][nViewModels])) 44 | RemoveEntity(g_iViewModels[iClient][nViewModels]); 45 | 46 | g_iViewModels[iClient][nViewModels] = INVALID_ENT_REFERENCE; 47 | } 48 | 49 | void ViewModels_UpdateArms(int iClient, int iForceWeapon = INVALID_ENT_REFERENCE) 50 | { 51 | bool bSameClass; 52 | 53 | int iActiveWeapon = iForceWeapon == INVALID_ENT_REFERENCE ? GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon") : iForceWeapon; 54 | int iViewModel = GetEntPropEnt(iClient, Prop_Send, "m_hViewModel"); 55 | if (iViewModel != INVALID_ENT_REFERENCE) 56 | { 57 | TFClassType nClass; 58 | 59 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 60 | nClass = TF2_GetDefaultClassFromItem(iActiveWeapon); 61 | else 62 | nClass = TF2_GetPlayerClass(iClient); 63 | 64 | bSameClass = nClass == TF2_GetPlayerClass(iClient); 65 | if (bSameClass) 66 | RemoveEntityEffect(iViewModel, EF_NODRAW); 67 | else 68 | AddEntityEffect(iViewModel, EF_NODRAW); 69 | 70 | char sModel[PLATFORM_MAX_PATH]; 71 | int iTarget = iActiveWeapon == INVALID_ENT_REFERENCE ? iClient : iActiveWeapon; 72 | if (TF2Attrib_HookValueFloat(0.0, "wrench_builds_minisentry", iTarget)) 73 | sModel = MODEL_ARMS_ROBOTARM; 74 | else if (IsClassname(iTarget, "tf_weapon_pda_spy")) //Disguise kit have it's own viewmodel, ok valve? 75 | sModel = MODEL_ARMS_DISGUISE; 76 | else 77 | sModel = g_sViewModelsArms[nClass]; 78 | 79 | int iModelIndex = GetModelIndex(sModel); 80 | if (GetEntProp(iViewModel, Prop_Send, "m_nModelIndex") != iModelIndex) 81 | SetEntProp(iViewModel, Prop_Send, "m_nModelIndex", iModelIndex); 82 | } 83 | 84 | int iArmsModelIndex = GetModelIndex(g_sViewModelsArms[TF2_GetPlayerClass(iClient)]); 85 | int iArms = ViewModels_GetFromClient(iClient, ViewModels_Arm, iArmsModelIndex); 86 | 87 | if (bSameClass) 88 | AddEntityEffect(iArms, EF_NODRAW); 89 | else 90 | RemoveEntityEffect(iArms, EF_NODRAW); 91 | 92 | if (bSameClass) 93 | { 94 | ViewModels_DeleteFromClient(iClient, ViewModels_Weapon); 95 | } 96 | else if (iActiveWeapon != INVALID_ENT_REFERENCE) 97 | { 98 | int iWeaponModelIndex = GetEntProp(iActiveWeapon, Prop_Send, "m_iWorldModelIndex"); 99 | int iWearable = ViewModels_GetFromClient(iClient, ViewModels_Weapon, iWeaponModelIndex, iActiveWeapon); 100 | 101 | SetEntPropEnt(iArms, Prop_Send, "m_hWeaponAssociatedWith", iActiveWeapon); 102 | SetEntPropEnt(iWearable, Prop_Send, "m_hWeaponAssociatedWith", iActiveWeapon); 103 | } 104 | 105 | int iMaxWeapons = GetMaxWeapons(); 106 | for (int i = 0; i < iMaxWeapons; i++) 107 | { 108 | int iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", i); 109 | if (iWeapon != INVALID_ENT_REFERENCE) 110 | SetEntProp(iWeapon, Prop_Send, "m_nCustomViewmodelModelIndex", GetEntProp(iWeapon, Prop_Send, "m_nModelIndex")); 111 | } 112 | } 113 | 114 | int ViewModels_CreateWearable(int iClient, int iModelIndex, int iWeapon = INVALID_ENT_REFERENCE) 115 | { 116 | int iWearable = CreateEntityByName("tf_wearable_vm"); 117 | 118 | if (iWeapon != INVALID_ENT_REFERENCE) //Copy m_Item from weapon, so reskin stuffs can show 119 | SDKCall_SetItem(GetEntityAddress(iWearable) + view_as
(g_iOffsetItem), GetEntityAddress(iWeapon) + view_as
(g_iOffsetItem)); 120 | 121 | float vecOrigin[3], vecAngles[3]; 122 | GetEntPropVector(iClient, Prop_Send, "m_vecOrigin", vecOrigin); 123 | GetEntPropVector(iClient, Prop_Send, "m_angRotation", vecAngles); 124 | TeleportEntity(iWearable, vecOrigin, vecAngles, NULL_VECTOR); 125 | 126 | SetEntProp(iWearable, Prop_Send, "m_bValidatedAttachedEntity", true); 127 | SetEntPropEnt(iWearable, Prop_Send, "m_hOwnerEntity", iClient); 128 | SetEntProp(iWearable, Prop_Send, "m_iTeamNum", GetClientTeam(iClient)); 129 | SetEntProp(iWearable, Prop_Send, "m_fEffects", EF_BONEMERGE|EF_BONEMERGE_FASTCULL); 130 | 131 | DispatchSpawn(iWearable); 132 | 133 | SetEntProp(iWearable, Prop_Send, "m_nModelIndex", iModelIndex); // After DispatchSpawn, otherwise CEconItemView overrides it 134 | 135 | SetVariantString("!activator"); 136 | AcceptEntityInput(iWearable, "SetParent", GetEntPropEnt(iClient, Prop_Send, "m_hViewModel")); 137 | 138 | return EntIndexToEntRef(iWearable); 139 | } 140 | 141 | void ViewModels_RemoveAll() 142 | { 143 | int iViewmodel = INVALID_ENT_REFERENCE; 144 | while ((iViewmodel=FindEntityByClassname(iViewmodel, "tf_wearable_vm")) != INVALID_ENT_REFERENCE) 145 | RemoveEntity(iViewmodel); 146 | 147 | for (int iClient = 1; iClient <= MaxClients; iClient++) 148 | { 149 | if (!IsClientInGame(iClient)) 150 | continue; 151 | 152 | int iViewModel = GetEntPropEnt(iClient, Prop_Send, "m_hViewModel"); 153 | if (iViewModel == INVALID_ENT_REFERENCE) 154 | continue; 155 | 156 | RemoveEntityEffect(iViewModel, EF_NODRAW); 157 | SetEntityModel(iViewModel, g_sViewModelsArms[TF2_GetPlayerClass(iClient)]); 158 | } 159 | } -------------------------------------------------------------------------------- /scripting/randomizer/group.sp: -------------------------------------------------------------------------------- 1 | static RandomizedInfo g_eGroupInfo[MAX_GROUPS]; //List of groups on what to randomize 2 | 3 | void Group_Init() 4 | { 5 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 6 | g_eGroupInfo[i].Reset(); 7 | } 8 | 9 | void Group_ClearType(RandomizedType nType) 10 | { 11 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 12 | { 13 | if (g_eGroupInfo[i].nType == nType) 14 | { 15 | int[] iGroupList = new int[MaxClients]; 16 | int iGroupCount = Group_GetAllGroupList(g_eGroupInfo[i], iGroupList); 17 | 18 | g_eGroupInfo[i].Reset(); 19 | 20 | //Randomize client after his group removed 21 | for (int j = 0; j < iGroupCount; j++) 22 | Loadout_RandomizeClient(iGroupList[j], nType); 23 | } 24 | } 25 | } 26 | 27 | void Group_Add(RandomizedInfo eInfo) 28 | { 29 | //Find empty space to insert it 30 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 31 | { 32 | if (g_eGroupInfo[i].nType == RandomizedType_None) 33 | { 34 | g_eGroupInfo[i] = eInfo; 35 | 36 | //Randomize group for "same" 37 | Loadout_RandomizeGroup(i); 38 | 39 | //Randomize everyone in group for not "same" 40 | int[] iGroupList = new int[MaxClients]; 41 | int iGroupCount = Group_GetAllGroupList(eInfo, iGroupList); 42 | for (int j = 0; j < iGroupCount; j++) 43 | Loadout_RandomizeClient(iGroupList[j], eInfo.nType); 44 | return; 45 | } 46 | } 47 | } 48 | 49 | bool Group_IsClientRandomized(int iClient, RandomizedType nType) 50 | { 51 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 52 | { 53 | RandomizedInfo eInfo; 54 | eInfo = g_eGroupInfo[i]; 55 | if (eInfo.nType != nType) 56 | continue; 57 | 58 | if (Group_IsClientInAllGroup(eInfo, iClient)) 59 | return true; 60 | } 61 | 62 | return false; 63 | } 64 | 65 | int Group_GetClientSameInfoPos(int iClient, RandomizedType nType) 66 | { 67 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 68 | { 69 | RandomizedInfo eInfo; 70 | eInfo = g_eGroupInfo[i]; 71 | if (eInfo.nType != nType) 72 | continue; 73 | 74 | if (!eInfo.bSame) 75 | continue; 76 | 77 | if (Group_IsClientInAllGroup(eInfo, iClient)) 78 | return i; 79 | } 80 | 81 | return -1; 82 | } 83 | 84 | void Group_GetInfoFromClient(int iClient, RandomizedType nType, RandomizedInfo eBuffer) 85 | { 86 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 87 | { 88 | RandomizedInfo eInfo; 89 | eInfo = g_eGroupInfo[i]; 90 | if (eInfo.nType != nType) 91 | continue; 92 | 93 | if (Group_IsClientInAllGroup(eInfo, iClient)) 94 | { 95 | eBuffer = eInfo; 96 | return; 97 | } 98 | } 99 | } 100 | 101 | void Group_GetInfoFromPos(int iPos, RandomizedInfo eBuffer) 102 | { 103 | eBuffer = g_eGroupInfo[iPos]; 104 | } 105 | 106 | bool Group_CanRandomizePosForClients(int iPos, RandomizedType nType, const int[] iClients, int iCount) 107 | { 108 | RandomizedInfo eInfo; 109 | eInfo = g_eGroupInfo[iPos]; 110 | if (eInfo.nType != nType) 111 | return false; 112 | 113 | if (!eInfo.bSame) 114 | return false; 115 | 116 | int[] iGroupList = new int[MaxClients]; 117 | int iGroupCount = Group_GetAllGroupList(eInfo, iGroupList); 118 | for (int i = 0; i < iGroupCount; i++) 119 | { 120 | int iFound = 0; //-1 if found none, 1 if found so far 121 | 122 | if (Group_IsClientInList(iGroupList[i], iClients, iCount)) 123 | { 124 | if (iFound != -1) 125 | iFound = 1; 126 | else 127 | return false; 128 | } 129 | else 130 | { 131 | if (iFound != 1) 132 | iFound = -1; 133 | else 134 | return false; 135 | } 136 | } 137 | 138 | return true; 139 | } 140 | 141 | void Group_TriggerRandomizeClient(int iClient, RandomizedAction nAction) 142 | { 143 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 144 | { 145 | RandomizedInfo eInfo; 146 | eInfo = g_eGroupInfo[i]; 147 | if (eInfo.nType == RandomizedType_None) 148 | continue; 149 | 150 | if (!(eInfo.nAction & nAction)) 151 | continue; 152 | 153 | if (!Group_IsClientInTrigger(eInfo, iClient)) 154 | continue; 155 | 156 | if (eInfo.bSame) 157 | { 158 | //Use group to randomize same loadout to distribute all clients in group 159 | Loadout_RandomizeGroup(i); 160 | } 161 | else 162 | { 163 | //Randomize each clients 164 | int[] iGroupList = new int[MaxClients]; 165 | int iGroupCount = Group_GetGroupList(eInfo, iGroupList, iClient); 166 | for (int j = 0; j < iGroupCount; j++) 167 | Loadout_RandomizeClient(iGroupList[j], eInfo.nType); 168 | } 169 | } 170 | } 171 | 172 | void Group_TriggerRandomizeAll(RandomizedAction nAction) 173 | { 174 | for (int i = 0; i < sizeof(g_eGroupInfo); i++) 175 | { 176 | RandomizedInfo eInfo; 177 | eInfo = g_eGroupInfo[i]; 178 | if (eInfo.nType == RandomizedType_None) 179 | continue; 180 | 181 | if (!(eInfo.nAction & nAction)) 182 | continue; 183 | 184 | if (eInfo.bSame) 185 | { 186 | //Use group to randomize same loadout to distribute all clients in group 187 | Loadout_RandomizeGroup(i); 188 | } 189 | else 190 | { 191 | int[] iGroupList = new int[MaxClients]; 192 | int iGroupCount = Group_GetAllGroupList(eInfo, iGroupList); 193 | for (int j = 0; j < iGroupCount; j++) 194 | Loadout_RandomizeClient(iGroupList[j], eInfo.nType); 195 | } 196 | } 197 | } 198 | 199 | int Group_GetTriggerList(RandomizedInfo eInfo, int[] iTriggerList) 200 | { 201 | char sGroupName[MAX_TARGET_LENGTH]; 202 | bool bIsML; 203 | return ProcessTargetString(eInfo.sTrigger, 0, iTriggerList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 204 | } 205 | 206 | int Group_GetGroupList(RandomizedInfo eInfo, int[] iGroupList, int iTrigger) 207 | { 208 | char sGroupName[MAX_TARGET_LENGTH]; 209 | bool bIsML; 210 | return ProcessTargetString(eInfo.sGroup, iTrigger, iGroupList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 211 | } 212 | 213 | int Group_GetAllGroupList(RandomizedInfo eInfo, int[] iBufferList) 214 | { 215 | int iBufferCount; 216 | 217 | int[] iTriggerList = new int[MaxClients]; 218 | int iTriggerCount = Group_GetTriggerList(eInfo, iTriggerList); 219 | for (int i = 0; i < iTriggerCount; i++) 220 | { 221 | int[] iGroupList = new int[MaxClients]; 222 | int iGroupCount = Group_GetGroupList(eInfo, iGroupList, iTriggerList[i]); 223 | for (int j = 0; j < iGroupCount; j++) 224 | { 225 | if (!Group_IsClientInList(iGroupList[j], iBufferList, iBufferCount)) 226 | { 227 | iBufferList[iBufferCount] = iGroupList[j]; 228 | iBufferCount++; 229 | } 230 | } 231 | } 232 | 233 | return iBufferCount; 234 | } 235 | 236 | bool Group_IsClientInTrigger(RandomizedInfo eInfo, int iClient) 237 | { 238 | int[] iTriggerList = new int[MaxClients]; 239 | int iTriggerCount = Group_GetTriggerList(eInfo, iTriggerList); 240 | return Group_IsClientInList(iClient, iTriggerList, iTriggerCount); 241 | } 242 | 243 | bool Group_IsClientInAllGroup(RandomizedInfo eInfo, int iClient) 244 | { 245 | int[] iGroupList = new int[MaxClients]; 246 | int iGroupCount = Group_GetAllGroupList(eInfo, iGroupList); 247 | return Group_IsClientInList(iClient, iGroupList, iGroupCount); 248 | } 249 | 250 | bool Group_IsClientInList(int iClient, const int[] iList, int iCount) 251 | { 252 | for (int i = 0; i < iCount; i++) 253 | if (iList[i] == iClient) 254 | return true; 255 | 256 | return false; 257 | } -------------------------------------------------------------------------------- /scripting/randomizer/convar.sp: -------------------------------------------------------------------------------- 1 | static char g_sRandomizedAction[][] = { 2 | "death", 3 | "death-kill", 4 | "death-env", 5 | "death-suicide", 6 | "kill", 7 | "assist", 8 | "round", 9 | "round-full", 10 | "cp-capture", 11 | "flag-capture", 12 | "pass-score", 13 | }; 14 | 15 | enum ConVarResult 16 | { 17 | ConVarResult_NotFound = 0, 18 | ConVarResult_Found = 1, 19 | ConVarResult_Error = 2, 20 | } 21 | 22 | void ConVar_Init() 23 | { 24 | CreateConVar("randomizer_version", PLUGIN_VERSION ... "." ... PLUGIN_VERSION_REVISION, "Randomizer plugin version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD); 25 | 26 | g_cvEnabled = CreateConVar("randomizer_enabled", "1", "Enable Randomizer?", _, true, 0.0, true, 1.0); 27 | g_cvEnabled.AddChangeHook(ConVar_EnableChanged); 28 | 29 | g_cvDebug = CreateConVar("randomizer_debug", "0", "Enable debugging info, enabling may cause preformance issues.", _, true, 0.0, true, 1.0); 30 | g_cvFixTaunt = CreateConVar("randomizer_fix_taunt", "1", "Fix Taunting?", _, true, 0.0, true, 1.0); 31 | g_cvDroppedWeapons = CreateConVar("randomizer_droppedweapons", "0", "Allow dropped weapons?", _, true, 0.0, true, 1.0); 32 | g_cvHuds = CreateConVar("randomizer_huds", "1", "Hud to use to display weapons. 0 = none, 1 = hud text, 2 = menu.", _, true, 0.0, true, float(HudMode_MAX - 1)); 33 | 34 | ConVar_AddType(RandomizedType_Class, "randomizer_class", "trigger=@all group=@me action=death-kill action=round", "How should class be randomized?"); 35 | ConVar_AddType(RandomizedType_Weapons, "randomizer_weapons", "trigger=@all group=@me action=death-kill action=round count-primary=1 count-secondary=1 count-melee=1", "How should weapons be randomized?"); 36 | ConVar_AddType(RandomizedType_Cosmetics, "randomizer_cosmetics", "trigger=@all group=@me action=death-kill action=round count=3 conflicts=1", "How should cosmetics be randomized?"); 37 | ConVar_AddType(RandomizedType_Rune, "randomizer_rune", "", "How should rune be randomized?"); 38 | ConVar_AddType(RandomizedType_Spells, "randomizer_spells", "", "How should spells be randomized?"); 39 | } 40 | 41 | void ConVar_AddType(RandomizedType nType, const char[] sName, const char[] sDefault, const char[] sDesp) 42 | { 43 | g_cvRandomize[nType] = CreateConVar(sName, sDefault, sDesp); 44 | g_cvRandomize[nType].AddChangeHook(ConVar_RandomizeChanged); 45 | } 46 | 47 | void ConVar_Refresh() 48 | { 49 | for (RandomizedType nType; nType < RandomizedType_MAX; nType++) 50 | { 51 | char sBuffer[1024]; 52 | g_cvRandomize[nType].GetString(sBuffer, sizeof(sBuffer)); 53 | ConVar_RandomizeChanged(g_cvRandomize[nType], "", sBuffer); 54 | } 55 | } 56 | 57 | public void ConVar_EnableChanged(ConVar convar, const char[] sOldValue, const char[] sNewValue) 58 | { 59 | if (!!StringToInt(sNewValue)) 60 | EnableRandomizer(); 61 | else 62 | DisableRandomizer(); 63 | } 64 | 65 | public void ConVar_RandomizeChanged(ConVar convar, const char[] sOldValue, const char[] sNewValue) 66 | { 67 | static bool bSkip; 68 | if (bSkip) 69 | return; 70 | 71 | RandomizedType nType = ConVar_GetType(convar); 72 | 73 | if (!sNewValue[0]) //Empty string 74 | { 75 | Group_ClearType(nType); 76 | return; 77 | } 78 | 79 | RandomizedInfo eInfoList[MAX_GROUPS]; 80 | int iInfoCount; 81 | 82 | char sGroups[32][256]; 83 | int iGroupCount = ExplodeString(sNewValue, ",", sGroups, sizeof(sGroups), sizeof(sGroups[])); 84 | for (int i = 0; i < iGroupCount; i++) 85 | { 86 | RandomizedInfo eInfo; 87 | eInfo.Reset(); 88 | eInfo.nType = nType; 89 | 90 | TrimString(sGroups[i]); 91 | char sParams[16][256]; 92 | int iCount = ExplodeString(sGroups[i], " ", sParams, sizeof(sParams), sizeof(sParams[])); 93 | for (int j = 0; j < iCount; j++) 94 | { 95 | TrimString(sParams[j]); 96 | 97 | char sParam[3][256]; 98 | if (ExplodeString(sParams[j], "=", sParam, sizeof(sParam), sizeof(sParam[])) != 2) 99 | { 100 | PrintToServer("Invalid param format '%s' (must be '=')", sParams[j]); 101 | bSkip = true; 102 | convar.SetString(sOldValue); 103 | bSkip = false; 104 | return; 105 | } 106 | 107 | ConVarResult nResult; 108 | 109 | nResult += ConVar_AddString("trigger", sParam, eInfo.sTrigger, sizeof(eInfo.sTrigger)); 110 | nResult += ConVar_AddString("group", sParam, eInfo.sGroup, sizeof(eInfo.sGroup)); 111 | nResult += ConVar_AddAction("action", sParam, eInfo.nAction); 112 | nResult += ConVar_AddInt("same", sParam, eInfo.bSame); 113 | nResult += ConVar_AddInt("count", sParam, eInfo.iCount); 114 | nResult += ConVar_AddInt("count-primary", sParam, eInfo.iCountSlot[WeaponSlot_Primary]); 115 | nResult += ConVar_AddInt("count-secondary", sParam, eInfo.iCountSlot[WeaponSlot_Secondary]); 116 | nResult += ConVar_AddInt("count-melee", sParam, eInfo.iCountSlot[WeaponSlot_Melee]); 117 | nResult += ConVar_AddInt("defaultclass", sParam, eInfo.bDefaultClass); 118 | nResult += ConVar_AddInt("conflicts", sParam, eInfo.bConflicts); 119 | 120 | if (nResult == ConVarResult_NotFound) 121 | PrintToServer("Invalid param name '%s'", sParam[0]); 122 | 123 | if (nResult != ConVarResult_Found) 124 | { 125 | bSkip = true; 126 | convar.SetString(sOldValue); 127 | bSkip = false; 128 | return; 129 | } 130 | } 131 | 132 | eInfoList[iInfoCount] = eInfo; 133 | iInfoCount++; 134 | } 135 | 136 | Group_ClearType(nType); 137 | 138 | for (int i = 0; i < iInfoCount; i++) 139 | Group_Add(eInfoList[i]); 140 | } 141 | 142 | RandomizedType ConVar_GetType(ConVar convar) 143 | { 144 | for (int i = 0; i < sizeof(g_cvRandomize); i++) 145 | if (g_cvRandomize[i] == convar) 146 | return view_as(i); 147 | 148 | return RandomizedType_None; 149 | } 150 | 151 | ConVarResult ConVar_AddString(const char[] sName, const char[][] sParam, char[] sBuffer, int iLength) 152 | { 153 | if (!StrEqual(sParam[0], sName, false)) 154 | return ConVarResult_NotFound; 155 | 156 | if (sBuffer[0]) 157 | { 158 | PrintToServer("Name '%s' must not be used multiple times in one group", sName); 159 | return ConVarResult_Error; 160 | } 161 | 162 | strcopy(sBuffer, iLength, sParam[1]); 163 | return ConVarResult_Found; 164 | } 165 | 166 | ConVarResult ConVar_AddAction(const char[] sName, const char[][] sParam, RandomizedAction &nAction) 167 | { 168 | if (!StrEqual(sParam[0], sName, false)) 169 | return ConVarResult_NotFound; 170 | 171 | for (int i = 0; i < sizeof(g_sRandomizedAction); i++) 172 | { 173 | if (StrEqual(g_sRandomizedAction[i], sParam[1], false)) 174 | { 175 | if (nAction & view_as(1<(1<(-1); 2 | 3 | static Handle g_hSDKGetMaxAmmo; 4 | static Handle g_hSDKAddObject; 5 | static Handle g_hSDKRemoveObject; 6 | static Handle g_hSDKDoClassSpecialSkill; 7 | static Handle g_hSDKEndClassSpecialSkill; 8 | static Handle g_hSDKGetLoadoutItem; 9 | static Handle g_hSDKRollNewSpell; 10 | static Handle g_hSDKUpdateRageBuffsAndRage; 11 | static Handle g_hSDKModifyRage; 12 | static Handle g_hSDKSetCarryingRuneType; 13 | static Handle g_hSDKHandleRageGain; 14 | static Handle g_hSDKSetItem; 15 | static Handle g_hSDKGetBaseEntity; 16 | static Handle g_hSDKGetMaxHealth; 17 | static Handle g_hSDKGetSwordHealthMod; 18 | static Handle g_hSDKWeaponCanSwitchTo; 19 | static Handle g_hSDKEquipWearable; 20 | static Handle g_hSDKGiveNamedItem; 21 | 22 | public void SDKCall_Init(GameData hGameData) 23 | { 24 | g_hSDKGetMaxAmmo = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Signature, "CTFPlayer::GetMaxAmmo", SDKType_PlainOldData, SDKType_PlainOldData, SDKType_PlainOldData); 25 | g_hSDKAddObject = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Signature, "CTFPlayer::AddObject", _, SDKType_CBaseEntity); 26 | g_hSDKRemoveObject = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Signature, "CTFPlayer::RemoveObject", _, SDKType_CBaseEntity); 27 | g_hSDKDoClassSpecialSkill = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Signature, "CTFPlayer::DoClassSpecialSkill", SDKType_Bool); 28 | g_hSDKEndClassSpecialSkill = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Signature, "CTFPlayer::EndClassSpecialSkill", SDKType_Bool); 29 | g_hSDKGetLoadoutItem = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Signature, "CTFPlayer::GetLoadoutItem", SDKType_PlainOldData, SDKType_PlainOldData, SDKType_PlainOldData, SDKType_PlainOldData); 30 | g_hSDKRollNewSpell = SDKCall_Create(hGameData, SDKCall_Entity, SDKConf_Signature, "CTFSpellBook::RollNewSpell", _, SDKType_PlainOldData, SDKType_Bool); 31 | g_hSDKUpdateRageBuffsAndRage = SDKCall_Create(hGameData, SDKCall_Raw, SDKConf_Signature, "CTFPlayerShared::UpdateRageBuffsAndRage"); 32 | g_hSDKModifyRage = SDKCall_Create(hGameData, SDKCall_Raw, SDKConf_Signature, "CTFPlayerShared::ModifyRage", _, SDKType_Float); 33 | g_hSDKSetCarryingRuneType = SDKCall_Create(hGameData, SDKCall_Raw, SDKConf_Signature, "CTFPlayerShared::SetCarryingRuneType", _, SDKType_PlainOldData); 34 | g_hSDKHandleRageGain = SDKCall_Create(hGameData, SDKCall_Static, SDKConf_Signature, "HandleRageGain", _, SDKType_CBaseEntity, SDKType_PlainOldData, SDKType_Float, SDKType_Float); 35 | g_hSDKSetItem = SDKCall_Create(hGameData, SDKCall_Raw, SDKConf_Signature, "CEconItemView::operator=", SDKType_PlainOldData, SDKType_PlainOldData); 36 | g_hSDKGetBaseEntity = SDKCall_Create(hGameData, SDKCall_Raw, SDKConf_Virtual, "CBaseEntity::GetBaseEntity", SDKType_CBaseEntity); 37 | g_hSDKGetMaxHealth = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Virtual, "CBaseEntity::GetMaxHealth", SDKType_PlainOldData); 38 | g_hSDKGetSwordHealthMod = SDKCall_Create(hGameData, SDKCall_Entity, SDKConf_Virtual, "CTFSword::GetSwordHealthMod", SDKType_PlainOldData); 39 | g_hSDKWeaponCanSwitchTo = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Virtual, "CBaseCombatCharacter::Weapon_CanSwitchTo", SDKType_Bool, SDKType_CBaseEntity); 40 | g_hSDKEquipWearable = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Virtual, "CBasePlayer::EquipWearable", _, SDKType_CBaseEntity); 41 | g_hSDKGiveNamedItem = SDKCall_Create(hGameData, SDKCall_Player, SDKConf_Virtual, "CTFPlayer::GiveNamedItem", SDKType_CBaseEntity, SDKType_String, SDKType_PlainOldData, SDKType_PlainOldData, SDKType_PlainOldData); 42 | } 43 | 44 | static Handle SDKCall_Create(GameData hGameData, SDKCallType nType, SDKFuncConfSource nSource, const char[] sName, SDKType nReturn = SDKType_Unknown, SDKType nParam1 = SDKType_Unknown, SDKType nParam2 = SDKType_Unknown, SDKType nParam3 = SDKType_Unknown, SDKType nParam4 = SDKType_Unknown) 45 | { 46 | StartPrepSDKCall(nType); 47 | PrepSDKCall_SetFromConf(hGameData, nSource, sName); 48 | 49 | SDKCall_AddParameter(nParam1); 50 | SDKCall_AddParameter(nParam2); 51 | SDKCall_AddParameter(nParam3); 52 | SDKCall_AddParameter(nParam4); 53 | 54 | if (nReturn != SDKType_Unknown) 55 | { 56 | if (nReturn == SDKType_String || nReturn == SDKType_CBaseEntity) 57 | PrepSDKCall_SetReturnInfo(nReturn, SDKPass_Pointer); 58 | else 59 | PrepSDKCall_SetReturnInfo(nReturn, SDKPass_ByValue); 60 | } 61 | 62 | Handle hSDKCall = EndPrepSDKCall(); 63 | if (!hSDKCall) 64 | LogError("Failed to create call: %s", sName); 65 | 66 | return hSDKCall; 67 | } 68 | 69 | static void SDKCall_AddParameter(SDKType nParam) 70 | { 71 | if (nParam == SDKType_Unknown) 72 | return; 73 | 74 | if (nParam == SDKType_String || nParam == SDKType_CBaseEntity) 75 | PrepSDKCall_AddParameter(nParam, SDKPass_Pointer); 76 | else 77 | PrepSDKCall_AddParameter(nParam, SDKPass_ByValue); 78 | } 79 | 80 | int SDKCall_GetMaxAmmo(int iClient, int iAmmoType, TFClassType nClass = view_as(-1)) 81 | { 82 | return SDKCall(g_hSDKGetMaxAmmo, iClient, iAmmoType, nClass); 83 | } 84 | 85 | void SDKCall_AddObject(int iClient, int iObject) 86 | { 87 | SDKCall(g_hSDKAddObject, iClient, iObject); 88 | } 89 | 90 | void SDKCall_RemoveObject(int iClient, int iObject) 91 | { 92 | SDKCall(g_hSDKRemoveObject, iClient, iObject); 93 | } 94 | 95 | bool SDKCall_DoClassSpecialSkill(int iClient) 96 | { 97 | return SDKCall(g_hSDKDoClassSpecialSkill, iClient); 98 | } 99 | 100 | bool SDKCall_EndClassSpecialSkill(int iClient) 101 | { 102 | return SDKCall(g_hSDKEndClassSpecialSkill, iClient); 103 | } 104 | 105 | Address SDKCall_GetLoadoutItem(int iClient, TFClassType nClass, int iSlot, bool bReportWhitelistFails = false) 106 | { 107 | return SDKCall(g_hSDKGetLoadoutItem, iClient, nClass, iSlot, bReportWhitelistFails); 108 | } 109 | 110 | void SDKCall_RollNewSpell(int iSpellbook, int iTier, bool bForceReroll) 111 | { 112 | SDKCall(g_hSDKRollNewSpell, iSpellbook, iTier, bForceReroll); 113 | } 114 | 115 | void SDKCall_UpdateRageBuffsAndRage(Address pPlayerShared) 116 | { 117 | SDKCall(g_hSDKUpdateRageBuffsAndRage, pPlayerShared); 118 | } 119 | 120 | void SDKCall_ModifyRage(Address pPlayerShared, float flAdd) 121 | { 122 | SDKCall(g_hSDKModifyRage, pPlayerShared, flAdd); 123 | } 124 | 125 | void SDKCall_SetCarryingRuneType(Address pPlayerShared, int iRuneType) 126 | { 127 | SDKCall(g_hSDKSetCarryingRuneType, pPlayerShared, iRuneType); 128 | } 129 | 130 | void SDKCall_HandleRageGain(int iClient, int iRequiredBuffFlags, float flDamage, float fInverseRageGainScale) 131 | { 132 | SDKCall(g_hSDKHandleRageGain, iClient, iRequiredBuffFlags, flDamage, fInverseRageGainScale); 133 | } 134 | 135 | void SDKCall_SetItem(Address pItem, Address pOther) 136 | { 137 | SDKCall(g_hSDKSetItem, pItem, pOther); 138 | } 139 | 140 | int SDKCall_GetBaseEntity(Address pEntity) 141 | { 142 | return SDKCall(g_hSDKGetBaseEntity, pEntity); 143 | } 144 | 145 | int SDKCall_GetMaxHealth(int iEntity) 146 | { 147 | return SDKCall(g_hSDKGetMaxHealth, iEntity); 148 | } 149 | 150 | int SDKCall_GetSwordHealthMod(int iSword) 151 | { 152 | return SDKCall(g_hSDKGetSwordHealthMod, iSword); 153 | } 154 | 155 | bool SDKCall_WeaponCanSwitchTo(int iClient, int iWeapon) 156 | { 157 | return SDKCall(g_hSDKWeaponCanSwitchTo, iClient, iWeapon); 158 | } 159 | 160 | void SDKCall_EquipWearable(int iClient, int iWearable) 161 | { 162 | SDKCall(g_hSDKEquipWearable, iClient, iWearable); 163 | } 164 | 165 | int SDKCall_GiveNamedItem(int iClient, const char[] sClassname, int iSubType, Address pItem, bool b = false) 166 | { 167 | return SDKCall(g_hSDKGiveNamedItem, iClient, sClassname, iSubType, pItem, b); 168 | } -------------------------------------------------------------------------------- /scripting/randomizer/controls.sp: -------------------------------------------------------------------------------- 1 | #define FILEPATH_CONFIG_CONTROLS "configs/randomizer/controls.cfg" 2 | 3 | enum struct ControlsInfo 4 | { 5 | int iButton; //Bitflag button 6 | char sKey[64]; //String in config to get stuffs 7 | WeaponWhitelist weaponWhitelist; //List of weapons that uses this control 8 | } 9 | 10 | enum struct ControlsPassive 11 | { 12 | Button nButton; //Which button to use as alt way 13 | float flCooldown; //After passive is used, cooldown before able to use again 14 | bool bInvis; //Can this be used while cloaked? 15 | 16 | char sTextMain[64]; //Text to use if button not changed 17 | char sTextAlt[64]; //Text to use if button is changed to alt way 18 | char sTextNone[64]; //Text to use if no buttons to use 19 | } 20 | 21 | static ControlsInfo g_controlsInfo[Button_MAX]; 22 | static StringMap g_mControlsPassive; 23 | static bool g_bControlsButton[MAXPLAYERS + 1][WeaponSlot_Building+1][view_as(Button_MAX)]; 24 | static float g_flControlsCooldown[MAXPLAYERS + 1][WeaponSlot_Building+1]; 25 | 26 | public void Controls_Init() 27 | { 28 | g_controlsInfo[Button_Attack2].iButton = IN_ATTACK2; 29 | g_controlsInfo[Button_Attack2].sKey = "attack2"; 30 | 31 | g_controlsInfo[Button_Attack3].iButton = IN_ATTACK3; 32 | g_controlsInfo[Button_Attack3].sKey = "attack3"; 33 | 34 | g_controlsInfo[Button_Reload].iButton = IN_RELOAD; 35 | g_controlsInfo[Button_Reload].sKey = "reload"; 36 | 37 | g_mControlsPassive = new StringMap(); 38 | } 39 | 40 | public void Controls_Refresh() 41 | { 42 | KeyValues kv = LoadConfig(FILEPATH_CONFIG_CONTROLS, "Controls"); 43 | if (!kv) 44 | return; 45 | 46 | for (int i = 0; i < sizeof(g_controlsInfo); i++) 47 | { 48 | g_controlsInfo[i].weaponWhitelist.Delete(); 49 | g_controlsInfo[i].weaponWhitelist.Load(kv, g_controlsInfo[i].sKey); 50 | } 51 | 52 | g_mControlsPassive.Clear(); 53 | 54 | if (kv.JumpToKey("passive", false)) 55 | { 56 | if (kv.GotoFirstSubKey(false)) 57 | { 58 | do 59 | { 60 | ControlsPassive controlsPassive; 61 | char sName[CONFIG_MAXCHAR], sButton[CONFIG_MAXCHAR]; 62 | kv.GetSectionName(sName, sizeof(sName)); 63 | kv.GetString("button", sButton, sizeof(sButton)); 64 | controlsPassive.flCooldown = kv.GetFloat("cooldown", 0.0); 65 | controlsPassive.bInvis = !!kv.GetNum("invis", false); 66 | 67 | if (StrEqual(sButton, "attack2")) 68 | controlsPassive.nButton = Button_Attack2; 69 | else if (StrEqual(sButton, "attack3")) 70 | controlsPassive.nButton = Button_Attack3; 71 | else if (StrEqual(sButton, "reload")) 72 | controlsPassive.nButton = Button_Reload; 73 | else 74 | controlsPassive.nButton = Button_Invalid; 75 | 76 | Controls_GetTranslation(kv, sName, "textmain", controlsPassive.sTextMain, sizeof(controlsPassive.sTextMain)); 77 | Controls_GetTranslation(kv, sName, "textalt", controlsPassive.sTextAlt, sizeof(controlsPassive.sTextAlt)); 78 | Controls_GetTranslation(kv, sName, "textnone", controlsPassive.sTextNone, sizeof(controlsPassive.sTextNone)); 79 | 80 | g_mControlsPassive.SetArray(sName, controlsPassive, sizeof(controlsPassive)); 81 | } 82 | while (kv.GotoNextKey(false)); 83 | } 84 | kv.GoBack(); 85 | } 86 | kv.GoBack(); 87 | 88 | delete kv; 89 | } 90 | 91 | void Controls_GetTranslation(KeyValues kv, const char[] sName, const char[] sKey, char[] sBuffer, int iLength) 92 | { 93 | kv.GetString(sKey, sBuffer, iLength); 94 | if (!sBuffer[0]) 95 | return; 96 | 97 | if (!TranslationPhraseExists(sBuffer)) 98 | LogError("Found controls classname '%s' but translation '%s' doesn't exist", sName, sBuffer); 99 | } 100 | 101 | void Controls_RefreshClient(int iClient) 102 | { 103 | for (int iSlot = WeaponSlot_Primary; iSlot <= WeaponSlot_PDA2; iSlot++) 104 | { 105 | g_flControlsCooldown[iClient][iSlot] = 0.0; 106 | 107 | for (Button nButton; nButton < Button_MAX; nButton++) 108 | g_bControlsButton[iClient][iSlot][nButton] = false; 109 | } 110 | 111 | int iWeapon; 112 | int iPos; 113 | while (TF2_GetItem(iClient, iWeapon, iPos)) 114 | { 115 | int iSlot = TF2_GetSlot(iWeapon); 116 | int iIndex = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex"); 117 | 118 | for (Button nButton; nButton < Button_MAX; nButton++) 119 | if (g_controlsInfo[nButton].weaponWhitelist.IsIndexAllowed(iIndex)) 120 | g_bControlsButton[iClient][iSlot][nButton] = true; 121 | } 122 | } 123 | 124 | Button Controls_GetPassiveButton(int iClient, int iWeapon) 125 | { 126 | ControlsPassive controlsPassive; 127 | if (!Controls_GetPassive(iWeapon, controlsPassive)) 128 | return Button_Invalid; 129 | 130 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 131 | if (iActiveWeapon == INVALID_ENT_REFERENCE) 132 | return Button_Invalid; 133 | 134 | //Active weapon always use attack2 135 | if (iWeapon == iActiveWeapon) 136 | return Button_Attack2; 137 | 138 | int iActiveSlot = TF2_GetSlot(iActiveWeapon); 139 | if (g_bControlsButton[iClient][iActiveSlot][Button_Attack2]) 140 | { 141 | //Active slot use attack2, use alt button instead if possible 142 | if (controlsPassive.nButton == Button_Invalid) 143 | return Button_Invalid; 144 | if (g_bControlsButton[iClient][iActiveSlot][controlsPassive.nButton]) 145 | return Button_Invalid; 146 | else 147 | return controlsPassive.nButton; 148 | } 149 | 150 | int iSlot = TF2_GetSlot(iWeapon); 151 | int iWeaponTemp, iPos; 152 | while (TF2_GetItem(iClient, iWeaponTemp, iPos)) 153 | { 154 | ControlsPassive buffer; 155 | if (!Controls_GetPassive(iWeaponTemp, buffer)) 156 | continue; //not passive 157 | 158 | int iSlotTemp = TF2_GetSlot(iWeaponTemp); 159 | if (g_bControlsButton[iClient][iSlotTemp][Button_Attack2] && iSlotTemp < iSlot) 160 | { 161 | //iWeaponTemp uses attack2 and have higher priority than iWeapon, use alt 162 | return controlsPassive.nButton; 163 | } 164 | } 165 | 166 | return Button_Attack2; 167 | } 168 | 169 | int Controls_GetPassiveButtonBit(int iClient, int iWeapon) 170 | { 171 | Button nButton = Controls_GetPassiveButton(iClient, iWeapon); 172 | if (nButton == Button_Invalid) 173 | return 0; 174 | 175 | return g_controlsInfo[nButton].iButton; 176 | } 177 | 178 | bool Controls_GetPassiveInfo(int iClient, int iWeapon, char[] sText, int iLength) 179 | { 180 | ControlsPassive controlsPassive; 181 | if (!Controls_GetPassive(iWeapon, controlsPassive)) 182 | return false; 183 | 184 | Button nButton = Controls_GetPassiveButton(iClient, iWeapon); 185 | 186 | if (nButton == Button_Invalid) 187 | Format(sText, iLength, "%T", controlsPassive.sTextNone, iClient); 188 | else if (nButton == Button_Attack2) 189 | Format(sText, iLength, "%T", controlsPassive.sTextMain, iClient); 190 | else 191 | Format(sText, iLength, "%T", controlsPassive.sTextAlt, iClient); 192 | 193 | return true; 194 | } 195 | 196 | void Controls_OnPassiveUse(int iClient, int iWeapon) 197 | { 198 | ControlsPassive controlsPassive; 199 | if (Controls_GetPassive(iWeapon, controlsPassive)) 200 | { 201 | int iSlot = TF2_GetSlot(iWeapon); 202 | g_flControlsCooldown[iClient][iSlot] = GetGameTime() + controlsPassive.flCooldown; 203 | } 204 | } 205 | 206 | bool Controls_GetPassive(int iWeapon, ControlsPassive controlsPassive) 207 | { 208 | char sClassname[CONFIG_MAXCHAR]; 209 | GetEntityClassname(iWeapon, sClassname, sizeof(sClassname)); 210 | return g_mControlsPassive.GetArray(sClassname, controlsPassive, sizeof(controlsPassive)); 211 | } 212 | 213 | bool Controls_CanUse(int iClient, int iWeapon) 214 | { 215 | int iSlot = TF2_GetSlot(iWeapon); 216 | if (g_flControlsCooldown[iClient][iSlot] > GetGameTime()) 217 | return false; 218 | 219 | ControlsPassive controlsPassive; 220 | Controls_GetPassive(iWeapon, controlsPassive); 221 | if (!controlsPassive.bInvis && TF2_IsPlayerInCondition(iClient, TFCond_Cloaked)) 222 | return false; 223 | 224 | return true; 225 | } -------------------------------------------------------------------------------- /scripting/randomizer/event.sp: -------------------------------------------------------------------------------- 1 | void Event_Init() 2 | { 3 | HookEvent("teamplay_round_start", Event_RoundStart); 4 | HookEvent("teamplay_point_captured", Event_PointCaptured); 5 | HookEvent("teamplay_flag_event", Event_FlagCaptured); 6 | HookEvent("post_inventory_application", Event_PlayerInventoryUpdate); 7 | HookEvent("player_spawn", Event_PlayerSpawn); 8 | HookEvent("player_regenerate", Event_PlayerRegenerate); 9 | HookEvent("player_hurt", Event_PlayerHurt); 10 | HookEvent("player_death", Event_PlayerDeath); 11 | HookEvent("pass_score", Event_PassScore); 12 | HookEvent("rocket_jump", Event_WeaponJump); 13 | HookEvent("sticky_jump", Event_WeaponJump); 14 | } 15 | 16 | public void Event_RoundStart(Event event, const char[] sName, bool bDontBroadcast) 17 | { 18 | if (!g_bEnabled) 19 | return; 20 | 21 | Group_TriggerRandomizeAll(RandomizedAction_Round); 22 | 23 | if (event.GetBool("full_reset")) 24 | Group_TriggerRandomizeAll(RandomizedAction_RoundFull); 25 | } 26 | 27 | public void Event_PointCaptured(Event hEvent, const char[] sName, bool bDontBroadcast) 28 | { 29 | if (!g_bEnabled) 30 | return; 31 | 32 | char[] sCappers = new char[MaxClients]; 33 | 34 | hEvent.GetString("cappers", sCappers, MaxClients+1); 35 | 36 | for (int i = 0; i < MaxClients; i++) 37 | if (sCappers[i]) 38 | Group_TriggerRandomizeClient(view_as(sCappers[i]), RandomizedAction_CPCapture); 39 | } 40 | 41 | public void Event_FlagCaptured(Event hEvent, const char[] sName, bool bDontBroadcast) 42 | { 43 | if (!g_bEnabled) 44 | return; 45 | 46 | if (hEvent.GetInt("eventtype") != 2) //Check if it actually capture and not other events 47 | return; 48 | 49 | int iClient = hEvent.GetInt("player"); 50 | if (iClient <= 0 || iClient > MaxClients) 51 | return; 52 | 53 | Group_TriggerRandomizeClient(iClient, RandomizedAction_FlagCapture); 54 | } 55 | 56 | public void Event_PlayerInventoryUpdate(Event event, const char[] sName, bool bDontBroadcast) 57 | { 58 | if (!g_bEnabled) 59 | return; 60 | 61 | int iClient = GetClientOfUserId(event.GetInt("userid")); 62 | if (TF2_GetClientTeam(iClient) <= TFTeam_Spectator) 63 | return; 64 | 65 | //Refill charge meters 66 | int iWeapon, iPos; 67 | while (TF2_GetItem(iClient, iWeapon, iPos)) 68 | { 69 | if (!TF2Attrib_HookValueFloat(0.0, "item_meter_resupply_denied", iWeapon)) 70 | Properties_AddWeaponChargeMeter(iClient, iWeapon, 100.0); 71 | } 72 | } 73 | 74 | public void Event_PlayerSpawn(Event event, const char[] sName, bool bDontBroadcast) 75 | { 76 | if (!g_bEnabled) 77 | return; 78 | 79 | int iClient = GetClientOfUserId(event.GetInt("userid")); 80 | if (TF2_GetClientTeam(iClient) <= TFTeam_Spectator) 81 | return; 82 | 83 | Loadout_RefreshClient(iClient); 84 | 85 | //Set max health after giving weapons 86 | SetEntProp(iClient, Prop_Send, "m_iHealth", SDKCall_GetMaxHealth(iClient)); 87 | 88 | //Because client caught sourcemod changes faster than its own prediction (somehow), 89 | // remove rune and add a delay to give it back so icon above head appears properly 90 | SDKCall_SetCarryingRuneType(GetEntityAddress(iClient) + view_as
(g_iOffsetPlayerShared), -1); 91 | CreateTimer(0.2, Loadout_TimerApplyClientRune, iClient); 92 | } 93 | 94 | public void Event_PlayerRegenerate(Event event, const char[] sName, bool bDontBroadcast) 95 | { 96 | if (!g_bEnabled) 97 | return; 98 | 99 | //This event dont have any params, not even userid 100 | //Regenerate screws up max ammo after InitClass, give it back 101 | for (int iAmmoType = 0; iAmmoType < TF_AMMO_COUNT; iAmmoType++) 102 | GivePlayerAmmo(g_iClientInitClass, SDKCall_GetMaxAmmo(g_iClientInitClass, iAmmoType), iAmmoType, true); 103 | } 104 | 105 | public void Event_PlayerHurt(Event event, const char[] sName, bool bDontBroadcast) 106 | { 107 | if (!g_bEnabled) 108 | return; 109 | 110 | int iClient = GetClientOfUserId(event.GetInt("userid")); 111 | int iAttacker = GetClientOfUserId(event.GetInt("attacker")); 112 | float flDamage = float(event.GetInt("damageamount")); 113 | 114 | if (0 < iAttacker <= MaxClients && iAttacker != iClient) 115 | { 116 | int iWeapon, iPos; 117 | while (TF2_GetItem(iAttacker, iWeapon, iPos)) 118 | { 119 | float flRate = TF2Attrib_HookValueFloat(0.0, "item_meter_damage_for_full_charge", iWeapon); 120 | if (flRate) 121 | { 122 | //Increase meter for Gas Passer, 123 | // could add check whenever if item_meter_charge_type value is 3, meh 124 | flRate = flDamage / flRate * 100.0; 125 | Properties_AddWeaponChargeMeter(iAttacker, iWeapon, flRate); 126 | } 127 | 128 | if (TF2Attrib_HookValueFloat(0.0, "hype_on_damage", iWeapon) && !TF2_IsPlayerInCondition(iAttacker, TFCond_CritHype)) 129 | { 130 | //Soda popper 131 | float flHype = RemapValClamped(flDamage, 1.0, 200.0, 1.0, 50.0); //This really is valve's method for hype meter, weird 132 | flHype = min(Properties_GetWeaponPropFloat(iWeapon, "m_flHypeMeter") + flHype, 100.0); 133 | Properties_SetWeaponPropFloat(iWeapon, "m_flHypeMeter", flHype); 134 | } 135 | 136 | if (TF2Attrib_HookValueFloat(0.0, "boost_on_damage", iWeapon)) 137 | { 138 | //Baby Face Blaster 139 | float flHype = Properties_GetWeaponPropFloat(iWeapon, "m_flHypeMeter"); 140 | flHype = min(FindConVar("tf_scout_hype_pep_max").FloatValue, flHype + (max(FindConVar("tf_scout_hype_pep_min_damage").FloatValue, flDamage) / FindConVar("tf_scout_hype_pep_mod").FloatValue)); 141 | Properties_SetWeaponPropFloat(iWeapon, "m_flHypeMeter", flHype); 142 | } 143 | } 144 | } 145 | 146 | int iWeapon, iPos; 147 | while (TF2_GetItem(iClient, iWeapon, iPos)) 148 | { 149 | float flVal = TF2Attrib_HookValueFloat(0.0, "lose_hype_on_take_damage", iWeapon); 150 | if (flVal) 151 | { 152 | float flHype = Properties_GetWeaponPropFloat(iWeapon, "m_flHypeMeter"); 153 | Properties_SetWeaponPropFloat(iWeapon, "m_flHypeMeter", max(0.0, flHype - (flVal * flDamage))); 154 | } 155 | } 156 | } 157 | 158 | public void Event_PlayerDeath(Event event, const char[] sName, bool bDontBroadcast) 159 | { 160 | if (!g_bEnabled) 161 | return; 162 | 163 | int iVictim = GetClientOfUserId(event.GetInt("userid")); 164 | int iAttacker = GetClientOfUserId(event.GetInt("attacker")); 165 | int iAssister = GetClientOfUserId(event.GetInt("assister")); 166 | bool bDeadRinger = (event.GetInt("death_flags") & TF_DEATHFLAG_DEADRINGER) != 0; 167 | int iCustomKill = event.GetInt("customkill"); 168 | 169 | if (bDeadRinger) 170 | { 171 | g_bFeignDeath[iVictim] = true; 172 | } 173 | else 174 | { 175 | Group_TriggerRandomizeClient(iVictim, RandomizedAction_Death); 176 | 177 | if (0 < iAttacker <= MaxClients && iVictim != iAttacker) 178 | Group_TriggerRandomizeClient(iVictim, RandomizedAction_DeathKill); 179 | else if (iCustomKill == TF_CUSTOM_SUICIDE) 180 | Group_TriggerRandomizeClient(iVictim, RandomizedAction_DeathSuicide); 181 | else 182 | Group_TriggerRandomizeClient(iVictim, RandomizedAction_DeathEnv); 183 | } 184 | 185 | if (0 < iAttacker <= MaxClients && iVictim != iAttacker) 186 | Group_TriggerRandomizeClient(iAttacker, RandomizedAction_Kill); 187 | if (0 < iAssister <= MaxClients && iVictim != iAssister) 188 | Group_TriggerRandomizeClient(iAssister, RandomizedAction_Assist); 189 | } 190 | 191 | public void Event_PassScore(Event event, const char[] sName, bool bDontBroadcast) 192 | { 193 | if (!g_bEnabled) 194 | return; 195 | 196 | int iClient = event.GetInt("scorer"); 197 | Group_TriggerRandomizeClient(iClient, RandomizedAction_PassScore); 198 | } 199 | 200 | public void Event_WeaponJump(Event event, const char[] sName, bool bDontBroadcast) 201 | { 202 | if (!g_bEnabled) 203 | return; 204 | 205 | //Class check should be done by this point, revert class so pain sound can be played as actual class 206 | int iClient = GetClientOfUserId(event.GetInt("userid")); 207 | if (g_bOnTakeDamageClass[iClient]) 208 | { 209 | RevertClientClass(iClient); 210 | g_bOnTakeDamageClass[iClient] = false; 211 | } 212 | } -------------------------------------------------------------------------------- /scripting/randomizer/patch.sp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This section is used to fix bugs, mostly class checks by changing instructions in TF2 memory. 4 | It loops through each lists in gamedata by numbers, starting from 01. Ends loop when number at "PatchSig" could not be found. 5 | "PatchSig" is used to find matching signature in TF2 memory. 6 | "PatchReplace" is memory used to set in TF2 memory. Applies in same position as address from "PatchSig" 7 | 8 | makememsearch.idc, a modification of makesig.idc, is recommended to use to make signatures. Source code can be found here: 9 | https://github.com/FortyTwoFortyTwo/Randomizer/blob/master/scripts/makememsearch.idc 10 | 11 | */ 12 | 13 | #define PATCH_MAX 64 14 | #define PATCH_SPLIT "\\x" 15 | 16 | #define PATCH_SEARCH "PatchSig" 17 | #define PATCH_REPLACE "PatchReplace" 18 | 19 | enum struct Patch 20 | { 21 | Address pAddress; 22 | int iPatchCount; 23 | int iValueOriginal[PATCH_MAX]; 24 | int iValueReplacement[PATCH_MAX]; 25 | bool bFirstPatch; 26 | 27 | bool Load(GameData hGameData, const char[] sName, bool bSkipWarning = false) 28 | { 29 | //PatchReplace should be checked for more numbers instead of PatchSig, 30 | // would help report error if PatchSig broke from TF2 update 31 | 32 | char sBuffer[32]; 33 | char sReplaceValue[PATCH_MAX * 4]; 34 | Format(sBuffer, sizeof(sBuffer), PATCH_REPLACE ... "_%s", sName); 35 | if (!hGameData.GetKeyValue(sBuffer, sReplaceValue, sizeof(sReplaceValue))) 36 | { 37 | if (!bSkipWarning) 38 | LogError("Could not find Gamedata key value '%s'", sBuffer); 39 | 40 | return false; //No more numbers to search 41 | } 42 | 43 | this.iPatchCount = Patch_StringToMemory(sReplaceValue, this.iValueReplacement); 44 | if (this.iPatchCount <= 0) 45 | { 46 | LogError("Gamedata key '%s' has invalid memory value '%s'", sBuffer, sReplaceValue); 47 | return true; 48 | } 49 | 50 | Format(sBuffer, sizeof(sBuffer), PATCH_SEARCH ... "_%s", sName); 51 | this.pAddress = hGameData.GetAddress(sBuffer); 52 | if (!this.pAddress) 53 | { 54 | LogError("Could not find Gamedata address or invalid value '%s'", sBuffer); 55 | this.iPatchCount = 0; 56 | return true; 57 | } 58 | 59 | for (int i = 0; i < this.iPatchCount; i++) 60 | this.iValueOriginal[i] = LoadFromAddress(this.pAddress + view_as
(i), NumberType_Int8); 61 | 62 | return true; 63 | } 64 | 65 | bool Enable() 66 | { 67 | for (int i = 0; i < this.iPatchCount; i++) 68 | StoreToAddress(this.pAddress + view_as
(i), this.iValueReplacement[i], NumberType_Int8, !this.bFirstPatch); 69 | 70 | // Updating mem access only need to be done once to each patches, after first patch we don't need to update mem access again 71 | if (!this.bFirstPatch) 72 | { 73 | this.bFirstPatch = true; 74 | return true; 75 | } 76 | 77 | return false; 78 | } 79 | 80 | void Disable() 81 | { 82 | // Assuming that Enable() is called before Disable(), not needing to update mem access 83 | 84 | for (int i = 0; i < this.iPatchCount; i++) 85 | StoreToAddress(this.pAddress + view_as
(i), this.iValueOriginal[i], NumberType_Int8, false); 86 | } 87 | } 88 | 89 | enum struct PatchSpeed 90 | { 91 | Address pAddress; 92 | int iValueOriginal; 93 | int bFirstPatch; 94 | } 95 | 96 | static ArrayList g_aSpeedPatches; //Arrays of PatchSpeed 97 | static Patch g_pIsPlayerClass; 98 | int g_iAllowPlayerClass; 99 | 100 | void Patch_Init(GameData hGameData) 101 | { 102 | int iWildcardMemory[PATCH_MAX]; 103 | int iWhitelistCount = Patch_GetGamedataMemory(hGameData, "PatchWildcard_Speed", iWildcardMemory); 104 | 105 | int iMaxBits = Patch_GetGamedataValueInt(hGameData, "PatchBits_Speed"); 106 | 107 | Address pAddress = hGameData.GetMemSig("CTFPlayer::TeamFortress_CalculateMaxSpeed"); 108 | 109 | g_aSpeedPatches = new ArrayList(sizeof(PatchSpeed)); 110 | int iCount = 0; 111 | 112 | do 113 | { 114 | iCount++; 115 | char sKey[64]; 116 | Format(sKey, sizeof(sKey), "PatchSearch_Speed%02d", iCount); 117 | 118 | int iMemory[PATCH_MAX]; 119 | int iMemoryCount = Patch_GetGamedataMemory(hGameData, sKey, iMemory); 120 | if (iMemoryCount == 0) 121 | break; 122 | 123 | int iPosition; 124 | PatchSpeed patch; 125 | for (Address pOffset; pOffset < view_as
(iMaxBits); pOffset++) 126 | { 127 | if (Patch_CheckValue(pAddress + pOffset, iMemory[iPosition], patch, iWildcardMemory, iWhitelistCount)) 128 | { 129 | iPosition++; 130 | } 131 | else if (iPosition > 0) 132 | { 133 | iPosition = 0; 134 | if (Patch_CheckValue(pAddress + pOffset, iMemory[iPosition], patch, iWildcardMemory, iWhitelistCount)) 135 | iPosition++; 136 | } 137 | 138 | if (iPosition >= iMemoryCount) 139 | { 140 | g_aSpeedPatches.PushArray(patch); 141 | iPosition = 0; 142 | } 143 | } 144 | } 145 | while (iCount); //Infinite loop until break 146 | 147 | int iLength = g_aSpeedPatches.Length; 148 | int iMaxCount = Patch_GetGamedataValueInt(hGameData, "PatchCount_Speed"); 149 | if (iLength != iMaxCount) 150 | { 151 | LogError("Found unexpected amount of speed patches to apply (expected %d, found %d), listing offsets found:", iMaxCount, iLength); 152 | for (int i = 0; i < iLength; i++) 153 | { 154 | PatchSpeed patch; 155 | g_aSpeedPatches.GetArray(i, patch); 156 | LogError("#%02d: %d", i + 1, patch.pAddress - pAddress); 157 | } 158 | 159 | g_aSpeedPatches.Clear(); 160 | } 161 | 162 | g_pIsPlayerClass.Load(hGameData, "IsPlayerClass"); 163 | } 164 | 165 | bool Patch_CheckValue(Address pAddress, int iExpected, PatchSpeed patch, int iWildcardMemory[PATCH_MAX], int iWhitelistCount) 166 | { 167 | int iValue = LoadFromAddress(pAddress, NumberType_Int8); 168 | 169 | if (iValue == iExpected) 170 | { 171 | return true; 172 | } 173 | else if (iExpected == 0x2A) 174 | { 175 | patch.pAddress = pAddress; 176 | patch.iValueOriginal = iValue; 177 | 178 | for (int i = 0; i < iWhitelistCount; i++) 179 | if (iValue == iWildcardMemory[i]) 180 | return true; 181 | } 182 | 183 | return false; 184 | } 185 | 186 | void Patch_SetSpeed(TFClassType nClass) 187 | { 188 | int iLength = g_aSpeedPatches.Length; 189 | for (int i = 0; i < iLength; i++) 190 | { 191 | PatchSpeed patch; 192 | g_aSpeedPatches.GetArray(i, patch); 193 | StoreToAddress(patch.pAddress, nClass, NumberType_Int8, !patch.bFirstPatch); 194 | if (!patch.bFirstPatch) 195 | { 196 | patch.bFirstPatch = true; 197 | g_aSpeedPatches.SetArray(i, patch); 198 | } 199 | } 200 | } 201 | 202 | void Patch_RevertSpeed() 203 | { 204 | int iLength = g_aSpeedPatches.Length; 205 | for (int i = 0; i < iLength; i++) 206 | { 207 | PatchSpeed patch; 208 | g_aSpeedPatches.GetArray(i, patch); 209 | StoreToAddress(patch.pAddress, patch.iValueOriginal, NumberType_Int8); 210 | } 211 | } 212 | 213 | void Patch_EnableIsPlayerClass() 214 | { 215 | if (g_iAllowPlayerClass == 0) 216 | g_pIsPlayerClass.Enable(); 217 | 218 | g_iAllowPlayerClass++; 219 | } 220 | 221 | void Patch_DisableIsPlayerClass() 222 | { 223 | g_iAllowPlayerClass--; 224 | 225 | if (g_iAllowPlayerClass == 0) 226 | g_pIsPlayerClass.Disable(); 227 | } 228 | 229 | int Patch_StringToMemory(const char[] sValue, int iMemory[PATCH_MAX]) 230 | { 231 | char sBytes[PATCH_MAX][4]; 232 | int iCount = ExplodeString(sValue, PATCH_SPLIT, sBytes, sizeof(sBytes), sizeof(sBytes[])) - 1; 233 | for (int i = 0; i < iCount; i++) 234 | { 235 | if (!StringToIntEx(sBytes[i+1], iMemory[i], 16)) 236 | return 0; 237 | } 238 | 239 | return iCount; 240 | } 241 | 242 | int Patch_GetGamedataMemory(GameData hGameData, const char[] sKey, int iMemory[PATCH_MAX]) 243 | { 244 | char sBuffer[PATCH_MAX * 4]; 245 | hGameData.GetKeyValue(sKey, sBuffer, sizeof(sBuffer)); 246 | return Patch_StringToMemory(sBuffer, iMemory); 247 | } 248 | 249 | int Patch_GetGamedataValueInt(GameData hGameData, const char[] sKey) 250 | { 251 | char sValue[12]; 252 | hGameData.GetKeyValue(sKey, sValue, sizeof(sValue)); 253 | return StringToInt(sValue); 254 | } 255 | -------------------------------------------------------------------------------- /scripting/randomizer/commands.sp: -------------------------------------------------------------------------------- 1 | static char g_sSlotName[][] = { 2 | "Primary", 3 | "Secondary", 4 | "Melee", 5 | "PDA1", 6 | "PDA2", 7 | "Building", 8 | }; 9 | 10 | void Commands_Init() 11 | { 12 | RegAdminCmd("sm_rndclass", Command_Class, ADMFLAG_CHANGEMAP); 13 | RegAdminCmd("sm_rndsetweapon", Command_SetWeapon, ADMFLAG_CHANGEMAP); 14 | RegAdminCmd("sm_rndsetslotweapon", Command_SetSlotWeapon, ADMFLAG_CHANGEMAP); 15 | RegAdminCmd("sm_rndgiveweapon", Command_GiveWeapon, ADMFLAG_CHANGEMAP); 16 | RegAdminCmd("sm_rndrune", Command_Rune, ADMFLAG_CHANGEMAP); 17 | RegAdminCmd("sm_rndgenerate", Command_Generate, ADMFLAG_CHANGEMAP); 18 | } 19 | 20 | public Action Command_Class(int iClient, int iArgs) 21 | { 22 | if (!g_bEnabled) 23 | return Plugin_Continue; 24 | 25 | if (iArgs < 2) 26 | { 27 | ReplyToCommand(iClient, "Format: sm_rndclass <@target> "); 28 | return Plugin_Handled; 29 | } 30 | 31 | char sGroup[32], sClass[32]; 32 | GetCmdArg(1, sGroup, sizeof(sGroup)); 33 | GetCmdArg(2, sClass, sizeof(sClass)); 34 | TFClassType nClass = TF2_GetClass(sClass); 35 | if (nClass == TFClass_Unknown) 36 | { 37 | ReplyToCommand(iClient, "Unable to get class '%s'", sClass); 38 | return Plugin_Handled; 39 | } 40 | 41 | int[] iTargetList = new int[MaxClients]; 42 | char sGroupName[MAX_TARGET_LENGTH]; 43 | bool bIsML; 44 | 45 | int iTargetCount = ProcessTargetString(sGroup, iClient, iTargetList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 46 | if (iTargetCount <= 0) 47 | { 48 | ReplyToCommand(iClient, "Could not find anyone to set class"); 49 | return Plugin_Handled; 50 | } 51 | 52 | Loadout_SetClass(iTargetList, iTargetCount, nClass); 53 | 54 | ReplyToCommand(iClient, "Set %s class to %s", sGroupName, sClass); 55 | return Plugin_Handled; 56 | } 57 | 58 | public Action Command_SetWeapon(int iClient, int iArgs) 59 | { 60 | if (!g_bEnabled) 61 | return Plugin_Continue; 62 | 63 | if (iArgs < 2) 64 | { 65 | ReplyToCommand(iClient, "Format: sm_rndsetweapon <@target> "); 66 | return Plugin_Handled; 67 | } 68 | 69 | char sGroup[32]; 70 | GetCmdArg(1, sGroup, sizeof(sGroup)); 71 | 72 | int[] iTargetList = new int[MaxClients]; 73 | char sGroupName[MAX_TARGET_LENGTH]; 74 | bool bIsML; 75 | 76 | int iTargetCount = ProcessTargetString(sGroup, iClient, iTargetList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 77 | if (iTargetCount <= 0) 78 | { 79 | ReplyToCommand(iClient, "Could not find anyone to set weapons"); 80 | return Plugin_Handled; 81 | } 82 | 83 | RandomizedWeapon eWeapon[MAX_WEAPONS]; 84 | int iCount = GetWeaponsFromCommand(iClient, eWeapon); 85 | if (iCount == 0) 86 | return Plugin_Handled; 87 | 88 | Loadout_SetWeapon(iTargetList, iTargetCount, eWeapon, iCount); 89 | 90 | if (iCount == 1) 91 | { 92 | char sName[256]; 93 | Weapons_GetName(eWeapon[0].iIndex, sName, sizeof(sName)); 94 | Format(sName, sizeof(sName), "%T", sName, LANG_SERVER); 95 | ReplyToCommand(iClient, "Set %s weapon '%s' for slot '%s'", sGroupName, sName, g_sSlotName[eWeapon[0].iSlot]); 96 | } 97 | else 98 | { 99 | ReplyToCommand(iClient, "Set %s %d weapons", sGroupName, iCount); 100 | } 101 | 102 | return Plugin_Handled; 103 | } 104 | 105 | public Action Command_SetSlotWeapon(int iClient, int iArgs) 106 | { 107 | if (!g_bEnabled) 108 | return Plugin_Continue; 109 | 110 | if (iArgs < 2) 111 | { 112 | ReplyToCommand(iClient, "Format: sm_rndsetweapon <@target> "); 113 | return Plugin_Handled; 114 | } 115 | 116 | char sGroup[32]; 117 | GetCmdArg(1, sGroup, sizeof(sGroup)); 118 | 119 | int[] iTargetList = new int[MaxClients]; 120 | char sGroupName[MAX_TARGET_LENGTH]; 121 | bool bIsML; 122 | 123 | int iTargetCount = ProcessTargetString(sGroup, iClient, iTargetList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 124 | if (iTargetCount <= 0) 125 | { 126 | ReplyToCommand(iClient, "Could not find anyone to set weapons"); 127 | return Plugin_Handled; 128 | } 129 | 130 | RandomizedWeapon eWeapon[MAX_WEAPONS]; 131 | int iCount = GetWeaponsFromCommand(iClient, eWeapon); 132 | if (iCount == 0) 133 | return Plugin_Handled; 134 | 135 | Loadout_SetSlotWeapon(iTargetList, iTargetCount, eWeapon, iCount); 136 | 137 | if (iCount == 1) 138 | { 139 | char sName[256]; 140 | Weapons_GetName(eWeapon[0].iIndex, sName, sizeof(sName)); 141 | Format(sName, sizeof(sName), "%T", sName, LANG_SERVER); 142 | ReplyToCommand(iClient, "Set %s weapon '%s' for slot '%s'", sGroupName, sName, g_sSlotName[eWeapon[0].iSlot]); 143 | } 144 | else 145 | { 146 | ReplyToCommand(iClient, "Set %s %d weapons", sGroupName, iCount); 147 | } 148 | 149 | return Plugin_Handled; 150 | } 151 | 152 | public Action Command_GiveWeapon(int iClient, int iArgs) 153 | { 154 | if (!g_bEnabled) 155 | return Plugin_Continue; 156 | 157 | if (iArgs < 2) 158 | { 159 | ReplyToCommand(iClient, "Format: sm_rndgiveweapon <@target> "); 160 | return Plugin_Handled; 161 | } 162 | 163 | char sGroup[32]; 164 | GetCmdArg(1, sGroup, sizeof(sGroup)); 165 | 166 | int[] iTargetList = new int[MaxClients]; 167 | char sGroupName[MAX_TARGET_LENGTH]; 168 | bool bIsML; 169 | 170 | int iTargetCount = ProcessTargetString(sGroup, iClient, iTargetList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 171 | if (iTargetCount <= 0) 172 | { 173 | ReplyToCommand(iClient, "Could not find anyone to give weapons"); 174 | return Plugin_Handled; 175 | } 176 | 177 | RandomizedWeapon eWeapon[MAX_WEAPONS]; 178 | int iCount = GetWeaponsFromCommand(iClient, eWeapon); 179 | if (iCount == 0) 180 | return Plugin_Handled; 181 | 182 | Loadout_GiveWeapon(iTargetList, iTargetCount, eWeapon, iCount); 183 | 184 | if (iCount == 1) 185 | { 186 | char sName[256]; 187 | Weapons_GetName(eWeapon[0].iIndex, sName, sizeof(sName)); 188 | Format(sName, sizeof(sName), "%T", sName, LANG_SERVER); 189 | ReplyToCommand(iClient, "Set %s weapon '%s' for slot '%s'", sGroupName, sName, g_sSlotName[eWeapon[0].iSlot]); 190 | } 191 | else 192 | { 193 | ReplyToCommand(iClient, "Set %s %d weapons", sGroupName, iCount); 194 | } 195 | 196 | return Plugin_Handled; 197 | } 198 | 199 | public Action Command_Rune(int iClient, int iArgs) 200 | { 201 | if (!g_bEnabled) 202 | return Plugin_Continue; 203 | 204 | if (iArgs < 2) 205 | { 206 | ReplyToCommand(iClient, "Format: sm_rndrune <@target> "); 207 | return Plugin_Handled; 208 | } 209 | 210 | char sGroup[32], sRuneType[32]; 211 | GetCmdArg(1, sGroup, sizeof(sGroup)); 212 | GetCmdArg(2, sRuneType, sizeof(sRuneType)); 213 | 214 | int iRuneType; 215 | if (!StringToIntEx(sRuneType, iRuneType)) 216 | { 217 | ReplyToCommand(iClient, "Unknown rune id '%s'", sRuneType); 218 | return Plugin_Handled; 219 | } 220 | else if (iRuneType < -1 || iRuneType >= g_iRuneCount) 221 | { 222 | ReplyToCommand(iClient, "Rune id must be between -1 to %d", g_iRuneCount - 1); 223 | return Plugin_Handled; 224 | } 225 | 226 | int[] iTargetList = new int[MaxClients]; 227 | char sGroupName[MAX_TARGET_LENGTH]; 228 | bool bIsML; 229 | 230 | int iTargetCount = ProcessTargetString(sGroup, iClient, iTargetList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 231 | if (iTargetCount <= 0) 232 | { 233 | ReplyToCommand(iClient, "Could not find anyone to set rune"); 234 | return Plugin_Handled; 235 | } 236 | 237 | Loadout_SetRune(iTargetList, iTargetCount, iRuneType); 238 | 239 | ReplyToCommand(iClient, "Set %s rune to %d", sGroupName, iRuneType); 240 | return Plugin_Handled; 241 | } 242 | 243 | public Action Command_Generate(int iClient, int iArgs) 244 | { 245 | if (!g_bEnabled) 246 | return Plugin_Continue; 247 | 248 | if (iArgs < 1) 249 | { 250 | ReplyToCommand(iClient, "Format: sm_rndgenerate <@target>"); 251 | return Plugin_Handled; 252 | } 253 | 254 | char sGroup[32]; 255 | GetCmdArg(1, sGroup, sizeof(sGroup)); 256 | 257 | int[] iTargetList = new int[MaxClients]; 258 | char sGroupName[MAX_TARGET_LENGTH]; 259 | bool bIsML; 260 | 261 | int iTargetCount = ProcessTargetString(sGroup, iClient, iTargetList, MaxClients, COMMAND_FILTER_NO_IMMUNITY, sGroupName, sizeof(sGroupName), bIsML); 262 | if (iTargetCount <= 0) 263 | { 264 | ReplyToCommand(iClient, "Could not find anyone to regenerate class and weapons"); 265 | return Plugin_Handled; 266 | } 267 | 268 | for (int i = 0; i < iTargetCount; i++) 269 | { 270 | Loadout_RandomizeClientAll(iTargetList[i]); 271 | Loadout_RefreshClient(iTargetList[i]); 272 | } 273 | 274 | ReplyToCommand(iClient, "Regenerated %s loadout", sGroupName); 275 | return Plugin_Handled; 276 | } 277 | 278 | int GetWeaponsFromCommand(int iClient, RandomizedWeapon eWeapon[MAX_WEAPONS]) 279 | { 280 | //Grab whole args, skip first 1 arg on target 281 | char sCommand[256]; 282 | GetCmdArgString(sCommand, sizeof(sCommand)); 283 | int iArg = 1, iLen = strlen(sCommand); 284 | for (int i = 1; i < iLen; i++) 285 | { 286 | if (sCommand[i] == ' ' && sCommand[i-1] != ' ') 287 | iArg++; 288 | 289 | if (iArg == 2) 290 | { 291 | Format(sCommand, sizeof(sCommand), sCommand[i+1]); 292 | break; 293 | } 294 | } 295 | 296 | char sWeapons[MAX_WEAPONS][64]; 297 | int iCount = ExplodeString(sCommand, ",", sWeapons, sizeof(sWeapons), sizeof(sWeapons[])); 298 | for (int i = 0; i < iCount; i++) 299 | { 300 | TrimString(sWeapons[i]); 301 | eWeapon[i].Reset(); 302 | eWeapon[i].iSlot = RemoveSlotFromCommand(sWeapons[i], sizeof(sWeapons[])); 303 | 304 | if (!StringToIntEx(sWeapons[i], eWeapon[i].iIndex)) 305 | eWeapon[i].iIndex = Weapons_GetIndexFromName(sWeapons[i]); 306 | 307 | if (eWeapon[i].iIndex == -1) 308 | { 309 | ReplyToCommand(iClient, "Unable to find weapon by name '%s'", sWeapons[i]); 310 | return 0; 311 | } 312 | 313 | bool bValid; 314 | 315 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 316 | { 317 | int iSlot = TF2_GetSlotFromIndex(eWeapon[i].iIndex, view_as(iClass)); 318 | if (eWeapon[i].iSlot != -1 && eWeapon[i].iSlot == iSlot) 319 | { 320 | bValid = true; 321 | break; 322 | } 323 | else if (eWeapon[i].iSlot == -1 && 0 <= iSlot <= WeaponSlot_Building) 324 | { 325 | eWeapon[i].iSlot = iSlot; 326 | bValid = true; 327 | break; 328 | } 329 | } 330 | 331 | if (!bValid) 332 | { 333 | if (eWeapon[i].iSlot == -1) 334 | { 335 | ReplyToCommand(iClient, "Cannot find valid slot for Weapon index '%d'", eWeapon[i].iIndex); 336 | } 337 | else 338 | { 339 | char sName[256]; 340 | Weapons_GetName(eWeapon[i].iIndex, sName, sizeof(sName)); 341 | Format(sName, sizeof(sName), "%T", sName, LANG_SERVER); 342 | ReplyToCommand(iClient, "Weapon '%s' cannot be used for slot '%s'", sName, g_sSlotName[eWeapon[i].iSlot]); 343 | } 344 | 345 | return 0; 346 | } 347 | } 348 | 349 | return iCount; 350 | } 351 | 352 | int RemoveSlotFromCommand(char[] sCommand, int iLength) 353 | { 354 | char sWeapon[8][64]; //Need a better name instead of just without s... 355 | int iCount = ExplodeString(sCommand, " ", sWeapon, sizeof(sWeapon), sizeof(sWeapon[])); 356 | for (int i = 0; i < iCount; i++) 357 | { 358 | for (int iSlot = 0; iSlot < sizeof(g_sSlotName); iSlot++) 359 | { 360 | if (StrContains(g_sSlotName[iSlot], sWeapon[i], false) == 0) 361 | { 362 | char sBuffer[64]; 363 | if (i == 0) 364 | Format(sBuffer, sizeof(sBuffer), "%s ", sWeapon[i]); 365 | else 366 | Format(sBuffer, sizeof(sBuffer), " %s", sWeapon[i]); 367 | 368 | ReplaceString(sCommand, iLength, sBuffer, "", false); 369 | return iSlot; 370 | } 371 | } 372 | } 373 | 374 | return -1; 375 | } -------------------------------------------------------------------------------- /scripting/randomizer/properties.sp: -------------------------------------------------------------------------------- 1 | static StringMap g_mPropertiesWeaponSend[2048]; 2 | static StringMap g_mPropertiesWeaponData[2048]; 3 | 4 | static int g_iPropertiesForceWeaponAmmo = INVALID_ENT_REFERENCE; 5 | static int g_iPropertiesForceWeaponAmmoPriority = 0; 6 | 7 | // Load & Save Send Prop 8 | 9 | void Properties_LoadWeaponPropInt(int iClient, int iWeapon, const char[] sProp, int iElement = 0) 10 | { 11 | if (!g_mPropertiesWeaponSend[iWeapon]) 12 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 13 | 14 | int iValue = 0; 15 | g_mPropertiesWeaponSend[iWeapon].GetValue(sProp, iValue); 16 | SetEntProp(iClient, Prop_Send, sProp, iValue, _, iElement); 17 | } 18 | 19 | void Properties_LoadActiveWeaponPropInt(int iClient, const char[] sProp) 20 | { 21 | int iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 22 | if (iWeapon != INVALID_ENT_REFERENCE) 23 | Properties_LoadWeaponPropInt(iClient, iWeapon, sProp); 24 | } 25 | 26 | void Properties_SaveWeaponPropInt(int iClient, int iWeapon, const char[] sProp, int iElement = 0) 27 | { 28 | if (!g_mPropertiesWeaponSend[iWeapon]) 29 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 30 | 31 | g_mPropertiesWeaponSend[iWeapon].SetValue(sProp, GetEntProp(iClient, Prop_Send, sProp, _, iElement)); 32 | 33 | //Revert back to active weapon 34 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 35 | if (iActiveWeapon != INVALID_ENT_REFERENCE && iActiveWeapon != iWeapon) 36 | Properties_LoadWeaponPropInt(iClient, iActiveWeapon, sProp, iElement); 37 | } 38 | 39 | void Properties_SaveActiveWeaponPropInt(int iClient, const char[] sProp) 40 | { 41 | int iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 42 | if (iWeapon != INVALID_ENT_REFERENCE) 43 | Properties_SaveWeaponPropInt(iClient, iWeapon, sProp); 44 | } 45 | 46 | void Properties_LoadWeaponPropFloat(int iClient, int iWeapon, const char[] sProp, int iElement = 0) 47 | { 48 | if (!g_mPropertiesWeaponSend[iWeapon]) 49 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 50 | 51 | float flValue = 0.0; 52 | g_mPropertiesWeaponSend[iWeapon].GetValue(sProp, flValue); 53 | SetEntPropFloat(iClient, Prop_Send, sProp, flValue, iElement); 54 | } 55 | 56 | void Properties_LoadActiveWeaponPropFloat(int iClient, const char[] sProp) 57 | { 58 | int iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 59 | if (iWeapon != INVALID_ENT_REFERENCE) 60 | Properties_LoadWeaponPropFloat(iClient, iWeapon, sProp); 61 | } 62 | 63 | void Properties_SaveWeaponPropFloat(int iClient, int iWeapon, const char[] sProp, int iElement = 0) 64 | { 65 | if (!g_mPropertiesWeaponSend[iWeapon]) 66 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 67 | 68 | g_mPropertiesWeaponSend[iWeapon].SetValue(sProp, GetEntPropFloat(iClient, Prop_Send, sProp, iElement)); 69 | 70 | //Revert back to active weapon 71 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 72 | if (iActiveWeapon != INVALID_ENT_REFERENCE && iActiveWeapon != iWeapon) 73 | Properties_LoadWeaponPropFloat(iClient, iActiveWeapon, sProp, iElement); 74 | } 75 | 76 | // Load & Save Data Prop 77 | 78 | void Properties_LoadWeaponDataInt(int iClient, int iWeapon, int iOffset) 79 | { 80 | if (!g_mPropertiesWeaponSend[iWeapon]) 81 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 82 | 83 | char sBuffer[16]; 84 | IntToString(iOffset, sBuffer, sizeof(sBuffer)); 85 | 86 | int iValue = 0; 87 | g_mPropertiesWeaponSend[iWeapon].GetValue(sBuffer, iValue); 88 | SetEntData(iClient, iOffset, iValue); 89 | } 90 | 91 | void Properties_SaveWeaponDataInt(int iClient, int iWeapon, int iOffset) 92 | { 93 | if (!g_mPropertiesWeaponSend[iWeapon]) 94 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 95 | 96 | char sBuffer[16]; 97 | IntToString(iOffset, sBuffer, sizeof(sBuffer)); 98 | g_mPropertiesWeaponSend[iWeapon].SetValue(sBuffer, GetEntData(iClient, iOffset)); 99 | 100 | //Revert back to active weapon 101 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 102 | if (iActiveWeapon != INVALID_ENT_REFERENCE && iActiveWeapon != iWeapon) 103 | Properties_LoadWeaponDataInt(iClient, iActiveWeapon, iOffset); 104 | } 105 | 106 | void Properties_LoadWeaponDataFloat(int iClient, int iWeapon, int iOffset) 107 | { 108 | if (!g_mPropertiesWeaponSend[iWeapon]) 109 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 110 | 111 | char sBuffer[16]; 112 | IntToString(iOffset, sBuffer, sizeof(sBuffer)); 113 | 114 | float flValue = 0.0; 115 | g_mPropertiesWeaponSend[iWeapon].GetValue(sBuffer, flValue); 116 | SetEntDataFloat(iClient, iOffset, flValue); 117 | } 118 | 119 | void Properties_SaveWeaponDataFloat(int iClient, int iWeapon, int iOffset) 120 | { 121 | if (!g_mPropertiesWeaponSend[iWeapon]) 122 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 123 | 124 | char sBuffer[16]; 125 | IntToString(iOffset, sBuffer, sizeof(sBuffer)); 126 | g_mPropertiesWeaponSend[iWeapon].SetValue(sBuffer, GetEntDataFloat(iClient, iOffset)); 127 | 128 | //Revert back to active weapon 129 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 130 | if (iActiveWeapon != INVALID_ENT_REFERENCE && iActiveWeapon != iWeapon) 131 | Properties_LoadWeaponDataFloat(iClient, iActiveWeapon, iOffset); 132 | } 133 | 134 | // Other 135 | 136 | int Properties_GetWeaponPropInt(int iWeapon, const char[] sProp) 137 | { 138 | if (!g_mPropertiesWeaponSend[iWeapon]) 139 | return 0; 140 | 141 | int iValue = 0; 142 | g_mPropertiesWeaponSend[iWeapon].GetValue(sProp, iValue); 143 | return iValue; 144 | } 145 | 146 | void Properties_SetWeaponPropInt(int iWeapon, const char[] sProp, int iValue) 147 | { 148 | if (!g_mPropertiesWeaponSend[iWeapon]) 149 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 150 | 151 | g_mPropertiesWeaponSend[iWeapon].SetValue(sProp, iValue); 152 | } 153 | 154 | void Properties_AddWeaponPropInt(int iWeapon, const char[] sProp, int iValue) 155 | { 156 | if (!g_mPropertiesWeaponSend[iWeapon]) 157 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 158 | 159 | int iAdd = 0; 160 | g_mPropertiesWeaponSend[iWeapon].GetValue(sProp, iAdd); 161 | g_mPropertiesWeaponSend[iWeapon].SetValue(sProp, iAdd + iValue); 162 | } 163 | 164 | float Properties_GetWeaponPropFloat(int iWeapon, const char[] sProp) 165 | { 166 | if (!g_mPropertiesWeaponSend[iWeapon]) 167 | return 0.0; 168 | 169 | float flValue = 0.0; 170 | g_mPropertiesWeaponSend[iWeapon].GetValue(sProp, flValue); 171 | return flValue; 172 | } 173 | 174 | void Properties_SetWeaponPropFloat(int iWeapon, const char[] sProp, float flValue) 175 | { 176 | if (!g_mPropertiesWeaponSend[iWeapon]) 177 | g_mPropertiesWeaponSend[iWeapon] = new StringMap(); 178 | 179 | g_mPropertiesWeaponSend[iWeapon].SetValue(sProp, flValue); 180 | } 181 | 182 | void Properties_RemoveWeapon(int iWeapon) 183 | { 184 | delete g_mPropertiesWeaponSend[iWeapon]; 185 | delete g_mPropertiesWeaponData[iWeapon]; 186 | } 187 | 188 | // Ammos 189 | 190 | void Properties_SaveActiveWeaponAmmo(int iClient) 191 | { 192 | if (g_iPropertiesForceWeaponAmmoPriority > 0) 193 | return; 194 | 195 | int iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 196 | if (iWeapon == INVALID_ENT_REFERENCE) 197 | return; 198 | 199 | int iAmmoType = GetEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType"); 200 | if (iAmmoType == -1 || iAmmoType == TF_AMMO_METAL) 201 | return; 202 | 203 | Properties_SaveWeaponPropInt(iClient, iWeapon, "m_iAmmo", iAmmoType); 204 | } 205 | 206 | void Properties_UpdateActiveWeaponAmmo(int iClient) 207 | { 208 | //Update ammo to use active weapon and any other weapons that doesn't use same ammotype 209 | int iMaxWeapons = GetMaxWeapons(); 210 | int[] iWeapons = new int[iMaxWeapons]; 211 | int iAmmoTypeWeapon[TF_AMMO_COUNT]; 212 | 213 | for (int i = 0; i < iMaxWeapons; i++) 214 | { 215 | iWeapons[i] = GetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", i); 216 | if (iWeapons[i] == INVALID_ENT_REFERENCE) 217 | continue; 218 | 219 | int iAmmoType = GetEntProp(iWeapons[i], Prop_Send, "m_iPrimaryAmmoType"); 220 | if (iAmmoType == -1 || iAmmoType == TF_AMMO_METAL) 221 | continue; 222 | 223 | if (!iAmmoTypeWeapon[iAmmoType]) //Ammotype not used yet 224 | iAmmoTypeWeapon[iAmmoType] = iWeapons[i]; 225 | else if (iAmmoTypeWeapon[iAmmoType]) //More than 1 weapon use same ammotype, forget it 226 | iAmmoTypeWeapon[iAmmoType] = INVALID_ENT_REFERENCE; 227 | } 228 | 229 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 230 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 231 | { 232 | int iAmmoType = GetEntProp(iActiveWeapon, Prop_Send, "m_iPrimaryAmmoType"); 233 | if (iAmmoType != -1 && iAmmoType != TF_AMMO_METAL) 234 | iAmmoTypeWeapon[iAmmoType] = iActiveWeapon; 235 | } 236 | 237 | for (int iAmmoType = 0; iAmmoType < TF_AMMO_COUNT; iAmmoType++) 238 | if (iAmmoTypeWeapon[iAmmoType] > 0) 239 | Properties_LoadWeaponPropInt(iClient, iAmmoTypeWeapon[iAmmoType], "m_iAmmo", iAmmoType); 240 | } 241 | 242 | void Properties_SetForceWeaponAmmo(int iWeapon, int iPriority = 0) 243 | { 244 | int iClient = GetEntPropEnt(iWeapon, Prop_Send, "m_hOwnerEntity"); 245 | Properties_SaveActiveWeaponAmmo(iClient); 246 | 247 | int iAmmoType = GetEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType"); 248 | Properties_LoadWeaponPropInt(iClient, iWeapon, "m_iAmmo", iAmmoType); 249 | 250 | g_iPropertiesForceWeaponAmmo = iWeapon; 251 | g_iPropertiesForceWeaponAmmoPriority = iPriority; 252 | } 253 | 254 | void Properties_ResetForceWeaponAmmo(int iPriority = 0) 255 | { 256 | if (iPriority < g_iPropertiesForceWeaponAmmoPriority) 257 | return; 258 | 259 | if (g_iPropertiesForceWeaponAmmo != INVALID_ENT_REFERENCE) 260 | Properties_UpdateActiveWeaponAmmo(GetEntPropEnt(g_iPropertiesForceWeaponAmmo, Prop_Send, "m_hOwnerEntity")); 261 | 262 | g_iPropertiesForceWeaponAmmo = INVALID_ENT_REFERENCE; 263 | g_iPropertiesForceWeaponAmmoPriority = 0; 264 | } 265 | 266 | int Properties_GetForceWeaponAmmo() 267 | { 268 | return g_iPropertiesForceWeaponAmmo; 269 | } 270 | 271 | //Next several functions work together to effectively seperate m_flRageMeter between weapons 272 | //Whenever it matters, we change the players m_flRageMeter and m_bRageDraining to whatever it should be for the weapon's class 273 | 274 | void Properties_LoadRageProps(int iClient, int iWeapon) 275 | { 276 | int iOffset = FindSendPropInfo("CTFPlayer", "m_flNextRageEarnTime"); 277 | 278 | Properties_LoadWeaponPropFloat(iClient, iWeapon, "m_flRageMeter"); 279 | Properties_LoadWeaponPropInt(iClient, iWeapon, "m_bRageDraining"); 280 | Properties_LoadWeaponDataInt(iClient, iWeapon, iOffset + 4); //RageBuff.m_iBuffTypeActive 281 | Properties_LoadWeaponDataInt(iClient, iWeapon, iOffset + 8); //RageBuff.m_iBuffPulseCount 282 | Properties_LoadWeaponDataFloat(iClient, iWeapon, iOffset + 12); //RageBuff.m_flNextBuffPulseTime 283 | } 284 | 285 | void Properties_SaveRageProps(int iClient, int iWeapon) 286 | { 287 | int iOffset = FindSendPropInfo("CTFPlayer", "m_flNextRageEarnTime"); 288 | 289 | Properties_SaveWeaponPropFloat(iClient, iWeapon, "m_flRageMeter"); 290 | Properties_SaveWeaponPropInt(iClient, iWeapon, "m_bRageDraining"); 291 | Properties_SaveWeaponDataInt(iClient, iWeapon, iOffset + 4); //RageBuff.m_iBuffTypeActive 292 | Properties_SaveWeaponDataInt(iClient, iWeapon, iOffset + 8); //RageBuff.m_iBuffPulseCount 293 | Properties_SaveWeaponDataFloat(iClient, iWeapon, iOffset + 12); //RageBuff.m_flNextBuffPulseTime 294 | } 295 | 296 | void Properties_UpdateRageBuffsAndRage(int iClient) 297 | { 298 | if (iClient <= 0 || iClient > MaxClients || !IsClientInGame(iClient)) 299 | return; 300 | 301 | Properies_CallRageMeter(iClient, SDKCall_UpdateRageBuffsAndRage, GetEntityAddress(iClient) + view_as
(g_iOffsetPlayerShared)); 302 | } 303 | 304 | void Properties_ModifyRage(DataPack hPack) 305 | { 306 | hPack.Reset(); 307 | int iClient = GetClientFromSerial(hPack.ReadCell()); 308 | float flAdd = hPack.ReadFloat(); 309 | delete hPack; 310 | 311 | if (iClient <= 0 || iClient > MaxClients || !IsClientInGame(iClient)) 312 | return; 313 | 314 | Properies_CallRageMeter(iClient, SDKCall_ModifyRage, GetEntityAddress(iClient) + view_as
(g_iOffsetPlayerShared), flAdd); 315 | } 316 | 317 | void Properties_HandleRageGain(DataPack hPack) 318 | { 319 | hPack.Reset(); 320 | int iClient = GetClientFromSerial(hPack.ReadCell()); 321 | int iRequiredBuffFlags = hPack.ReadCell(); 322 | float flDamage = hPack.ReadFloat(); 323 | float fInverseRageGainScale = hPack.ReadFloat(); 324 | delete hPack; 325 | 326 | if (iClient <= 0 || iClient > MaxClients || !IsClientInGame(iClient)) 327 | return; 328 | 329 | Properies_CallRageMeter(iClient, SDKCall_HandleRageGain, iClient, iRequiredBuffFlags, flDamage, fInverseRageGainScale); 330 | } 331 | 332 | void Properies_CallRageMeter(int iClient, Function fCall, any nParam1 = 0, any nParam2 = 0, any nParam3 = 0, any nParam4 = 0) 333 | { 334 | //All weapons using set_buff_type, generate_rage_on_dmg and generate_rage_on_heal attrib is tf_weapon and no wearables, for now... 335 | int iMaxWeapons = GetMaxWeapons(); 336 | int[] iWeapons = new int[iMaxWeapons]; 337 | 338 | //Get overall buff type from multiple weapons to subtract down for each ones 339 | float flClientRageType = TF2Attrib_HookValueFloat(0.0, "set_buff_type", iClient); 340 | 341 | //Remove all weapons so they don't interfere with its rage stats 342 | for (int i = 0; i < iMaxWeapons; i++) 343 | { 344 | iWeapons[i] = GetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", i); 345 | SetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", -1, i); 346 | } 347 | 348 | bool bCalledClass[CLASS_MAX + 1]; 349 | 350 | for (int i = 0; i < iMaxWeapons; i++) 351 | { 352 | if (iWeapons[i] == INVALID_ENT_REFERENCE) 353 | continue; 354 | 355 | float flVal; 356 | TFClassType nClass = TF2_GetDefaultClassFromItem(iWeapons[i]); 357 | 358 | //Prevent calling same class twice, but only if it not for rage meter 359 | //Soldier, Pyro and Sniper(?) use rage meter, while Heavy, Engineer, Medic and Sniper use whatever else there 360 | flVal = TF2Attrib_HookValueFloat(0.0, "set_buff_type", iClient); 361 | if (!flVal && bCalledClass[nClass]) 362 | continue; 363 | 364 | //ModifyRage is expected to only be called for Hitman Heatmaker, only increase meter to it 365 | if (fCall == SDKCall_ModifyRage && flVal != 6.0) 366 | continue; 367 | 368 | bCalledClass[nClass] = true; 369 | 370 | if (fCall == SDKCall_UpdateRageBuffsAndRage) 371 | TF2Attrib_SetByName(iClient, "mod soldier buff type", flVal - flClientRageType); 372 | 373 | bool bFocusCond; 374 | if (TF2_IsPlayerInCondition(iClient, TFCond_FocusBuff) && (flVal != 6.0 || !Properties_GetWeaponPropInt(iWeapons[i], "m_bRageDraining"))) 375 | { 376 | //Updating weapons thats not in focus effect, but client is will set weapon to draining 377 | TF2_RemoveConditionFake(iClient, TFCond_FocusBuff); 378 | bFocusCond = true; 379 | } 380 | 381 | SetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", iWeapons[i], i); 382 | Properties_LoadRageProps(iClient, iWeapons[i]); 383 | 384 | SetClientClass(iClient, nClass); 385 | g_iGainingRageWeapon = iWeapons[i]; 386 | 387 | Call_StartFunction(null, fCall); 388 | Call_PushCell(nParam1); 389 | Call_PushCell(nParam2); 390 | Call_PushCell(nParam3); 391 | Call_PushCell(nParam4); 392 | Call_Finish(); 393 | 394 | Properties_SaveRageProps(iClient, iWeapons[i]); 395 | SetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", -1, i); 396 | 397 | if (bFocusCond) 398 | TF2_AddConditionFake(iClient, TFCond_FocusBuff); 399 | 400 | if (fCall == SDKCall_UpdateRageBuffsAndRage) 401 | TF2Attrib_RemoveByName(iClient, "mod soldier buff type"); 402 | 403 | RevertClientClass(iClient); 404 | } 405 | 406 | //Set it back 407 | for (int i = 0; i < iMaxWeapons; i++) 408 | SetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", iWeapons[i], i); 409 | 410 | g_iGainingRageWeapon = INVALID_ENT_REFERENCE; 411 | } 412 | 413 | // Item charge meter 414 | 415 | void Properties_AddWeaponChargeMeter(int iClient, int iWeapon, float flValue) 416 | { 417 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 418 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 419 | Properties_SaveWeaponPropFloat(iClient, iActiveWeapon, "m_flItemChargeMeter", TF2_GetSlot(iActiveWeapon)); 420 | 421 | float flCurrent = Properties_GetWeaponPropFloat(iWeapon, "m_flItemChargeMeter"); 422 | if (flCurrent < 100.0 && flCurrent + flValue >= 100.0) 423 | { 424 | Properties_SetWeaponPropFloat(iWeapon, "m_flItemChargeMeter", 100.0); 425 | 426 | if (HasEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType")) 427 | { 428 | int iAmmoType = GetEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType"); 429 | if (TF_AMMO_GRENADES1 <= iAmmoType <= TF_AMMO_GRENADES3) 430 | { 431 | int iAdd = TF2_GiveAmmo(iClient, iWeapon, Properties_GetWeaponPropInt(iWeapon, "m_iAmmo"), 1, iAmmoType, true, kAmmoSource_Pickup); 432 | 433 | Properties_SaveActiveWeaponAmmo(iClient); 434 | Properties_AddWeaponPropInt(iWeapon, "m_iAmmo", iAdd); 435 | Properties_UpdateActiveWeaponAmmo(iClient); 436 | } 437 | } 438 | 439 | if (IsClassname(iWeapon, "tf_wearable_razorback")) 440 | SetEntProp(iWeapon, Prop_Send, "m_fEffects", GetEntProp(iWeapon, Prop_Send, "m_fEffects") & ~EF_NODRAW); 441 | } 442 | else if (flCurrent < 100.0) 443 | { 444 | Properties_SetWeaponPropFloat(iWeapon, "m_flItemChargeMeter", flCurrent + flValue); 445 | } 446 | 447 | if (iWeapon == iActiveWeapon) 448 | Properties_LoadWeaponPropFloat(iClient, iWeapon, "m_flItemChargeMeter", TF2_GetSlot(iActiveWeapon)); 449 | } -------------------------------------------------------------------------------- /translations/ru/randomizer.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Controls_Stickybomb_Attack2" 4 | { 5 | "ru" "ПКМ, чтобы взорвать" 6 | } 7 | 8 | "Controls_Stickybomb_Reload" 9 | { 10 | "ru" "ПЕРЕЗАРЯДИТЬ, чтобы взорвать" 11 | } 12 | 13 | "Controls_Stickybomb_Unable" 14 | { 15 | "ru" "невозможно взорвать" 16 | } 17 | 18 | "Controls_Shield_Attack2" 19 | { 20 | "ru" "ПКМ для рывка" 21 | } 22 | 23 | "Controls_Shield_Reload" 24 | { 25 | "ru" "ПЕРЕЗАРЯДКА для рывка" 26 | } 27 | 28 | "Controls_Shield_Unable" 29 | { 30 | "ru" "невозможно совершить рывок" 31 | } 32 | 33 | "Controls_Build_Attack2" 34 | { 35 | "ru" "ПКМ для строительства" 36 | } 37 | 38 | "Controls_Build_Attack3" 39 | { 40 | "ru" "СКМ для строительства" 41 | } 42 | 43 | "Controls_Build_Unable" 44 | { 45 | "ru" "строительство невозможно" 46 | } 47 | 48 | "Controls_InvisWatch_Attack2" 49 | { 50 | "ru" "ПКМ для невидимости" 51 | } 52 | 53 | "Controls_InvisWatch_Attack3" 54 | { 55 | "ru" "СКМ для невидимости" 56 | } 57 | 58 | "Controls_InvisWatch_Unable" 59 | { 60 | "ru" "маскировка невозможна" 61 | } 62 | 63 | "Huds_Title" 64 | { 65 | "ru" "Оружие" 66 | } 67 | 68 | "Huds_EffectBarRegenTime" 69 | { 70 | "ru" "{1} сек." 71 | } 72 | 73 | "Huds_Decapitations" 74 | { 75 | "ru" "Головы: {1}" 76 | } 77 | 78 | "Huds_Energy" 79 | { 80 | "ru" "Энергия: {1}%" 81 | } 82 | 83 | "Huds_RageMeter" 84 | { 85 | "ru" "Ярость: {1}%" 86 | } 87 | 88 | "Huds_RevengeCrits" 89 | { 90 | "ru" "Криты: {1}" 91 | } 92 | 93 | "Huds_HypeMeter" 94 | { 95 | "ru" "Заряд: {1}%" 96 | } 97 | 98 | "Huds_ItemChargeMeter" 99 | { 100 | "ru" "{1}%" 101 | } 102 | 103 | "Huds_PipebombCount" 104 | { 105 | "ru" "Бомб-липучек: {1}" 106 | } 107 | 108 | "Huds_ChargeBeginTime" 109 | { 110 | "ru" "Заряд: {1}%" 111 | } 112 | 113 | "Huds_ChargeMeter" 114 | { 115 | "ru" "Заряд: {1}%" 116 | } 117 | 118 | "Huds_Ammo" 119 | { 120 | "ru" "Металл: {1}" 121 | } 122 | 123 | "Huds_ChargeLevel" 124 | { 125 | "ru" "Убер-заряд: {1}%" 126 | } 127 | 128 | "Huds_ChargeResistType_Bullet" 129 | { 130 | "ru" "Тип: Пули" 131 | } 132 | 133 | "Huds_ChargeResistType_Blast" 134 | { 135 | "ru" "Тип: Взрыв" 136 | } 137 | 138 | "Huds_ChargeResistType_Fire" 139 | { 140 | "ru" "Тип: Огонь" 141 | } 142 | 143 | "Huds_MinicritCharge" 144 | { 145 | "ru" "Криты: {1}%" 146 | } 147 | 148 | "Huds_KnifeMeltTimestamp" 149 | { 150 | "ru" "{1} сек." 151 | } 152 | 153 | "Huds_DisguiseTeam_Red" 154 | { 155 | "ru" "Красные" 156 | } 157 | 158 | "Huds_DisguiseTeam_Blue" 159 | { 160 | "ru" "Синие" 161 | } 162 | 163 | "Huds_DisguiseClass_Scout" 164 | { 165 | "ru" "Разведчик" 166 | } 167 | 168 | "Huds_DisguiseClass_Sniper" 169 | { 170 | "ru" "Снайпер" 171 | } 172 | 173 | "Huds_DisguiseClass_Soldier" 174 | { 175 | "ru" "Солдат" 176 | } 177 | 178 | "Huds_DisguiseClass_Demoman" 179 | { 180 | "ru" "Подрывник" 181 | } 182 | 183 | "Huds_DisguiseClass_Medic" 184 | { 185 | "ru" "Медик" 186 | } 187 | 188 | "Huds_DisguiseClass_Heavy" 189 | { 190 | "ru" "Пулемётчик" 191 | } 192 | 193 | "Huds_DisguiseClass_Pyro" 194 | { 195 | "ru" "Поджигатель" 196 | } 197 | 198 | "Huds_DisguiseClass_Spy" 199 | { 200 | "ru" "Шпион" 201 | } 202 | 203 | "Huds_DisguiseClass_Engineer" 204 | { 205 | "ru" "Инженер" 206 | } 207 | 208 | "Weapon_BASEJumper" 209 | { 210 | "ru" "Парашютист" 211 | } 212 | 213 | "Weapon_Shotgun" 214 | { 215 | "ru" "Дробовик" 216 | } 217 | 218 | "Weapon_PanicAttack" 219 | { 220 | "ru" "Паническая атака" 221 | } 222 | 223 | "Weapon_Pistol" 224 | { 225 | "ru" "Пистолет" 226 | } 227 | 228 | "Weapon_ReserveShooter" 229 | { 230 | "ru" "Офицер запаса" 231 | } 232 | 233 | "Weapon_PainTrain" 234 | { 235 | "ru" "Костыль" 236 | } 237 | 238 | "Weapon_HalfZatoichi" 239 | { 240 | "ru" "Полудзатоити" 241 | } 242 | 243 | "Weapon_Scattergun" 244 | { 245 | "ru" "Обрез" 246 | } 247 | 248 | "Weapon_ForceANature" 249 | { 250 | "ru" "Неумолимая сила" 251 | } 252 | 253 | "Weapon_Shortstop" 254 | { 255 | "ru" "Прерыватель" 256 | } 257 | 258 | "Weapon_SodaPopper" 259 | { 260 | "ru" "Газировщик" 261 | } 262 | 263 | "Weapon_BabyFaceBlaster" 264 | { 265 | "ru" "Обрез Малыша" 266 | } 267 | 268 | "Weapon_BackScatter" 269 | { 270 | "ru" "Спинобрез" 271 | } 272 | 273 | "Weapon_BonkAtomicPunch" 274 | { 275 | "ru" "Бонк! Атомный залп" 276 | } 277 | 278 | "Weapon_CritaCola" 279 | { 280 | "ru" "Критокола" 281 | } 282 | 283 | "Weapon_MadMilk" 284 | { 285 | "ru" "Зломолоко" 286 | } 287 | 288 | "Weapon_Winger" 289 | { 290 | "ru" "Окрылённый" 291 | } 292 | 293 | "Weapon_PrettyBoyPocketPistol" 294 | { 295 | "ru" "Дамский пистолет Красавчика" 296 | } 297 | 298 | "Weapon_FlyingGuillotine" 299 | { 300 | "ru" "Летающая гильотина" 301 | } 302 | 303 | "Weapon_Bat" 304 | { 305 | "ru" "Бита" 306 | } 307 | 308 | "Weapon_Sandman" 309 | { 310 | "ru" "Дрёма" 311 | } 312 | 313 | "Weapon_HolyMackerel" 314 | { 315 | "ru" "Поддай леща" 316 | } 317 | 318 | "Weapon_CandyCane" 319 | { 320 | "ru" "Карамельная трость" 321 | } 322 | 323 | "Weapon_BostonBasher" 324 | { 325 | "ru" "Бостонский раздолбай" 326 | } 327 | 328 | "Weapon_SunonaStick" 329 | { 330 | "ru" "Солнце на палочке" 331 | } 332 | 333 | "Weapon_FanOWar" 334 | { 335 | "ru" "Веер войны" 336 | } 337 | 338 | "Weapon_Atomizer" 339 | { 340 | "ru" "Расщепитель" 341 | } 342 | 343 | "Weapon_WrapAssassin" 344 | { 345 | "ru" "Обёрточный убийца" 346 | } 347 | 348 | "Weapon_RocketLauncher" 349 | { 350 | "ru" "Ракетомёт" 351 | } 352 | 353 | "Weapon_DirectHit" 354 | { 355 | "ru" "Прямое попадание" 356 | } 357 | 358 | "Weapon_BlackBox" 359 | { 360 | "ru" "Чёрный ящик" 361 | } 362 | 363 | "Weapon_RocketJumper" 364 | { 365 | "ru" "Тренировочный ракетомёт" 366 | } 367 | 368 | "Weapon_LibertyLauncher" 369 | { 370 | "ru" "Освободитель" 371 | } 372 | 373 | "Weapon_CowMangler5000" 374 | { 375 | "ru" "Линчеватель скота 5000" 376 | } 377 | 378 | "Weapon_Original" 379 | { 380 | "ru" "Прародитель" 381 | } 382 | 383 | "Weapon_BeggarBazooka" 384 | { 385 | "ru" "Базука бродяги" 386 | } 387 | 388 | "Weapon_AirStrike" 389 | { 390 | "ru" "Авиаудар" 391 | } 392 | 393 | "Weapon_BuffBanner" 394 | { 395 | "ru" "Вдохновляющее знамя" 396 | } 397 | 398 | "Weapon_Gunboats" 399 | { 400 | "ru" "Штурмботинки" 401 | } 402 | 403 | "Weapon_BattalionBackup" 404 | { 405 | "ru" "Поддержка батальона" 406 | } 407 | 408 | "Weapon_Concheror" 409 | { 410 | "ru" "Завоеватель" 411 | } 412 | 413 | "Weapon_RighteousBison" 414 | { 415 | "ru" "Благочестивый бизон" 416 | } 417 | 418 | "Weapon_Mantreads" 419 | { 420 | "ru" "Людодавы" 421 | } 422 | 423 | "Weapon_Shovel" 424 | { 425 | "ru" "Лопата" 426 | } 427 | 428 | "Weapon_Equalizer" 429 | { 430 | "ru" "Уравнитель" 431 | } 432 | 433 | "Weapon_MarketGardener" 434 | { 435 | "ru" "Землекоп" 436 | } 437 | 438 | "Weapon_DisciplinaryAction" 439 | { 440 | "ru" "Дисциплинарное взыскание" 441 | } 442 | 443 | "Weapon_EscapePlan" 444 | { 445 | "ru" "План эвакуации" 446 | } 447 | 448 | "Weapon_FlameThrower" 449 | { 450 | "ru" "Огнемёт" 451 | } 452 | 453 | "Weapon_Backburner" 454 | { 455 | "ru" "Дожигатель" 456 | } 457 | 458 | "Weapon_Degreaser" 459 | { 460 | "ru" "Чистильщик" 461 | } 462 | 463 | "Weapon_Phlogistinator" 464 | { 465 | "ru" "Флогистонатор" 466 | } 467 | 468 | "Weapon_DragonFury" 469 | { 470 | "ru" "Ярость дракона" 471 | } 472 | 473 | "Weapon_FlareGun" 474 | { 475 | "ru" "Ракетница" 476 | } 477 | 478 | "Weapon_Detonator" 479 | { 480 | "ru" "Детонатор" 481 | } 482 | 483 | "Weapon_Manmelter" 484 | { 485 | "ru" "Людоплав" 486 | } 487 | 488 | "Weapon_ScorchShot" 489 | { 490 | "ru" "Обжигающий выстрел" 491 | } 492 | 493 | "Weapon_ThermalThruster" 494 | { 495 | "ru" "Термическая тяга" 496 | } 497 | 498 | "Weapon_GasPasser" 499 | { 500 | "ru" "Запасной бак" 501 | } 502 | 503 | "Weapon_FireAxe" 504 | { 505 | "ru" "Пожарный топор" 506 | } 507 | 508 | "Weapon_Axtinguisher" 509 | { 510 | "ru" "Огнетопор" 511 | } 512 | 513 | "Weapon_Homewrecker" 514 | { 515 | "ru" "Крушитель" 516 | } 517 | 518 | "Weapon_Powerjack" 519 | { 520 | "ru" "Разъединитель" 521 | } 522 | 523 | "Weapon_BackScratcher" 524 | { 525 | "ru" "Спиночёс" 526 | } 527 | 528 | "Weapon_SharpenedVolcanoFragment" 529 | { 530 | "ru" "Заострённый осколок вулкана" 531 | } 532 | 533 | "Weapon_ThirdDegree" 534 | { 535 | "ru" "Третья степень" 536 | } 537 | 538 | "Weapon_NeonAnnihilator" 539 | { 540 | "ru" "Неоновый аннигилятор" 541 | } 542 | 543 | "Weapon_HotHand" 544 | { 545 | "ru" "Горячая рука" 546 | } 547 | 548 | "Weapon_GrenadeLauncher" 549 | { 550 | "ru" "Гранатомёт" 551 | } 552 | 553 | "Weapon_LochnLoad" 554 | { 555 | "ru" "Подкидыш" 556 | } 557 | 558 | "Weapon_AliBabaWeeBooties" 559 | { 560 | "ru" "Ботиночки Али-Бабы" 561 | } 562 | 563 | "Weapon_LooseCannon" 564 | { 565 | "ru" "Пушка без лафета" 566 | } 567 | 568 | "Weapon_IronBomber" 569 | { 570 | "ru" "Железный бомбардир" 571 | } 572 | 573 | "Weapon_StickybombLauncher" 574 | { 575 | "ru" "Липучкомёт" 576 | } 577 | 578 | "Weapon_ScottishResistance" 579 | { 580 | "ru" "Шотландское сопротивление" 581 | } 582 | 583 | "Weapon_CharginTarge" 584 | { 585 | "ru" "Штурмовой щит" 586 | } 587 | 588 | "Weapon_StickyJumper" 589 | { 590 | "ru" "Тренировочный липучкомёт" 591 | } 592 | 593 | "Weapon_SplendidScreen" 594 | { 595 | "ru" "Роскошное прикрытие" 596 | } 597 | 598 | "Weapon_TideTurner" 599 | { 600 | "ru" "Верный штурвал" 601 | } 602 | 603 | "Weapon_QuickiebombLauncher" 604 | { 605 | "ru" "Быстромёт" 606 | } 607 | 608 | "Weapon_Bottle" 609 | { 610 | "ru" "Бутылка" 611 | } 612 | 613 | "Weapon_Eyelander" 614 | { 615 | "ru" "Одноглазый горец" 616 | } 617 | 618 | "Weapon_ScotsmanSkullcutter" 619 | { 620 | "ru" "Шотландский головорез" 621 | } 622 | 623 | "Weapon_UllapoolCaber" 624 | { 625 | "ru" "Аллапульское бревно" 626 | } 627 | 628 | "Weapon_ClaidheamhMor" 629 | { 630 | "ru" "Клеймор" 631 | } 632 | 633 | "Weapon_PersianPersuader" 634 | { 635 | "ru" "Персидский заклинатель" 636 | } 637 | 638 | "Weapon_Minigun" 639 | { 640 | "ru" "Пулемёт" 641 | } 642 | 643 | "Weapon_Natascha" 644 | { 645 | "ru" "Наташа" 646 | } 647 | 648 | "Weapon_BrassBeast" 649 | { 650 | "ru" "Латунный монстр" 651 | } 652 | 653 | "Weapon_Tomislav" 654 | { 655 | "ru" "Томислав" 656 | } 657 | 658 | "Weapon_HuoLongHeater" 659 | { 660 | "ru" "Огненный дракон" 661 | } 662 | 663 | "Weapon_Sandvich" 664 | { 665 | "ru" "Бутерброд" 666 | } 667 | 668 | "Weapon_DalokohsBar" 669 | { 670 | "ru" "Плитка «Далокош»" 671 | } 672 | 673 | "Weapon_BuffaloSteakSandvich" 674 | { 675 | "ru" "Бутерброд из мяса буйвола" 676 | } 677 | 678 | "Weapon_FamilyBusiness" 679 | { 680 | "ru" "Семейное дело" 681 | } 682 | 683 | "Weapon_SecondBanana" 684 | { 685 | "ru" "Утешительный банан" 686 | } 687 | 688 | "Weapon_Fists" 689 | { 690 | "ru" "Кулаки" 691 | } 692 | 693 | "Weapon_KillingGlovesofBoxing" 694 | { 695 | "ru" "Кулаки грозного боксёра" 696 | } 697 | 698 | "Weapon_GlovesofRunningUrgently" 699 | { 700 | "ru" "Горящие рукавицы ускорения" 701 | } 702 | 703 | "Weapon_WarriorSpirit" 704 | { 705 | "ru" "Воинский дух" 706 | } 707 | 708 | "Weapon_FistsofSteel" 709 | { 710 | "ru" "Стальные кулаки" 711 | } 712 | 713 | "Weapon_EvictionNotice" 714 | { 715 | "ru" "Уведомление о выселении" 716 | } 717 | 718 | "Weapon_HolidayPunch" 719 | { 720 | "ru" "Праздничный удар" 721 | } 722 | 723 | "Weapon_FrontierJustice" 724 | { 725 | "ru" "Самосуд" 726 | } 727 | 728 | "Weapon_Widowmaker" 729 | { 730 | "ru" "Овдовитель" 731 | } 732 | 733 | "Weapon_Pomson6000" 734 | { 735 | "ru" "Помсон 6000" 736 | } 737 | 738 | "Weapon_RescueRanger" 739 | { 740 | "ru" "Спасатель" 741 | } 742 | 743 | "Weapon_Wrangler" 744 | { 745 | "ru" "Поводырь" 746 | } 747 | 748 | "Weapon_ShortCircuit" 749 | { 750 | "ru" "Короткое замыкание" 751 | } 752 | 753 | "Weapon_Wrench" 754 | { 755 | "ru" "Гаечный ключ" 756 | } 757 | 758 | "Weapon_Gunslinger" 759 | { 760 | "ru" "Оружейник" 761 | } 762 | 763 | "Weapon_SouthernHospitality" 764 | { 765 | "ru" "Южное гостеприимство" 766 | } 767 | 768 | "Weapon_Jag" 769 | { 770 | "ru" "Острозуб" 771 | } 772 | 773 | "Weapon_EurekaEffect" 774 | { 775 | "ru" "Озарение" 776 | } 777 | 778 | "Weapon_SyringeGun" 779 | { 780 | "ru" "Шприцемёт" 781 | } 782 | 783 | "Weapon_Blutsauger" 784 | { 785 | "ru" "Кровопийца" 786 | } 787 | 788 | "Weapon_CrusaderCrossbow" 789 | { 790 | "ru" "Арбалет крестоносца" 791 | } 792 | 793 | "Weapon_Overdose" 794 | { 795 | "ru" "Передоз" 796 | } 797 | 798 | "Weapon_MediGun" 799 | { 800 | "ru" "Лечебная пушка" 801 | } 802 | 803 | "Weapon_Kritzkrieg" 804 | { 805 | "ru" "Крицкриг" 806 | } 807 | 808 | "Weapon_QuickFix" 809 | { 810 | "ru" "Быстроправ" 811 | } 812 | 813 | "Weapon_Vaccinator" 814 | { 815 | "ru" "Вакцинатор" 816 | } 817 | 818 | "Weapon_Bonesaw" 819 | { 820 | "ru" "Медицинская пила" 821 | } 822 | 823 | "Weapon_Ubersaw" 824 | { 825 | "ru" "Убер-пила" 826 | } 827 | 828 | "Weapon_VitaSaw" 829 | { 830 | "ru" "Вита-пила" 831 | } 832 | 833 | "Weapon_Amputator" 834 | { 835 | "ru" "Ампутатор" 836 | } 837 | 838 | "Weapon_SolemnVow" 839 | { 840 | "ru" "Священная клятва" 841 | } 842 | 843 | "Weapon_SniperRifle" 844 | { 845 | "ru" "Снайперская винтовка" 846 | } 847 | 848 | "Weapon_Huntsman" 849 | { 850 | "ru" "Охотник" 851 | } 852 | 853 | "Weapon_SydneySleeper" 854 | { 855 | "ru" "Сиднейский соня" 856 | } 857 | 858 | "Weapon_BazaarBargain" 859 | { 860 | "ru" "Базарная безделушка" 861 | } 862 | 863 | "Weapon_Machina" 864 | { 865 | "ru" "Махина" 866 | } 867 | 868 | "Weapon_HitmanHeatmaker" 869 | { 870 | "ru" "Разжигатель разбойника" 871 | } 872 | 873 | "Weapon_Classic" 874 | { 875 | "ru" "Классика" 876 | } 877 | 878 | "Weapon_SMG" 879 | { 880 | "ru" "Пистолет-пулемёт" 881 | } 882 | 883 | "Weapon_Razorback" 884 | { 885 | "ru" "Бронепанцирь" 886 | } 887 | 888 | "Weapon_Jarate" 889 | { 890 | "ru" "Банкате" 891 | } 892 | 893 | "Weapon_DarwinDangerShield" 894 | { 895 | "ru" "Дарвинистский щит" 896 | } 897 | 898 | "Weapon_CozyCamper" 899 | { 900 | "ru" "Набор для кемпинга" 901 | } 902 | 903 | "Weapon_CleanerCarbine" 904 | { 905 | "ru" "Карабин Чистильщика" 906 | } 907 | 908 | "Weapon_Kukri" 909 | { 910 | "ru" "Кукри" 911 | } 912 | 913 | "Weapon_TribalmanShiv" 914 | { 915 | "ru" "Заточка дикаря" 916 | } 917 | 918 | "Weapon_Bushwacka" 919 | { 920 | "ru" "Кустолом" 921 | } 922 | 923 | "Weapon_Shahanshah" 924 | { 925 | "ru" "Шаханшах" 926 | } 927 | 928 | "Weapon_Revolver" 929 | { 930 | "ru" "Револьвер" 931 | } 932 | 933 | "Weapon_Ambassador" 934 | { 935 | "ru" "Амбассадор" 936 | } 937 | 938 | "Weapon_LEtranger" 939 | { 940 | "ru" "Незнакомец" 941 | } 942 | 943 | "Weapon_Enforcer" 944 | { 945 | "ru" "Принудитель" 946 | } 947 | 948 | "Weapon_Diamondback" 949 | { 950 | "ru" "Алмазный змей" 951 | } 952 | 953 | "Weapon_Sapper" 954 | { 955 | "ru" "Жучок" 956 | } 957 | 958 | "Weapon_RedTapeRecorder" 959 | { 960 | "ru" "Откатофон" 961 | } 962 | 963 | "Weapon_Knife" 964 | { 965 | "ru" "Нож" 966 | } 967 | 968 | "Weapon_YourEternalReward" 969 | { 970 | "ru" "Вечный покой" 971 | } 972 | 973 | "Weapon_ConniverKunai" 974 | { 975 | "ru" "Кунай заговорщика" 976 | } 977 | 978 | "Weapon_BigEarner" 979 | { 980 | "ru" "Главный делец" 981 | } 982 | 983 | "Weapon_Spycicle" 984 | { 985 | "ru" "Сосулька" 986 | } 987 | 988 | "Weapon_ConstructionPDA" 989 | { 990 | "ru" "КПК постройки" 991 | } 992 | 993 | "Weapon_DestructionPDA" 994 | { 995 | "ru" "КПК разрушения" 996 | } 997 | 998 | "Weapon_Toolbox" 999 | { 1000 | "ru" "Набор инструментов" 1001 | } 1002 | 1003 | "Weapon_DisguiseKit" 1004 | { 1005 | "ru" "Маскировочный набор" 1006 | } 1007 | 1008 | "Weapon_InvisWatch" 1009 | { 1010 | "ru" "Часы невидимости" 1011 | } 1012 | 1013 | "Weapon_DeadRinger" 1014 | { 1015 | "ru" "Звон смерти" 1016 | } 1017 | 1018 | "Weapon_CloakandDagger" 1019 | { 1020 | "ru" "Плащ и кинжал" 1021 | } 1022 | } 1023 | 1024 | -------------------------------------------------------------------------------- /translations/randomizer.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Controls_Stickybomb_Attack2" 4 | { 5 | "en" "right click to detonate" 6 | } 7 | 8 | "Controls_Stickybomb_Reload" 9 | { 10 | "en" "reload to detonate" 11 | } 12 | 13 | "Controls_Stickybomb_Unable" 14 | { 15 | "en" "unable to detonate" 16 | } 17 | 18 | "Controls_Shield_Attack2" 19 | { 20 | "en" "right click to charge" 21 | } 22 | 23 | "Controls_Shield_Reload" 24 | { 25 | "en" "reload to charge" 26 | } 27 | 28 | "Controls_Shield_Unable" 29 | { 30 | "en" "unable to charge" 31 | } 32 | 33 | "Controls_Build_Attack2" 34 | { 35 | "en" "right click to build" 36 | } 37 | 38 | "Controls_Build_Attack3" 39 | { 40 | "en" "middle click to build" 41 | } 42 | 43 | "Controls_Build_Unable" 44 | { 45 | "en" "unable to build" 46 | } 47 | 48 | "Controls_InvisWatch_Attack2" 49 | { 50 | "en" "right click to cloak" 51 | } 52 | 53 | "Controls_InvisWatch_Attack3" 54 | { 55 | "en" "middle click to cloak" 56 | } 57 | 58 | "Controls_InvisWatch_Unable" 59 | { 60 | "en" "unable to cloak" 61 | } 62 | 63 | "Huds_Title" 64 | { 65 | "en" "Weapons" 66 | } 67 | 68 | "Huds_EffectBarRegenTime" 69 | { 70 | "#format" "{1:.0f}" 71 | "en" "{1} Sec" 72 | } 73 | 74 | "Huds_Decapitations" 75 | { 76 | "#format" "{1:d}" 77 | "en" "{1} Heads" 78 | } 79 | 80 | "Huds_Energy" 81 | { 82 | "#format" "{1:.0f}" 83 | "en" "{1}% Energy" 84 | } 85 | 86 | "Huds_RageMeter" 87 | { 88 | "#format" "{1:.0f}" 89 | "en" "{1}% Rage" 90 | } 91 | 92 | "Huds_RevengeCrits" 93 | { 94 | "#format" "{1:d}" 95 | "en" "{1} Crits" 96 | } 97 | 98 | "Huds_HypeMeter" 99 | { 100 | "#format" "{1:.0f}" 101 | "en" "{1}% Hype" 102 | } 103 | 104 | "Huds_ItemChargeMeter" 105 | { 106 | "#format" "{1:.0f}" 107 | "en" "{1}%" 108 | } 109 | 110 | "Huds_PipebombCount" 111 | { 112 | "#format" "{1:d}" 113 | "en" "{1} Stickies" 114 | } 115 | 116 | "Huds_ChargeBeginTime" 117 | { 118 | "#format" "{1:.0f}" 119 | "en" "{1}% Charge" 120 | } 121 | 122 | "Huds_ChargeMeter" 123 | { 124 | "#format" "{1:.0f}" 125 | "en" "{1}% Charge" 126 | } 127 | 128 | "Huds_Ammo" 129 | { 130 | "#format" "{1:d}" 131 | "en" "{1} Metal" 132 | } 133 | 134 | "Huds_ChargeLevel" 135 | { 136 | "#format" "{1:.0f}" 137 | "en" "{1}% Über" 138 | } 139 | 140 | "Huds_ChargeResistType_Bullet" 141 | { 142 | "en" "Type: Bullet" 143 | } 144 | 145 | "Huds_ChargeResistType_Blast" 146 | { 147 | "en" "Type: Blast" 148 | } 149 | 150 | "Huds_ChargeResistType_Fire" 151 | { 152 | "en" "Type: Fire" 153 | } 154 | 155 | "Huds_MinicritCharge" 156 | { 157 | "#format" "{1:.0f}" 158 | "en" "{1}% Crikey" 159 | } 160 | 161 | "Huds_KnifeMeltTimestamp" 162 | { 163 | "#format" "{1:.0f}" 164 | "en" "{1} sec" 165 | } 166 | 167 | "Huds_DisguiseTeam_Red" 168 | { 169 | "en" "Red" 170 | } 171 | 172 | "Huds_DisguiseTeam_Blue" 173 | { 174 | "en" "Blue" 175 | } 176 | 177 | "Huds_DisguiseClass_Scout" 178 | { 179 | "en" "Scout" 180 | } 181 | 182 | "Huds_DisguiseClass_Sniper" 183 | { 184 | "en" "Sniper" 185 | } 186 | 187 | "Huds_DisguiseClass_Soldier" 188 | { 189 | "en" "Soldier" 190 | } 191 | 192 | "Huds_DisguiseClass_Demoman" 193 | { 194 | "en" "Demoman" 195 | } 196 | 197 | "Huds_DisguiseClass_Medic" 198 | { 199 | "en" "Medic" 200 | } 201 | 202 | "Huds_DisguiseClass_Heavy" 203 | { 204 | "en" "Heavy" 205 | } 206 | 207 | "Huds_DisguiseClass_Pyro" 208 | { 209 | "en" "Pyro" 210 | } 211 | 212 | "Huds_DisguiseClass_Spy" 213 | { 214 | "en" "Spy" 215 | } 216 | 217 | "Huds_DisguiseClass_Engineer" 218 | { 219 | "en" "Engineer" 220 | } 221 | 222 | "Weapon_BASEJumper" 223 | { 224 | "en" "B.A.S.E. Jumper" 225 | } 226 | 227 | "Weapon_Shotgun" 228 | { 229 | "en" "Shotgun" 230 | } 231 | 232 | "Weapon_PanicAttack" 233 | { 234 | "en" "Panic Attack" 235 | } 236 | 237 | "Weapon_Pistol" 238 | { 239 | "en" "Pistol" 240 | } 241 | 242 | "Weapon_ReserveShooter" 243 | { 244 | "en" "Reserve Shooter" 245 | } 246 | 247 | "Weapon_PainTrain" 248 | { 249 | "en" "Pain Train" 250 | } 251 | 252 | "Weapon_HalfZatoichi" 253 | { 254 | "en" "Half-Zatoichi" 255 | } 256 | 257 | "Weapon_Scattergun" 258 | { 259 | "en" "Scattergun" 260 | } 261 | 262 | "Weapon_ForceANature" 263 | { 264 | "en" "Force-A-Nature" 265 | } 266 | 267 | "Weapon_Shortstop" 268 | { 269 | "en" "Shortstop" 270 | } 271 | 272 | "Weapon_SodaPopper" 273 | { 274 | "en" "Soda Popper" 275 | } 276 | 277 | "Weapon_BabyFaceBlaster" 278 | { 279 | "en" "Baby Face's Blaster" 280 | } 281 | 282 | "Weapon_BackScatter" 283 | { 284 | "en" "Back Scatter" 285 | } 286 | 287 | "Weapon_BonkAtomicPunch" 288 | { 289 | "en" "Bonk! Atomic Punch" 290 | } 291 | 292 | "Weapon_CritaCola" 293 | { 294 | "en" "Crit-a-Cola" 295 | } 296 | 297 | "Weapon_MadMilk" 298 | { 299 | "en" "Mad Milk" 300 | } 301 | 302 | "Weapon_Winger" 303 | { 304 | "en" "Winger" 305 | } 306 | 307 | "Weapon_PrettyBoyPocketPistol" 308 | { 309 | "en" "Pretty Boy's Pocket Pistol" 310 | } 311 | 312 | "Weapon_FlyingGuillotine" 313 | { 314 | "en" "Flying Guillotine" 315 | } 316 | 317 | "Weapon_Bat" 318 | { 319 | "en" "Bat" 320 | } 321 | 322 | "Weapon_Sandman" 323 | { 324 | "en" "Sandman" 325 | } 326 | 327 | "Weapon_HolyMackerel" 328 | { 329 | "en" "Holy Mackerel" 330 | } 331 | 332 | "Weapon_CandyCane" 333 | { 334 | "en" "Candy Cane" 335 | } 336 | 337 | "Weapon_BostonBasher" 338 | { 339 | "en" "Boston Basher" 340 | } 341 | 342 | "Weapon_SunonaStick" 343 | { 344 | "en" "Sun-on-a-Stick" 345 | } 346 | 347 | "Weapon_FanOWar" 348 | { 349 | "en" "Fan O'War" 350 | } 351 | 352 | "Weapon_Atomizer" 353 | { 354 | "en" "Atomizer" 355 | } 356 | 357 | "Weapon_WrapAssassin" 358 | { 359 | "en" "Wrap Assassin" 360 | } 361 | 362 | "Weapon_RocketLauncher" 363 | { 364 | "en" "Rocket Launcher" 365 | } 366 | 367 | "Weapon_DirectHit" 368 | { 369 | "en" "Direct Hit" 370 | } 371 | 372 | "Weapon_BlackBox" 373 | { 374 | "en" "Black Box" 375 | } 376 | 377 | "Weapon_RocketJumper" 378 | { 379 | "en" "Rocket Jumper" 380 | } 381 | 382 | "Weapon_LibertyLauncher" 383 | { 384 | "en" "Liberty Launcher" 385 | } 386 | 387 | "Weapon_CowMangler5000" 388 | { 389 | "en" "Cow Mangler 5000" 390 | } 391 | 392 | "Weapon_Original" 393 | { 394 | "en" "Original" 395 | } 396 | 397 | "Weapon_BeggarBazooka" 398 | { 399 | "en" "Beggar's Bazooka" 400 | } 401 | 402 | "Weapon_AirStrike" 403 | { 404 | "en" "Air Strike" 405 | } 406 | 407 | "Weapon_BuffBanner" 408 | { 409 | "en" "Buff Banner" 410 | } 411 | 412 | "Weapon_Gunboats" 413 | { 414 | "en" "Gunboats" 415 | } 416 | 417 | "Weapon_BattalionBackup" 418 | { 419 | "en" "Battalion's Backup" 420 | } 421 | 422 | "Weapon_Concheror" 423 | { 424 | "en" "Concheror" 425 | } 426 | 427 | "Weapon_RighteousBison" 428 | { 429 | "en" "Righteous Bison" 430 | } 431 | 432 | "Weapon_Mantreads" 433 | { 434 | "en" "Mantreads" 435 | } 436 | 437 | "Weapon_Shovel" 438 | { 439 | "en" "Shovel" 440 | } 441 | 442 | "Weapon_Equalizer" 443 | { 444 | "en" "Equalizer" 445 | } 446 | 447 | "Weapon_MarketGardener" 448 | { 449 | "en" "Market Gardener" 450 | } 451 | 452 | "Weapon_DisciplinaryAction" 453 | { 454 | "en" "Disciplinary Action" 455 | } 456 | 457 | "Weapon_EscapePlan" 458 | { 459 | "en" "Escape Plan" 460 | } 461 | 462 | "Weapon_FlameThrower" 463 | { 464 | "en" "Flame Thrower" 465 | } 466 | 467 | "Weapon_Backburner" 468 | { 469 | "en" "Backburner" 470 | } 471 | 472 | "Weapon_Degreaser" 473 | { 474 | "en" "Degreaser" 475 | } 476 | 477 | "Weapon_Phlogistinator" 478 | { 479 | "en" "Phlogistinator" 480 | } 481 | 482 | "Weapon_DragonFury" 483 | { 484 | "en" "Dragon's Fury" 485 | } 486 | 487 | "Weapon_FlareGun" 488 | { 489 | "en" "Flare Gun" 490 | } 491 | 492 | "Weapon_Detonator" 493 | { 494 | "en" "Detonator" 495 | } 496 | 497 | "Weapon_Manmelter" 498 | { 499 | "en" "Manmelter" 500 | } 501 | 502 | "Weapon_ScorchShot" 503 | { 504 | "en" "Scorch Shot" 505 | } 506 | 507 | "Weapon_ThermalThruster" 508 | { 509 | "en" "Thermal Thruster" 510 | } 511 | 512 | "Weapon_GasPasser" 513 | { 514 | "en" "Gas Passer" 515 | } 516 | 517 | "Weapon_FireAxe" 518 | { 519 | "en" "Fire Axe" 520 | } 521 | 522 | "Weapon_Axtinguisher" 523 | { 524 | "en" "Axtinguisher" 525 | } 526 | 527 | "Weapon_Homewrecker" 528 | { 529 | "en" "Homewrecker" 530 | } 531 | 532 | "Weapon_Powerjack" 533 | { 534 | "en" "Powerjack" 535 | } 536 | 537 | "Weapon_BackScratcher" 538 | { 539 | "en" "Back Scratcher" 540 | } 541 | 542 | "Weapon_SharpenedVolcanoFragment" 543 | { 544 | "en" "Sharpened Volcano Fragment" 545 | } 546 | 547 | "Weapon_ThirdDegree" 548 | { 549 | "en" "Third Degree" 550 | } 551 | 552 | "Weapon_NeonAnnihilator" 553 | { 554 | "en" "Neon Annihilator" 555 | } 556 | 557 | "Weapon_HotHand" 558 | { 559 | "en" "Hot Hand" 560 | } 561 | 562 | "Weapon_GrenadeLauncher" 563 | { 564 | "en" "Grenade Launcher" 565 | } 566 | 567 | "Weapon_LochnLoad" 568 | { 569 | "en" "Loch-n-Load" 570 | } 571 | 572 | "Weapon_AliBabaWeeBooties" 573 | { 574 | "en" "Ali Baba's Wee Booties" 575 | } 576 | 577 | "Weapon_LooseCannon" 578 | { 579 | "en" "Loose Cannon" 580 | } 581 | 582 | "Weapon_IronBomber" 583 | { 584 | "en" "Iron Bomber" 585 | } 586 | 587 | "Weapon_StickybombLauncher" 588 | { 589 | "en" "Stickybomb Launcher" 590 | } 591 | 592 | "Weapon_ScottishResistance" 593 | { 594 | "en" "Scottish Resistance" 595 | } 596 | 597 | "Weapon_CharginTarge" 598 | { 599 | "en" "Chargin' Targe" 600 | } 601 | 602 | "Weapon_StickyJumper" 603 | { 604 | "en" "Sticky Jumper" 605 | } 606 | 607 | "Weapon_SplendidScreen" 608 | { 609 | "en" "Splendid Screen" 610 | } 611 | 612 | "Weapon_TideTurner" 613 | { 614 | "en" "Tide Turner" 615 | } 616 | 617 | "Weapon_QuickiebombLauncher" 618 | { 619 | "en" "Quickiebomb Launcher" 620 | } 621 | 622 | "Weapon_Bottle" 623 | { 624 | "en" "Bottle" 625 | } 626 | 627 | "Weapon_Eyelander" 628 | { 629 | "en" "Eyelander" 630 | } 631 | 632 | "Weapon_ScotsmanSkullcutter" 633 | { 634 | "en" "Scotsman's Skullcutter" 635 | } 636 | 637 | "Weapon_UllapoolCaber" 638 | { 639 | "en" "Ullapool Caber" 640 | } 641 | 642 | "Weapon_ClaidheamhMor" 643 | { 644 | "en" "Claidheamh Mòr" 645 | } 646 | 647 | "Weapon_PersianPersuader" 648 | { 649 | "en" "Persian Persuader" 650 | } 651 | 652 | "Weapon_Minigun" 653 | { 654 | "en" "Minigun" 655 | } 656 | 657 | "Weapon_Natascha" 658 | { 659 | "en" "Natascha" 660 | } 661 | 662 | "Weapon_BrassBeast" 663 | { 664 | "en" "Brass Beast" 665 | } 666 | 667 | "Weapon_Tomislav" 668 | { 669 | "en" "Tomislav" 670 | } 671 | 672 | "Weapon_HuoLongHeater" 673 | { 674 | "en" "Huo-Long Heater" 675 | } 676 | 677 | "Weapon_Sandvich" 678 | { 679 | "en" "Sandvich" 680 | } 681 | 682 | "Weapon_DalokohsBar" 683 | { 684 | "en" "Dalokohs Bar" 685 | } 686 | 687 | "Weapon_BuffaloSteakSandvich" 688 | { 689 | "en" "Buffalo Steak Sandvich" 690 | } 691 | 692 | "Weapon_FamilyBusiness" 693 | { 694 | "en" "Family Business" 695 | } 696 | 697 | "Weapon_SecondBanana" 698 | { 699 | "en" "Second Banana" 700 | } 701 | 702 | "Weapon_Fists" 703 | { 704 | "en" "Fists" 705 | } 706 | 707 | "Weapon_KillingGlovesofBoxing" 708 | { 709 | "en" "Killing Gloves of Boxing" 710 | } 711 | 712 | "Weapon_GlovesofRunningUrgently" 713 | { 714 | "en" "Gloves of Running Urgently" 715 | } 716 | 717 | "Weapon_WarriorSpirit" 718 | { 719 | "en" "Warrior's Spirit" 720 | } 721 | 722 | "Weapon_FistsofSteel" 723 | { 724 | "en" "Fists of Steel" 725 | } 726 | 727 | "Weapon_EvictionNotice" 728 | { 729 | "en" "Eviction Notice" 730 | } 731 | 732 | "Weapon_HolidayPunch" 733 | { 734 | "en" "Holiday Punch" 735 | } 736 | 737 | "Weapon_FrontierJustice" 738 | { 739 | "en" "Frontier Justice" 740 | } 741 | 742 | "Weapon_Widowmaker" 743 | { 744 | "en" "Widowmaker" 745 | } 746 | 747 | "Weapon_Pomson6000" 748 | { 749 | "en" "Pomson 6000" 750 | } 751 | 752 | "Weapon_RescueRanger" 753 | { 754 | "en" "Rescue Ranger" 755 | } 756 | 757 | "Weapon_Wrangler" 758 | { 759 | "en" "Wrangler" 760 | } 761 | 762 | "Weapon_ShortCircuit" 763 | { 764 | "en" "Short Circuit" 765 | } 766 | 767 | "Weapon_Wrench" 768 | { 769 | "en" "Wrench" 770 | } 771 | 772 | "Weapon_Gunslinger" 773 | { 774 | "en" "Gunslinger" 775 | } 776 | 777 | "Weapon_SouthernHospitality" 778 | { 779 | "en" "Southern Hospitality" 780 | } 781 | 782 | "Weapon_Jag" 783 | { 784 | "en" "Jag" 785 | } 786 | 787 | "Weapon_EurekaEffect" 788 | { 789 | "en" "Eureka Effect" 790 | } 791 | 792 | "Weapon_SyringeGun" 793 | { 794 | "en" "Syringe Gun" 795 | } 796 | 797 | "Weapon_Blutsauger" 798 | { 799 | "en" "Blutsauger" 800 | } 801 | 802 | "Weapon_CrusaderCrossbow" 803 | { 804 | "en" "Crusader's Crossbow" 805 | } 806 | 807 | "Weapon_Overdose" 808 | { 809 | "en" "Overdose" 810 | } 811 | 812 | "Weapon_MediGun" 813 | { 814 | "en" "Medi Gun" 815 | } 816 | 817 | "Weapon_Kritzkrieg" 818 | { 819 | "en" "Kritzkrieg" 820 | } 821 | 822 | "Weapon_QuickFix" 823 | { 824 | "en" "Quick-Fix" 825 | } 826 | 827 | "Weapon_Vaccinator" 828 | { 829 | "en" "Vaccinator" 830 | } 831 | 832 | "Weapon_Bonesaw" 833 | { 834 | "en" "Bonesaw" 835 | } 836 | 837 | "Weapon_Ubersaw" 838 | { 839 | "en" "Übersaw" 840 | } 841 | 842 | "Weapon_VitaSaw" 843 | { 844 | "en" "Vita-Saw" 845 | } 846 | 847 | "Weapon_Amputator" 848 | { 849 | "en" "Amputator" 850 | } 851 | 852 | "Weapon_SolemnVow" 853 | { 854 | "en" "Solemn Vow" 855 | } 856 | 857 | "Weapon_SniperRifle" 858 | { 859 | "en" "Sniper Rifle" 860 | } 861 | 862 | "Weapon_Huntsman" 863 | { 864 | "en" "Huntsman" 865 | } 866 | 867 | "Weapon_SydneySleeper" 868 | { 869 | "en" "Sydney Sleeper" 870 | } 871 | 872 | "Weapon_BazaarBargain" 873 | { 874 | "en" "Bazaar Bargain" 875 | } 876 | 877 | "Weapon_Machina" 878 | { 879 | "en" "Machina" 880 | } 881 | 882 | "Weapon_HitmanHeatmaker" 883 | { 884 | "en" "Hitman's Heatmaker" 885 | } 886 | 887 | "Weapon_Classic" 888 | { 889 | "en" "Classic" 890 | } 891 | 892 | "Weapon_SMG" 893 | { 894 | "en" "SMG" 895 | } 896 | 897 | "Weapon_Razorback" 898 | { 899 | "en" "Razorback" 900 | } 901 | 902 | "Weapon_Jarate" 903 | { 904 | "en" "Jarate" 905 | } 906 | 907 | "Weapon_DarwinDangerShield" 908 | { 909 | "en" "Darwin's Danger Shield" 910 | } 911 | 912 | "Weapon_CozyCamper" 913 | { 914 | "en" "Cozy Camper" 915 | } 916 | 917 | "Weapon_CleanerCarbine" 918 | { 919 | "en" "Cleaner's Carbine" 920 | } 921 | 922 | "Weapon_Kukri" 923 | { 924 | "en" "Kukri" 925 | } 926 | 927 | "Weapon_TribalmanShiv" 928 | { 929 | "en" "Tribalman's Shiv" 930 | } 931 | 932 | "Weapon_Bushwacka" 933 | { 934 | "en" "Bushwacka" 935 | } 936 | 937 | "Weapon_Shahanshah" 938 | { 939 | "en" "Shahanshah" 940 | } 941 | 942 | "Weapon_Revolver" 943 | { 944 | "en" "Revolver" 945 | } 946 | 947 | "Weapon_Ambassador" 948 | { 949 | "en" "Ambassador" 950 | } 951 | 952 | "Weapon_LEtranger" 953 | { 954 | "en" "L'Etranger" 955 | } 956 | 957 | "Weapon_Enforcer" 958 | { 959 | "en" "Enforcer" 960 | } 961 | 962 | "Weapon_Diamondback" 963 | { 964 | "en" "Diamondback" 965 | } 966 | 967 | "Weapon_Sapper" 968 | { 969 | "en" "Sapper" 970 | } 971 | 972 | "Weapon_RedTapeRecorder" 973 | { 974 | "en" "Red-Tape Recorder" 975 | } 976 | 977 | "Weapon_Knife" 978 | { 979 | "en" "Knife" 980 | } 981 | 982 | "Weapon_YourEternalReward" 983 | { 984 | "en" "Your Eternal Reward" 985 | } 986 | 987 | "Weapon_ConniverKunai" 988 | { 989 | "en" "Conniver's Kunai" 990 | } 991 | 992 | "Weapon_BigEarner" 993 | { 994 | "en" "Big Earner" 995 | } 996 | 997 | "Weapon_Spycicle" 998 | { 999 | "en" "Spy-cicle" 1000 | } 1001 | 1002 | "Weapon_ConstructionPDA" 1003 | { 1004 | "en" "Construction PDA" 1005 | } 1006 | 1007 | "Weapon_DestructionPDA" 1008 | { 1009 | "en" "Destruction PDA" 1010 | } 1011 | 1012 | "Weapon_Toolbox" 1013 | { 1014 | "en" "Toolbox" 1015 | } 1016 | 1017 | "Weapon_DisguiseKit" 1018 | { 1019 | "en" "Disguise Kit" 1020 | } 1021 | 1022 | "Weapon_InvisWatch" 1023 | { 1024 | "en" "Invis Watch" 1025 | } 1026 | 1027 | "Weapon_DeadRinger" 1028 | { 1029 | "en" "Dead Ringer" 1030 | } 1031 | 1032 | "Weapon_CloakandDagger" 1033 | { 1034 | "en" "Cloak and Dagger" 1035 | } 1036 | } 1037 | 1038 | -------------------------------------------------------------------------------- /scripting/randomizer/huds.sp: -------------------------------------------------------------------------------- 1 | #define FILEPATH_CONFIG_HUDS "configs/randomizer/huds.cfg" 2 | 3 | #define MENU_MAX_ITEM_SINGLE 9 //Can display 9 items in single page 4 | #define MENU_MAX_ITEM_MULTIPLE 7 //Can display 7 items for each page 5 | 6 | enum HudEntity 7 | { 8 | HudEntity_Weapon, 9 | HudEntity_Client, 10 | HudEntity_ClientSeperate, 11 | } 12 | 13 | enum HudType 14 | { 15 | HudType_Int, 16 | HudType_Float, 17 | HudType_Time, 18 | } 19 | 20 | enum struct HudInfo 21 | { 22 | WeaponWhitelist weaponWhitelist; //Weapons that uses this netprop 23 | char sNetprop[32]; //Netprop to get value 24 | int iElement; //If netprop is array, element postition to get 25 | HudEntity nEntity; //Entity to target from netprop 26 | HudType nType; //How is netprop value stored 27 | float flAdd; //Add netprop value to display 28 | float flMultiply; //Multiply netprop value to display 29 | bool bMin; //Is there netprop min value? 30 | float flMin; //Netprop min value inorder to display 31 | bool bMax; //Is there netprop max value? 32 | float flMax; //Netprop max value inorder to display 33 | char sText[64]; //Text to display next to value 34 | StringMap mText; //If not null, use this instead of sText to get text depending on netprop value 35 | 36 | bool CalculateIntValue(int &iVal) 37 | { 38 | iVal = RoundToNearest(float(iVal) * this.flMultiply); 39 | iVal += RoundToNearest(this.flAdd); 40 | 41 | if (this.bMin && float(iVal) <= this.flMin) 42 | return false; 43 | 44 | if (this.bMax && float(iVal) >= this.flMax) 45 | return false; 46 | 47 | return true; 48 | } 49 | 50 | bool CalculateFloatValue(float &flVal) 51 | { 52 | flVal *= this.flMultiply; 53 | flVal += this.flAdd; 54 | 55 | if (this.bMin && flVal <= this.flMin) 56 | return false; 57 | 58 | if (this.bMax && flVal >= this.flMax) 59 | return false; 60 | 61 | return true; 62 | } 63 | 64 | bool GetDynamicText(int iVal, char[] sBuffer, int iLength) 65 | { 66 | if (!this.mText) 67 | return false; 68 | 69 | char sVal[16]; 70 | IntToString(iVal, sVal, sizeof(sVal)); 71 | return this.mText.GetString(sVal, sBuffer, iLength); 72 | } 73 | } 74 | 75 | enum struct HudWeapon 76 | { 77 | int iRef; //Weapon ref 78 | char sName[64]; //Translated name of weapon to display 79 | ArrayList aHudInfo; //Arrays of HudInfo to display 80 | 81 | void GetText(int iClient, char[] sDisplay, int iLength) 82 | { 83 | int iWeapon = EntRefToEntIndex(this.iRef); 84 | strcopy(sDisplay, iLength, this.sName); 85 | 86 | //Go through every netprops to display 87 | if (this.aHudInfo) 88 | { 89 | int iHudLength = this.aHudInfo.Length; 90 | for (int j = 0; j < iHudLength; j++) 91 | { 92 | HudInfo hudInfo; 93 | this.aHudInfo.GetArray(j, hudInfo); 94 | 95 | switch (hudInfo.nType) 96 | { 97 | case HudType_Int: 98 | { 99 | int iVal; 100 | 101 | switch (hudInfo.nEntity) 102 | { 103 | case HudEntity_Weapon: iVal = GetEntProp(iWeapon, Prop_Send, hudInfo.sNetprop, _, hudInfo.iElement); 104 | case HudEntity_Client: iVal = GetEntProp(iClient, Prop_Send, hudInfo.sNetprop, _, hudInfo.iElement); 105 | case HudEntity_ClientSeperate: iVal = Properties_GetWeaponPropInt(iWeapon, hudInfo.sNetprop); 106 | 107 | } 108 | 109 | if (hudInfo.CalculateIntValue(iVal)) 110 | { 111 | char sText[64]; 112 | if (hudInfo.GetDynamicText(iVal, sText, sizeof(sText))) 113 | Format(sDisplay, iLength, "%s: %T", sDisplay, sText, iClient); 114 | else 115 | Format(sDisplay, iLength, "%s: %T", sDisplay, hudInfo.sText, iClient, iVal); 116 | } 117 | } 118 | case HudType_Float, HudType_Time: 119 | { 120 | float flVal; 121 | 122 | switch (hudInfo.nEntity) 123 | { 124 | case HudEntity_Weapon: flVal = GetEntPropFloat(iWeapon, Prop_Send, hudInfo.sNetprop, hudInfo.iElement); 125 | case HudEntity_Client: flVal = GetEntPropFloat(iClient, Prop_Send, hudInfo.sNetprop, hudInfo.iElement); 126 | case HudEntity_ClientSeperate: flVal = Properties_GetWeaponPropFloat(iWeapon, hudInfo.sNetprop); 127 | } 128 | 129 | if (hudInfo.nType == HudType_Time) 130 | flVal -= GetGameTime(); 131 | 132 | if (hudInfo.CalculateFloatValue(flVal)) 133 | Format(sDisplay, iLength, "%s: %T", sDisplay, hudInfo.sText, iClient, flVal); 134 | } 135 | } 136 | } 137 | 138 | char sBuffer[64]; 139 | if (Controls_GetPassiveInfo(iClient, iWeapon, sBuffer, sizeof(sBuffer))) 140 | Format(sDisplay, iLength, "%s (%s)", sDisplay, sBuffer); 141 | } 142 | } 143 | } 144 | 145 | static ArrayList g_aHuds; //Arrays of HudInfo 146 | static ArrayList g_aHudWeapon[MAXPLAYERS + 1]; //Arrays of HudWeapon 147 | static Menu g_iHudClientMenu[MAXPLAYERS + 1]; 148 | static int g_iHudClientPage[MAXPLAYERS + 1]; //Current page client is at 149 | 150 | void Huds_Init() 151 | { 152 | g_aHuds = new ArrayList(sizeof(HudInfo)); 153 | 154 | for (int iClient = 0; iClient < sizeof(g_aHudWeapon); iClient++) 155 | g_aHudWeapon[iClient] = new ArrayList(sizeof(HudWeapon)); 156 | } 157 | 158 | void Huds_Refresh() 159 | { 160 | KeyValues kv = LoadConfig(FILEPATH_CONFIG_HUDS, "Huds"); 161 | if (!kv) 162 | return; 163 | 164 | //Clear handles 165 | int iLength = g_aHuds.Length; 166 | for (int i = 0; i < iLength; i++) 167 | { 168 | HudInfo hudInfo; 169 | g_aHuds.GetArray(i, hudInfo); 170 | hudInfo.weaponWhitelist.Delete(); 171 | delete hudInfo.mText; 172 | } 173 | 174 | g_aHuds.Clear(); 175 | 176 | if (kv.GotoFirstSubKey(false)) //netprop name 177 | { 178 | do 179 | { 180 | char sBuffer[256]; 181 | HudInfo hudInfo; 182 | kv.GetSectionName(hudInfo.sNetprop, sizeof(hudInfo.sNetprop)); 183 | hudInfo.iElement = kv.GetNum("element", 0); 184 | hudInfo.flAdd = kv.GetFloat("add", 0.0); 185 | hudInfo.flMultiply = kv.GetFloat("multiply", 1.0); 186 | 187 | kv.GetString("entity", sBuffer, sizeof(sBuffer)); 188 | if (StrEqual(sBuffer, "weapon")) 189 | { 190 | hudInfo.nEntity = HudEntity_Weapon; 191 | } 192 | else if (StrEqual(sBuffer, "client")) 193 | { 194 | hudInfo.nEntity = HudEntity_Client; 195 | } 196 | else if (StrEqual(sBuffer, "client-seperate")) 197 | { 198 | hudInfo.nEntity = HudEntity_ClientSeperate; 199 | } 200 | else 201 | { 202 | LogError("Invalid entity type at %s: %s", hudInfo.sNetprop, sBuffer); 203 | continue; 204 | } 205 | 206 | kv.GetString("type", sBuffer, sizeof(sBuffer)); 207 | if (StrEqual(sBuffer, "int")) 208 | { 209 | hudInfo.nType = HudType_Int; 210 | } 211 | else if (StrEqual(sBuffer, "float")) 212 | { 213 | hudInfo.nType = HudType_Float; 214 | } 215 | else if (StrEqual(sBuffer, "time")) 216 | { 217 | hudInfo.nType = HudType_Time; 218 | } 219 | else 220 | { 221 | LogError("Invalid value type at %s: %s", hudInfo.sNetprop, sBuffer); 222 | continue; 223 | } 224 | 225 | kv.GetString("min", sBuffer, sizeof(sBuffer), ""); 226 | if (sBuffer[0] != '\0') 227 | { 228 | hudInfo.bMin = true; 229 | hudInfo.flMin = StringToFloat(sBuffer); 230 | } 231 | 232 | kv.GetString("max", sBuffer, sizeof(sBuffer), ""); 233 | if (sBuffer[0] != '\0') 234 | { 235 | hudInfo.bMax = true; 236 | hudInfo.flMax = StringToFloat(sBuffer); 237 | } 238 | 239 | if (kv.JumpToKey("text", false)) 240 | { 241 | if (kv.GotoFirstSubKey(false)) 242 | { 243 | hudInfo.mText = new StringMap(); 244 | 245 | do 246 | { 247 | char sValue[CONFIG_MAXCHAR], sText[CONFIG_MAXCHAR]; 248 | kv.GetSectionName(sValue, sizeof(sValue)); 249 | kv.GetString(NULL_STRING, sText, sizeof(sText)); 250 | hudInfo.mText.SetString(sValue, sText); 251 | } 252 | while (kv.GotoNextKey(false)); 253 | kv.GoBack(); 254 | } 255 | else 256 | { 257 | kv.GetString(NULL_STRING, hudInfo.sText, sizeof(hudInfo.sText)); 258 | } 259 | 260 | kv.GoBack(); 261 | } 262 | 263 | hudInfo.weaponWhitelist.Load(kv, "weapon"); 264 | 265 | //Push all of the stuffs to ArrayList 266 | g_aHuds.PushArray(hudInfo); 267 | } 268 | while (kv.GotoNextKey(false)); 269 | kv.GoBack(); 270 | } 271 | } 272 | 273 | void Huds_RefreshClient(int iClient) 274 | { 275 | int iLength = g_aHudWeapon[iClient].Length; 276 | for (int i = 0; i < iLength; i++) 277 | delete view_as(g_aHudWeapon[iClient].Get(i, HudWeapon::aHudInfo)); 278 | 279 | g_aHudWeapon[iClient].Clear(); 280 | g_iHudClientPage[iClient] = 0; //Restart current page 281 | 282 | if (g_cvHuds.IntValue == HudMode_None) //ConVar dont want us to do anything 283 | return; 284 | 285 | int iWeapon; 286 | int iPos; 287 | while (TF2_GetItem(iClient, iWeapon, iPos)) 288 | { 289 | if (TF2_GetSlot(iWeapon) >= WeaponSlot_Building) //Ignore toolbox 290 | continue; 291 | 292 | HudWeapon hudWeapon; 293 | hudWeapon.iRef = EntIndexToEntRef(iWeapon); 294 | 295 | int iIndex = Weapons_GetReskinIndex(GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex")); 296 | if (!Weapons_GetName(iIndex, hudWeapon.sName, sizeof(hudWeapon.sName))) 297 | continue; //This weapon is probably a special one and not from randomizer, ignore 298 | 299 | //Translate weapon name 300 | Format(hudWeapon.sName, sizeof(hudWeapon.sName), "%T", hudWeapon.sName, iClient); 301 | 302 | //Go through every HudInfo to see whenever which weapon can use 303 | int iHudLength = g_aHuds.Length; 304 | for (int i = 0; i < iHudLength; i++) 305 | { 306 | HudInfo hudInfo; 307 | g_aHuds.GetArray(i, hudInfo); 308 | 309 | if (hudInfo.nEntity == HudEntity_Weapon && !HasEntProp(iWeapon, Prop_Send, hudInfo.sNetprop)) 310 | continue; 311 | 312 | if (!hudInfo.weaponWhitelist.IsEmpty() && !hudInfo.weaponWhitelist.IsIndexAllowed(iIndex)) 313 | continue; 314 | 315 | if (!hudWeapon.aHudInfo) 316 | hudWeapon.aHudInfo = new ArrayList(sizeof(HudInfo)); 317 | 318 | hudWeapon.aHudInfo.PushArray(hudInfo); 319 | } 320 | 321 | g_aHudWeapon[iClient].PushArray(hudWeapon); 322 | } 323 | 324 | g_aHudWeapon[iClient].SortCustom(Huds_SortWeapons); 325 | } 326 | 327 | public int Huds_SortWeapons(int iPos1, int iPos2, Handle hMap, Handle hHandle) 328 | { 329 | HudWeapon hudWeapon1, hudWeapon2; 330 | GetArrayArray(hMap, iPos1, hudWeapon1); //Callback using legacy handle reeee 331 | GetArrayArray(hMap, iPos2, hudWeapon2); 332 | 333 | //Sort by slot position 334 | int iSlot1 = TF2_GetSlot(hudWeapon1.iRef); 335 | int iSlot2 = TF2_GetSlot(hudWeapon2.iRef); 336 | if (iSlot1 < iSlot2) 337 | return -1; 338 | else if (iSlot1 > iSlot2) 339 | return 1; 340 | 341 | //Sort by wearable 342 | bool bWearable1 = TF2_IsWearable(hudWeapon1.iRef); 343 | bool bWearable2 = TF2_IsWearable(hudWeapon2.iRef); 344 | if (!bWearable1 && bWearable2) 345 | return -1; 346 | else if (bWearable1 && !bWearable2) 347 | return 1; 348 | 349 | if (!bWearable1 && !bWearable2) 350 | { 351 | //If both not wearable, sort by ammo type 352 | int iAmmoType1 = GetEntProp(hudWeapon1.iRef, Prop_Send, "m_iPrimaryAmmoType"); 353 | int iAmmoType2 = GetEntProp(hudWeapon2.iRef, Prop_Send, "m_iPrimaryAmmoType"); 354 | if (iAmmoType1 == -1 && iAmmoType2 != -1) 355 | return 1; 356 | else if (iAmmoType1 != -1 && iAmmoType2 == -1) 357 | return -1; 358 | else if (iAmmoType1 < iAmmoType2) 359 | return -1; 360 | else if (iAmmoType1 > iAmmoType2) 361 | return 1; 362 | } 363 | 364 | //Sort by def index 365 | int iIndex1 = Weapons_GetReskinIndex(GetEntProp(hudWeapon1.iRef, Prop_Send, "m_iItemDefinitionIndex")); 366 | int iIndex2 = Weapons_GetReskinIndex(GetEntProp(hudWeapon2.iRef, Prop_Send, "m_iItemDefinitionIndex")); 367 | if (iIndex1 < iIndex2) 368 | return -1; 369 | else if (iIndex1 > iIndex2) 370 | return 1; 371 | 372 | //Literally exact same weapon bro 373 | return hudWeapon1.iRef < hudWeapon2.iRef ? -1 : 1; 374 | } 375 | 376 | public Action Huds_ClientDisplay(Handle hTimer, int iClient) 377 | { 378 | if (g_hTimerClientHud[iClient] != hTimer || !IsClientInGame(iClient)) 379 | return Plugin_Continue; 380 | 381 | //Remove any deleted weapons 382 | int iLength = g_aHudWeapon[iClient].Length; 383 | for (int i = iLength - 1; i >= 0 ; i--) 384 | { 385 | HudWeapon hudWeapon; 386 | g_aHudWeapon[iClient].GetArray(i, hudWeapon); 387 | if (!IsValidEntity(hudWeapon.iRef)) 388 | g_aHudWeapon[iClient].Erase(i); 389 | } 390 | 391 | switch (g_cvHuds.IntValue) 392 | { 393 | case HudMode_Text: Huds_ClientDisplayText(iClient); 394 | case HudMode_Menu: Huds_ClientDisplayMenu(iClient); 395 | } 396 | 397 | return Plugin_Continue; 398 | } 399 | 400 | void Huds_ClientDisplayText(int iClient) 401 | { 402 | g_hTimerClientHud[iClient] = CreateTimer(0.2, Huds_ClientDisplay, iClient); 403 | 404 | if (!IsPlayerAlive(iClient)) 405 | return; 406 | 407 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 408 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 409 | iActiveWeapon = EntIndexToEntRef(iActiveWeapon); 410 | 411 | char sDisplay[512]; 412 | 413 | int iLength = g_aHudWeapon[iClient].Length; 414 | for (int i = 0; i < iLength; i++) 415 | { 416 | HudWeapon hudWeapon; 417 | g_aHudWeapon[iClient].GetArray(i, hudWeapon); 418 | 419 | char sBuffer[256]; 420 | hudWeapon.GetText(iClient, sBuffer, sizeof(sBuffer)); 421 | 422 | if (iActiveWeapon == hudWeapon.iRef) 423 | Format(sBuffer, sizeof(sBuffer), "> %s", sBuffer); 424 | 425 | if (sDisplay[0]) 426 | Format(sDisplay, sizeof(sDisplay), "%s\n%s", sDisplay, sBuffer); 427 | else 428 | strcopy(sDisplay, sizeof(sDisplay), sBuffer); 429 | } 430 | 431 | SetHudTextParams(0.28, 1.0, 0.5, 255, 255, 255, 255); 432 | ShowHudText(iClient, 0, sDisplay); 433 | } 434 | 435 | void Huds_ClientDisplayMenu(int iClient) 436 | { 437 | g_hTimerClientHud[iClient] = CreateTimer(1.0, Huds_ClientDisplay, iClient); 438 | 439 | if (!IsPlayerAlive(iClient)) 440 | return; 441 | 442 | if (GetClientMenu(iClient) != MenuSource_None && !g_iHudClientMenu[iClient]) 443 | return; //Client currently have menu opened thats not this, don't disturb active menu 444 | 445 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 446 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 447 | iActiveWeapon = EntIndexToEntRef(iActiveWeapon); 448 | 449 | Menu hMenu = new Menu(Huds_MenuAction); 450 | 451 | char sTitle[64]; 452 | Format(sTitle, sizeof(sTitle), "%T\n ", "Huds_Title", iClient); 453 | hMenu.SetTitle(sTitle); 454 | 455 | int iCount; 456 | 457 | bool bPrevious, bNext; 458 | int iLength = g_aHudWeapon[iClient].Length; 459 | int iStart = 0; 460 | int iEnd = iLength; 461 | int iMaxDisplay = MENU_MAX_ITEM_SINGLE; 462 | 463 | if (iLength > MENU_MAX_ITEM_SINGLE) 464 | { 465 | //Too many weapons to fit in one page, work through which page to display 466 | 467 | iStart = g_iHudClientPage[iClient] * MENU_MAX_ITEM_MULTIPLE; 468 | iMaxDisplay = MENU_MAX_ITEM_MULTIPLE; 469 | 470 | if (g_iHudClientPage[iClient] > 0) 471 | bPrevious = true; 472 | 473 | if ((g_iHudClientPage[iClient] + 1) * MENU_MAX_ITEM_MULTIPLE < iLength) 474 | { 475 | bNext = true; 476 | iEnd = iStart + MENU_MAX_ITEM_MULTIPLE; 477 | } 478 | } 479 | 480 | for (int i = iStart; i < iEnd; i++) 481 | { 482 | HudWeapon hudWeapon; 483 | g_aHudWeapon[iClient].GetArray(i, hudWeapon); 484 | 485 | char sBuffer[256]; 486 | hudWeapon.GetText(iClient, sBuffer, sizeof(sBuffer)); 487 | 488 | if (iActiveWeapon == hudWeapon.iRef) //Active weapon 489 | Format(sBuffer, sizeof(sBuffer), "> %s", sBuffer); 490 | 491 | if (iMaxDisplay == MENU_MAX_ITEM_MULTIPLE && i + 1 == iEnd) //Add spaces above previous & next button 492 | StrCat(sBuffer, sizeof(sBuffer), "\n "); 493 | 494 | char sValue[16]; 495 | IntToString(hudWeapon.iRef, sValue, sizeof(sValue)); 496 | bool bSwitch = iActiveWeapon == hudWeapon.iRef || TF2_CanSwitchTo(iClient, hudWeapon.iRef); 497 | hMenu.AddItem(sValue, sBuffer, bSwitch ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); 498 | iCount++; 499 | } 500 | 501 | //If multi-page, fill remaining items as nothing for previous & next button 502 | if (iLength > MENU_MAX_ITEM_SINGLE) 503 | for (int i = iEnd - iStart; i < iMaxDisplay; i++) 504 | hMenu.AddItem("", "", ITEMDRAW_NOTEXT|ITEMDRAW_SPACER); 505 | 506 | if (bPrevious) 507 | { 508 | char sPrevious[64]; 509 | Format(sPrevious, sizeof(sPrevious), "%T", "Previous", iClient); 510 | hMenu.AddItem("previous", sPrevious); 511 | } 512 | else if (iLength > MENU_MAX_ITEM_SINGLE) 513 | { 514 | hMenu.AddItem("", "", ITEMDRAW_NOTEXT|ITEMDRAW_SPACER); 515 | } 516 | 517 | if (bNext) 518 | { 519 | char sNext[64]; 520 | Format(sNext, sizeof(sNext), "%T", "Next", iClient); 521 | hMenu.AddItem("next", sNext); 522 | } 523 | else if (iLength > MENU_MAX_ITEM_SINGLE) 524 | { 525 | hMenu.AddItem("", "", ITEMDRAW_NOTEXT|ITEMDRAW_SPACER); 526 | } 527 | 528 | hMenu.Pagination = MENU_NO_PAGINATION; 529 | hMenu.ExitButton = false; 530 | hMenu.OptionFlags |= MENUFLAG_NO_SOUND; 531 | hMenu.Display(iClient, 5); 532 | g_iHudClientMenu[iClient] = hMenu; 533 | } 534 | 535 | public int Huds_MenuAction(Menu hMenu, MenuAction action, int iClient, int iChoice) 536 | { 537 | switch (action) 538 | { 539 | case MenuAction_End: 540 | { 541 | for (int i = 1; i <= MaxClients; i++) 542 | if (g_iHudClientMenu[i] == hMenu) 543 | g_iHudClientMenu[i] = null; 544 | 545 | delete hMenu; 546 | } 547 | case MenuAction_Select: 548 | { 549 | if (g_cvHuds.IntValue != HudMode_Menu) 550 | return 0; // Convar just changed, dont need it anymore 551 | 552 | char sValue[16]; 553 | hMenu.GetItem(iChoice, sValue, sizeof(sValue)); 554 | 555 | if (StrEqual(sValue, "previous")) 556 | { 557 | g_iHudClientPage[iClient]--; 558 | } 559 | else if (StrEqual(sValue, "next")) 560 | { 561 | g_iHudClientPage[iClient]++; 562 | } 563 | else 564 | { 565 | int iRef = StringToInt(sValue); 566 | if (IsValidEntity(iRef)) 567 | TF2_SwitchToWeapon(iClient, EntRefToEntIndex(iRef)); 568 | } 569 | 570 | Huds_ClientDisplayMenu(iClient); 571 | } 572 | } 573 | 574 | return 0; 575 | } -------------------------------------------------------------------------------- /translations/pt/randomizer.phrases.txt: -------------------------------------------------------------------------------- 1 | "Phrases" 2 | { 3 | "Controls_Stickybomb_Attack2" 4 | { 5 | "pt" "clique direito para detonar" 6 | } 7 | 8 | "Controls_Stickybomb_Reload" 9 | { 10 | "pt" "recarregue para detonar" 11 | } 12 | 13 | "Controls_Stickybomb_Unable" 14 | { 15 | "pt" "incapaz de detonar" 16 | } 17 | 18 | "Controls_Shield_Attack2" 19 | { 20 | "pt" "clique direito para investir" 21 | } 22 | 23 | "Controls_Shield_Reload" 24 | { 25 | "pt" "recarregue para investir" 26 | } 27 | 28 | "Controls_Shield_Unable" 29 | { 30 | "pt" "incapaz de investir" 31 | } 32 | 33 | "Controls_Build_Attack2" 34 | { 35 | "pt" "clique direito para construir" 36 | } 37 | 38 | "Controls_Build_Attack3" 39 | { 40 | "pt" "clique do meio para construir" 41 | } 42 | 43 | "Controls_Build_Unable" 44 | { 45 | "pt" "incapaz de construir" 46 | } 47 | 48 | "Controls_InvisWatch_Attack2" 49 | { 50 | "pt" "clique direito para ir invis" 51 | } 52 | 53 | "Controls_InvisWatch_Attack3" 54 | { 55 | "pt" "clique do meio para ir invis" 56 | } 57 | 58 | "Controls_InvisWatch_Unable" 59 | { 60 | "pt" "invis indisponível" 61 | } 62 | 63 | "Huds_Title" 64 | { 65 | "pt" "Armas" 66 | } 67 | "Huds_EffectBarRegenTime" 68 | { 69 | "pt" "{1} Segs" 70 | } 71 | 72 | "Huds_Decapitations" 73 | { 74 | "pt" "{1} Cabeças" 75 | } 76 | 77 | "Huds_Energy" 78 | { 79 | "pt" "{1}% Energia" 80 | } 81 | 82 | "Huds_RageMeter" 83 | { 84 | "pt" "{1}% Fúria" 85 | } 86 | 87 | "Huds_RevengeCrits" 88 | { 89 | "pt" "{1} Crits" 90 | } 91 | 92 | "Huds_HypeMeter" 93 | { 94 | "pt" "{1}% Hyper" 95 | } 96 | 97 | "Huds_ItemChargeMeter" 98 | { 99 | "pt" "{1}%" 100 | } 101 | 102 | "Huds_PipebombCount" 103 | { 104 | "pt" "{1} Stickies" 105 | } 106 | 107 | "Huds_ChargeBeginTime" 108 | { 109 | "pt" "{1}% Investida" 110 | } 111 | 112 | "Huds_ChargeMeter" 113 | { 114 | "pt" "{1}% Carga" 115 | } 116 | 117 | "Huds_Ammo" 118 | { 119 | "pt" "{1} Ferro" 120 | } 121 | 122 | "Huds_ChargeLevel" 123 | { 124 | "pt" "{1}% Über" 125 | } 126 | 127 | "Huds_ChargeResistType_Bullet" 128 | { 129 | "pt" "Tipo: Balas" 130 | } 131 | 132 | "Huds_ChargeResistType_Blast" 133 | { 134 | "pt" "Tipo: Explosão" 135 | } 136 | 137 | "Huds_ChargeResistType_Fire" 138 | { 139 | "pt" "Tipo: Fogo" 140 | } 141 | 142 | "Huds_MinicritCharge" 143 | { 144 | "pt" "{1}% Crikey" 145 | } 146 | 147 | "Huds_KnifeMeltTimestamp" 148 | { 149 | "pt" "{1} seg" 150 | } 151 | 152 | "Huds_DisguiseTeam_Red" 153 | { 154 | "pt" "Vermelho" 155 | } 156 | 157 | "Huds_DisguiseTeam_Blue" 158 | { 159 | "pt" "Azul" 160 | } 161 | 162 | "Huds_DisguiseClass_Scout" 163 | { 164 | "pt" "Scout" 165 | } 166 | 167 | "Huds_DisguiseClass_Sniper" 168 | { 169 | "pt" "Sniper" 170 | } 171 | 172 | "Huds_DisguiseClass_Soldier" 173 | { 174 | "pt" "Soldier" 175 | } 176 | 177 | "Huds_DisguiseClass_Demoman" 178 | { 179 | "pt" "Demoman" 180 | } 181 | 182 | "Huds_DisguiseClass_Medic" 183 | { 184 | "pt" "Medic" 185 | } 186 | 187 | "Huds_DisguiseClass_Heavy" 188 | { 189 | "pt" "Heavy" 190 | } 191 | 192 | "Huds_DisguiseClass_Pyro" 193 | { 194 | "pt" "Pyro" 195 | } 196 | 197 | "Huds_DisguiseClass_Spy" 198 | { 199 | "pt" "Spy" 200 | } 201 | 202 | "Huds_DisguiseClass_Engineer" 203 | { 204 | "pt" "Engineer" 205 | } 206 | 207 | "Weapon_BASEJumper" 208 | { 209 | "pt" "Saltador de B.A.S.E" 210 | } 211 | 212 | "Weapon_Shotgun" 213 | { 214 | "pt" "Escopeta" 215 | } 216 | 217 | "Weapon_PanicAttack" 218 | { 219 | "pt" "Ataque de Panico" 220 | } 221 | 222 | "Weapon_Pistol" 223 | { 224 | "pt" "Pistola" 225 | } 226 | 227 | "Weapon_ReserveShooter" 228 | { 229 | "pt" "Atiradora Reversa" 230 | } 231 | 232 | "Weapon_PainTrain" 233 | { 234 | "pt" "Pau e Prego" 235 | } 236 | 237 | "Weapon_HalfZatoichi" 238 | { 239 | "pt" "Quase-Zatoichi" 240 | } 241 | "Weapon_Scattergun" 242 | { 243 | "pt" "Escopeta Compacta" 244 | } 245 | 246 | "Weapon_ForceANature" 247 | { 248 | "pt" "Força-Da-Natureza" 249 | } 250 | 251 | "Weapon_Shortstop" 252 | { 253 | "pt" "Interbases" 254 | } 255 | 256 | "Weapon_SodaPopper" 257 | { 258 | "pt" "Refriespingarda" 259 | } 260 | 261 | "Weapon_BabyFaceBlaster" 262 | { 263 | "pt" "Destruidora do Degenerado" 264 | } 265 | 266 | "Weapon_BackScatter" 267 | { 268 | "pt" "Fuziladora de Costas" 269 | } 270 | 271 | "Weapon_RocketLauncher" 272 | { 273 | "pt" "Lança-Foguetes" 274 | } 275 | 276 | "Weapon_DirectHit" 277 | { 278 | "pt" "Tiro Certo" 279 | } 280 | 281 | "Weapon_BlackBox" 282 | { 283 | "pt" "Caixa Preta" 284 | } 285 | 286 | "Weapon_RocketJumper" 287 | { 288 | "pt" "Lança-Foguetes de Treino" 289 | } 290 | 291 | "Weapon_LibertyLauncher" 292 | { 293 | "pt" "Lançador da Liberdade" 294 | } 295 | 296 | "Weapon_CowMangler5000" 297 | { 298 | "pt" "Avacalhador 5000" 299 | } 300 | 301 | "Weapon_Original" 302 | { 303 | "pt" "Original" 304 | } 305 | 306 | "Weapon_BeggarBazooka" 307 | { 308 | "pt" "Lança-Foguetes do Lamentável" 309 | } 310 | 311 | "Weapon_AirStrike" 312 | { 313 | "pt" "O Ataque Aéreo" 314 | } 315 | 316 | "Weapon_FlameThrower" 317 | { 318 | "pt" "Lança-Chamas" 319 | } 320 | 321 | "Weapon_Backburner" 322 | { 323 | "pt" "Queima-Scostas" 324 | } 325 | 326 | "Weapon_Degreaser" 327 | { 328 | "pt" "Desengraxante" 329 | } 330 | 331 | "Weapon_Phlogistinator" 332 | { 333 | "pt" "Flogistinador" 334 | } 335 | 336 | "Weapon_DragonFury" 337 | { 338 | "pt" "Fúria do Dragão" 339 | } 340 | 341 | "Weapon_GrenadeLauncher" 342 | { 343 | "pt" "Lança-Granadas" 344 | } 345 | 346 | "Weapon_LochnLoad" 347 | { 348 | "pt" "Bombas do Lago Ness" 349 | } 350 | 351 | "Weapon_AliBabaWeeBooties" 352 | { 353 | "pt" "Botinhas do Ali babá" 354 | } 355 | 356 | "Weapon_LooseCannon" 357 | { 358 | "pt" "Pavio Curto" 359 | } 360 | 361 | "Weapon_IronBomber" 362 | { 363 | "pt" "Bombardeiro de Ferro" 364 | } 365 | 366 | "Weapon_Minigun" 367 | { 368 | "pt" "Minigun" 369 | } 370 | 371 | "Weapon_Natascha" 372 | { 373 | "pt" "Natascha" 374 | } 375 | 376 | "Weapon_BrassBeast" 377 | { 378 | "pt" "Fera do Bronze" 379 | } 380 | 381 | "Weapon_Tomislav" 382 | { 383 | "pt" "Tomislav" 384 | } 385 | 386 | "Weapon_HuoLongHeater" 387 | { 388 | "pt" "Chamas de Huo-long" 389 | } 390 | 391 | "Weapon_FrontierJustice" 392 | { 393 | "pt" "Justiça Vingadora" 394 | } 395 | 396 | "Weapon_Widowmaker" 397 | { 398 | "pt" "Viuvadora" 399 | } 400 | 401 | "Weapon_Pomson6000" 402 | { 403 | "pt" "Pomson 6000" 404 | } 405 | 406 | "Weapon_RescueRanger" 407 | { 408 | "pt" "Telescopeta" 409 | } 410 | 411 | "Weapon_SyringeGun" 412 | { 413 | "pt" "Arma de Seringas" 414 | } 415 | 416 | "Weapon_Blutsauger" 417 | { 418 | "pt" "Blutsauger" 419 | } 420 | 421 | "Weapon_CrusaderCrossbow" 422 | { 423 | "pt" "Besta do Cruzado" 424 | } 425 | 426 | "Weapon_Overdose" 427 | { 428 | "pt" "Overdose" 429 | } 430 | 431 | "Weapon_SniperRifle" 432 | { 433 | "pt" "Rifle de Sniper" 434 | } 435 | 436 | "Weapon_Huntsman" 437 | { 438 | "pt" "Caçador" 439 | } 440 | 441 | "Weapon_SydneySleeper" 442 | { 443 | "pt" "Anestesiador de Sydney" 444 | } 445 | 446 | "Weapon_BazaarBargain" 447 | { 448 | "pt" "Barganha do Bazar" 449 | } 450 | 451 | "Weapon_Machina" 452 | { 453 | "pt" "Machina" 454 | } 455 | 456 | "Weapon_HitmanHeatmaker" 457 | { 458 | "pt" "Assasino de Aluguel" 459 | } 460 | 461 | "Weapon_FortifiedCompound" 462 | { 463 | "pt" "Arco Composto Fortificado" 464 | } 465 | 466 | "Weapon_Classic" 467 | { 468 | "pt" "Clássico" 469 | } 470 | 471 | "Weapon_Revolver" 472 | { 473 | "pt" "Revólver" 474 | } 475 | 476 | "Weapon_Ambassador" 477 | { 478 | "pt" "Embaixador" 479 | } 480 | 481 | "Weapon_LEtranger" 482 | { 483 | "pt" "L'Etranger" 484 | } 485 | 486 | "Weapon_Enforcer" 487 | { 488 | "pt" "Capanga" 489 | } 490 | 491 | "Weapon_Diamondback" 492 | { 493 | "pt" "Diamante Negro" 494 | } 495 | 496 | "Weapon_BonkAtomicPunch" 497 | { 498 | "pt" "Bonk! Pancada Atómica" 499 | } 500 | 501 | "Weapon_CritaCola" 502 | { 503 | "pt" "Criti-Cola" 504 | } 505 | 506 | "Weapon_MadMilk" 507 | { 508 | "pt" "Leite Louco" 509 | } 510 | 511 | "Weapon_Winger" 512 | { 513 | "pt" "Ala" 514 | } 515 | 516 | "Weapon_PrettyBoyPocketPistol" 517 | { 518 | "pt" "Pistola Portátil do Peralta" 519 | } 520 | 521 | "Weapon_FlyingGuillotine" 522 | { 523 | "pt" "Guilhotina Voadora" 524 | } 525 | 526 | "Weapon_BuffBanner" 527 | { 528 | "pt" "Estandarte do Encorajamento" 529 | } 530 | 531 | "Weapon_Gunboats" 532 | { 533 | "pt" "Canhoneiras" 534 | } 535 | 536 | "Weapon_BattalionBackup" 537 | { 538 | "pt" "Reforço do Batalhão" 539 | } 540 | 541 | "Weapon_Concheror" 542 | { 543 | "pt" "Conchistador" 544 | } 545 | 546 | "Weapon_RighteousBison" 547 | { 548 | "pt" "Bisão Justiceiro" 549 | } 550 | 551 | "Weapon_Mantreads" 552 | { 553 | "pt" "Coturnos de Macho" 554 | } 555 | 556 | "Weapon_FlareGun" 557 | { 558 | "pt" "Arma Sinalizadora" 559 | } 560 | 561 | "Weapon_Detonator" 562 | { 563 | "pt" "Detonadora" 564 | } 565 | 566 | "Weapon_Manmelter" 567 | { 568 | "pt" "Derretedora de Homens" 569 | } 570 | 571 | "Weapon_ScorchShot" 572 | { 573 | "pt" "Queima-Roupa" 574 | } 575 | 576 | "Weapon_ThermalThruster" 577 | { 578 | "pt" "Propulsor Térmico" 579 | } 580 | 581 | "Weapon_GasPasser" 582 | { 583 | "pt" "Galão de Gasolina" 584 | } 585 | 586 | "Weapon_StickybombLauncher" 587 | { 588 | "pt" "Lança-StickyBombs" 589 | } 590 | 591 | "Weapon_ScottishResistance" 592 | { 593 | "pt" "Resistência Escocesa" 594 | } 595 | 596 | "Weapon_CharginTarge" 597 | { 598 | "pt" "Tarja de Investida" 599 | } 600 | 601 | "Weapon_StickyJumper" 602 | { 603 | "pt" "Sticky Bombs de Treino" 604 | } 605 | 606 | "Weapon_SplendidScreen" 607 | { 608 | "pt" "Escudo Esplêndido" 609 | } 610 | 611 | "Weapon_TideTurner" 612 | { 613 | "pt" "Descobridor dos 7 Mares" 614 | } 615 | 616 | "Weapon_QuickiebombLauncher" 617 | { 618 | "pt" "Lança-Rápibombas" 619 | } 620 | 621 | "Weapon_Sandvich" 622 | { 623 | "pt" "Sanduba" 624 | } 625 | 626 | "Weapon_DalokohsBar" 627 | { 628 | "pt" "Barra de Dalokohs" 629 | } 630 | 631 | "Weapon_BuffaloSteakSandvich" 632 | { 633 | "pt" "Bife de Búfalo" 634 | } 635 | 636 | "Weapon_FamilyBusiness" 637 | { 638 | "pt" "Negócios de Família" 639 | } 640 | 641 | "Weapon_SecondBanana" 642 | { 643 | "pt" "Banana de Consolação" 644 | } 645 | 646 | "Weapon_Wrangler" 647 | { 648 | "pt" "Peão" 649 | } 650 | 651 | "Weapon_ShortCircuit" 652 | { 653 | "pt" "Curto-Circuito" 654 | } 655 | 656 | "Weapon_MediGun" 657 | { 658 | "pt" "Arma Médica" 659 | } 660 | 661 | "Weapon_Kritzkrieg" 662 | { 663 | "pt" "Kritzkrieg" 664 | } 665 | 666 | "Weapon_QuickFix" 667 | { 668 | "pt" "Quebra-Galho" 669 | } 670 | 671 | "Weapon_Vaccinator" 672 | { 673 | "pt" "Vacinadora" 674 | } 675 | 676 | "Weapon_SMG" 677 | { 678 | "pt" "SMG" 679 | } 680 | 681 | "Weapon_Razorback" 682 | { 683 | "pt" "Escudo Antinavalha" 684 | } 685 | 686 | "Weapon_Jarate" 687 | { 688 | "pt" "Jarratê" 689 | } 690 | 691 | "Weapon_DarwinDangerShield" 692 | { 693 | "pt" "Escudo Darwinista de Perigo" 694 | } 695 | 696 | "Weapon_CozyCamper" 697 | { 698 | "pt" "Campista Comfortável" 699 | } 700 | 701 | "Weapon_CleanerCarbine" 702 | { 703 | "pt" "Carabina do Carrasco" 704 | } 705 | 706 | "Weapon_Sapper" 707 | { 708 | "pt" "Sabotador" 709 | } 710 | 711 | "Weapon_RedTapeRecorder" 712 | { 713 | "pt" "Rebobinador" 714 | } 715 | 716 | "Weapon_Bat" 717 | { 718 | "pt" "Taco" 719 | } 720 | 721 | "Weapon_Sandman" 722 | { 723 | "pt" "João Pestana" 724 | } 725 | 726 | "Weapon_HolyMackerel" 727 | { 728 | "pt" "Santo Carapau" 729 | } 730 | 731 | "Weapon_CandyCane" 732 | { 733 | "pt" "Bengala Doce" 734 | } 735 | 736 | "Weapon_BostonBasher" 737 | { 738 | "pt" "Batedor de Boston" 739 | } 740 | 741 | "Weapon_SunonaStick" 742 | { 743 | "pt" "Espetinho de Sol" 744 | } 745 | 746 | "Weapon_FanOWar" 747 | { 748 | "pt" "Leque D'Guerra" 749 | } 750 | 751 | "Weapon_Atomizer" 752 | { 753 | "pt" "Atomizador" 754 | } 755 | 756 | "Weapon_WrapAssassin" 757 | { 758 | "pt" "Assasino do Embrulho" 759 | } 760 | 761 | "Weapon_Shovel" 762 | { 763 | "pt" "Pá" 764 | } 765 | 766 | "Weapon_Equalizer" 767 | { 768 | "pt" "Equalizador" 769 | } 770 | 771 | "Weapon_PainTrain" 772 | { 773 | "pt" "Pau e Prego" 774 | } 775 | 776 | "Weapon_HalfZatoichi" 777 | { 778 | "pt" "Quase-Zatoichi" 779 | } 780 | 781 | "Weapon_MarketGardener" 782 | { 783 | "pt" "Pá-raquedista" 784 | } 785 | 786 | "Weapon_DisciplinaryAction" 787 | { 788 | "pt" "Ação Disciplinar" 789 | } 790 | 791 | "Weapon_EscapePlan" 792 | { 793 | "pt" "Plano de Fuga" 794 | } 795 | 796 | "Weapon_FireAxe" 797 | { 798 | "pt" "Machado de Incêndio" 799 | } 800 | 801 | "Weapon_Axtinguisher" 802 | { 803 | "pt" "Queimachado" 804 | } 805 | 806 | "Weapon_Homewrecker" 807 | { 808 | "pt" "Quebra-Casas" 809 | } 810 | 811 | "Weapon_Powerjack" 812 | { 813 | "pt" "Ligação Direta" 814 | } 815 | 816 | "Weapon_BackScratcher" 817 | { 818 | "pt" "Coçador de Costas" 819 | } 820 | 821 | "Weapon_SharpenedVolcanoFragment" 822 | { 823 | "pt" "Fragmento Afiado de Vulcão" 824 | } 825 | 826 | "Weapon_PostalPummeler" 827 | { 828 | "pt" "Pancada Postal" 829 | } 830 | 831 | "Weapon_ThirdDegree" 832 | { 833 | "pt" "Terceiro-Grau" 834 | } 835 | 836 | "Weapon_NeonAnnihilator" 837 | { 838 | "pt" "Aniquilador Neônico" 839 | } 840 | 841 | "Weapon_HotHand" 842 | { 843 | "pt" "Bofetada a Brasa" 844 | } 845 | 846 | "Weapon_Bottle" 847 | { 848 | "pt" "Garrafa" 849 | } 850 | 851 | "Weapon_Eyelander" 852 | { 853 | "pt" "Eyelander" 854 | } 855 | 856 | "Weapon_PainTrain" 857 | { 858 | "pt" "Pau e Prego" 859 | } 860 | 861 | "Weapon_ScotsmanSkullcutter" 862 | { 863 | "pt" "Corta-Crânios Escocês" 864 | } 865 | 866 | "Weapon_UllapoolCaber" 867 | { 868 | "pt" "Tora de Ullapool" 869 | } 870 | 871 | "Weapon_ClaidheamhMor" 872 | { 873 | "pt" "Claidheamh Mòr" 874 | } 875 | 876 | "Weapon_HalfZatoichi" 877 | { 878 | "pt" "Quase-Zatoichi" 879 | } 880 | 881 | "Weapon_PersianPersuader" 882 | { 883 | "pt" "Persuador Persa" 884 | } 885 | 886 | "Weapon_Fists" 887 | { 888 | "pt" "Punhos" 889 | } 890 | 891 | "Weapon_KillingGlovesofBoxing" 892 | { 893 | "pt" "Kríticos Garantidos no Boxe" 894 | } 895 | 896 | "Weapon_GlovesofRunningUrgently" 897 | { 898 | "pt" "Geradoras Rapidez Urgente" 899 | } 900 | 901 | "Weapon_WarriorSpirit" 902 | { 903 | "pt" "Espírito do Guerreiro" 904 | } 905 | 906 | "Weapon_FistsofSteel" 907 | { 908 | "pt" "Punhos de Ferro" 909 | } 910 | 911 | "Weapon_EvictionNotice" 912 | { 913 | "pt" "Aviso de Despejo" 914 | } 915 | 916 | "Weapon_HolidayPunch" 917 | { 918 | "pt" "Soco Festivo" 919 | } 920 | 921 | "Weapon_Wrench" 922 | { 923 | "pt" "Chave Inglesa" 924 | } 925 | 926 | "Weapon_Gunslinger" 927 | { 928 | "pt" "Pistoleiro" 929 | } 930 | 931 | "Weapon_SouthernHospitality" 932 | { 933 | "pt" "Hospitalidade do Sul" 934 | } 935 | 936 | "Weapon_Jag" 937 | { 938 | "pt" "Dentadura" 939 | } 940 | 941 | "Weapon_EurekaEffect" 942 | { 943 | "pt" "Efeito Eureka" 944 | } 945 | 946 | "Weapon_Bonesaw" 947 | { 948 | "pt" "Serra de Ossos" 949 | } 950 | 951 | "Weapon_Ubersaw" 952 | { 953 | "pt" "Überserra" 954 | } 955 | 956 | "Weapon_VitaSaw" 957 | { 958 | "pt" "Vitasserra" 959 | } 960 | 961 | "Weapon_Amputator" 962 | { 963 | "pt" "Amputadora" 964 | } 965 | 966 | "Weapon_SolemnVow" 967 | { 968 | "pt" "Voto Solene" 969 | } 970 | 971 | "Weapon_Kukri" 972 | { 973 | "pt" "Kukri" 974 | } 975 | 976 | "Weapon_TribalmanShiv" 977 | { 978 | "pt" "Estoque Tribal" 979 | } 980 | 981 | "Weapon_Bushwacka" 982 | { 983 | "pt" "Facão do Desbravador" 984 | } 985 | 986 | "Weapon_Shahanshah" 987 | { 988 | "pt" "Shahanshah" 989 | } 990 | 991 | "Weapon_Knife" 992 | { 993 | "pt" "Faca Borboleta" 994 | } 995 | 996 | "Weapon_YourEternalReward" 997 | { 998 | "pt" "Sua Eterna Recompensa" 999 | } 1000 | 1001 | "Weapon_ConniverKunai" 1002 | { 1003 | "pt" "Kunai do Conspirador" 1004 | } 1005 | 1006 | "Weapon_BigEarner" 1007 | { 1008 | "pt" "Grande Ganhador" 1009 | } 1010 | 1011 | "Weapon_Spycicle" 1012 | { 1013 | "pt" "Spy-Lactite" 1014 | } 1015 | 1016 | "Weapon_ConstructionPDA" 1017 | { 1018 | "pt" "PDA de Construção" 1019 | } 1020 | 1021 | "Weapon_DestructionPDA" 1022 | { 1023 | "pt" "PDA de Demolição" 1024 | } 1025 | 1026 | "Weapon_Toolbox" 1027 | { 1028 | "pt" "Caixa de Ferramentas" 1029 | } 1030 | 1031 | "Weapon_DisguiseKit" 1032 | { 1033 | "pt" "Kit de Disfarce" 1034 | } 1035 | 1036 | "Weapon_InvisWatch" 1037 | { 1038 | "pt" "Relogio de Camuflagem" 1039 | } 1040 | 1041 | "Weapon_DeadRinger" 1042 | { 1043 | "pt" "Cópia Mortal" 1044 | } 1045 | 1046 | "Weapon_CloakandDagger" 1047 | { 1048 | "pt" "Manto e Adaga" 1049 | } 1050 | } 1051 | -------------------------------------------------------------------------------- /scripting/randomizer/sdkhook.sp: -------------------------------------------------------------------------------- 1 | static int g_iMedigunBeamRef[MAXPLAYERS + 1] = {INVALID_ENT_REFERENCE, ...}; 2 | static int g_iTouchLunchbox = INVALID_ENT_REFERENCE; 3 | 4 | void SDKHook_HookClient(int iClient) 5 | { 6 | SDKHook(iClient, SDKHook_OnTakeDamage, Client_OnTakeDamage); 7 | SDKHook(iClient, SDKHook_OnTakeDamagePost, Client_OnTakeDamagePost); 8 | SDKHook(iClient, SDKHook_PreThink, Client_PreThink); 9 | SDKHook(iClient, SDKHook_PreThinkPost, Client_PreThinkPost); 10 | SDKHook(iClient, SDKHook_PostThink, Client_PostThink); 11 | SDKHook(iClient, SDKHook_PostThinkPost, Client_PostThinkPost); 12 | SDKHook(iClient, SDKHook_WeaponEquip, Client_WeaponEquip); 13 | SDKHook(iClient, SDKHook_WeaponEquipPost, Client_WeaponEquipPost); 14 | SDKHook(iClient, SDKHook_WeaponSwitch, Client_WeaponSwitch); 15 | SDKHook(iClient, SDKHook_WeaponSwitchPost, Client_WeaponSwitchPost); 16 | SDKHook(iClient, SDKHook_WeaponCanSwitchTo, Client_WeaponCanSwitchTo); 17 | SDKHook(iClient, SDKHook_WeaponCanSwitchToPost, Client_WeaponCanSwitchToPost); 18 | } 19 | 20 | void SDKHook_UnhookClient(int iClient) 21 | { 22 | SDKUnhook(iClient, SDKHook_OnTakeDamage, Client_OnTakeDamage); 23 | SDKUnhook(iClient, SDKHook_OnTakeDamagePost, Client_OnTakeDamagePost); 24 | SDKUnhook(iClient, SDKHook_PreThink, Client_PreThink); 25 | SDKUnhook(iClient, SDKHook_PreThinkPost, Client_PreThinkPost); 26 | SDKUnhook(iClient, SDKHook_PostThink, Client_PostThink); 27 | SDKUnhook(iClient, SDKHook_PostThinkPost, Client_PostThinkPost); 28 | SDKUnhook(iClient, SDKHook_WeaponEquip, Client_WeaponEquip); 29 | SDKUnhook(iClient, SDKHook_WeaponEquipPost, Client_WeaponEquipPost); 30 | SDKUnhook(iClient, SDKHook_WeaponSwitch, Client_WeaponSwitch); 31 | SDKUnhook(iClient, SDKHook_WeaponSwitchPost, Client_WeaponSwitchPost); 32 | SDKUnhook(iClient, SDKHook_WeaponCanSwitchTo, Client_WeaponCanSwitchTo); 33 | SDKUnhook(iClient, SDKHook_WeaponCanSwitchToPost, Client_WeaponCanSwitchToPost); 34 | } 35 | 36 | void SDKHook_OnEntityCreated(int iEntity, const char[] sClassname) 37 | { 38 | if (StrContains(sClassname, "tf_weapon_") == 0) 39 | SDKHook(iEntity, SDKHook_Reload, Weapon_Reload); 40 | else if (StrEqual(sClassname, "item_healthkit_small")) 41 | SDKHook(iEntity, SDKHook_SpawnPost, HealthKit_SpawnPost); 42 | 43 | if (StrContains(sClassname, "item_healthkit") == 0 || StrEqual(sClassname, "tf_projectile_stun_ball") || StrEqual(sClassname, "tf_projectile_ball_ornament")) 44 | { 45 | SDKHook(iEntity, SDKHook_Touch, Item_Touch); 46 | SDKHook(iEntity, SDKHook_TouchPost, Item_TouchPost); 47 | } 48 | } 49 | 50 | public Action Client_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &flDamage, int &iDamageType, int &iWeapon, float vecDamageForce[3], float vecDamagePosition[3], int iDamageCustom) 51 | { 52 | //Enable IsPlayerClass patch at ApplyOnDamageModifyRules detour, 53 | // so proper soldier/demoman class check can be done for rocket jumping, 54 | // before ApplyOnDamageModifyRules detour is called 55 | g_bOnTakeDamage = true; 56 | 57 | if (0 < iAttacker <= MaxClients) 58 | { 59 | if (iWeapon != INVALID_ENT_REFERENCE && HasEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex")) // iWeapon is not always actually a weapon 60 | { 61 | //Set attacker class to whatever default class from weapon, 62 | // IsPlayerClass not always called on linux, 63 | // and some class checks don't even use IsPlayerClass. 64 | SetClientClass(iAttacker, TF2_GetDefaultClassFromItem(iWeapon)); 65 | g_bOnTakeDamageClass[iAttacker] = true; 66 | 67 | Properties_LoadWeaponPropInt(iAttacker, iWeapon, "m_iDecapitations"); 68 | g_bWeaponDecap[iAttacker] = true; 69 | } 70 | 71 | //Setup collecting revenge crits for diamondback 72 | Properties_SaveActiveWeaponPropInt(iAttacker, "m_iRevengeCrits"); 73 | SetEntProp(iAttacker, Prop_Send, "m_iRevengeCrits", 0); 74 | } 75 | 76 | return Plugin_Continue; 77 | } 78 | 79 | public void Client_OnTakeDamagePost(int iVictim, int iAttacker, int iInflictor, float flDamage, int iDamageType, int iWeapon, const float vecDamageForce[3], const float vecDamagePosition[3], int iDamageCustom) 80 | { 81 | if (!g_bOnTakeDamage) 82 | Patch_DisableIsPlayerClass(); 83 | 84 | g_bOnTakeDamage = false; 85 | g_bFeignDeath[iVictim] = false; 86 | 87 | if (0 < iAttacker <= MaxClients) 88 | { 89 | if (g_bOnTakeDamageClass[iAttacker]) 90 | { 91 | RevertClientClass(iAttacker); 92 | g_bOnTakeDamageClass[iAttacker] = false; 93 | } 94 | 95 | if (iWeapon != INVALID_ENT_REFERENCE && HasEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex")) 96 | { 97 | Properties_SaveWeaponPropInt(iAttacker, iWeapon, "m_iDecapitations"); 98 | g_bWeaponDecap[iAttacker] = false; 99 | 100 | if (IsClassname(iWeapon, "tf_weapon_sword")) 101 | { 102 | //Set same value to all eyelanders 103 | int iDecap = Properties_GetWeaponPropInt(iWeapon, "m_iDecapitations"); 104 | 105 | int iTempWeapon, iPos; 106 | while (TF2_GetItemFromClassname(iAttacker, "tf_weapon_sword", iTempWeapon, iPos)) 107 | Properties_SetWeaponPropInt(iTempWeapon, "m_iDecapitations", iDecap); 108 | } 109 | } 110 | 111 | int iRevengeCrits = GetEntProp(iAttacker, Prop_Send, "m_iRevengeCrits"); 112 | if (iRevengeCrits > 0) 113 | { 114 | //Add revenge crit to all diamondbacks 115 | int iTempWeapon, iPos; 116 | while (TF2_GetItemFromAttribute(iAttacker, "sapper_kills_collect_crits", iTempWeapon, iPos)) //This is not a sapper kill... 117 | Properties_AddWeaponPropInt(iTempWeapon, "m_iRevengeCrits", iRevengeCrits); 118 | } 119 | 120 | //Set it back 121 | Properties_LoadActiveWeaponPropInt(iAttacker, "m_iRevengeCrits"); 122 | } 123 | } 124 | 125 | public void Client_PreThink(int iClient) 126 | { 127 | //Make sure player cant use primary or secondary attack while cloaked 128 | if (TF2_IsPlayerInCondition(iClient, TFCond_Cloaked)) 129 | { 130 | int iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 131 | if (iWeapon > MaxClients) 132 | { 133 | float flGameTime = GetGameTime(); 134 | if (GetEntPropFloat(iWeapon, Prop_Send, "m_flNextPrimaryAttack") - 0.5 < flGameTime) 135 | SetEntPropFloat(iWeapon, Prop_Send, "m_flNextPrimaryAttack", flGameTime + 0.5); 136 | 137 | if (GetEntPropFloat(iWeapon, Prop_Send, "m_flNextSecondaryAttack") - 0.5 < flGameTime) 138 | SetEntPropFloat(iWeapon, Prop_Send, "m_flNextSecondaryAttack", flGameTime + 0.5); 139 | } 140 | } 141 | 142 | //PreThink have way too many IsPlayerClass check, always return true during it 143 | Patch_EnableIsPlayerClass(); 144 | 145 | int iWeapon, iPos; 146 | if (TF2_GetItemFromClassname(iClient, "tf_weapon_soda_popper", iWeapon, iPos)) 147 | { 148 | Properties_LoadWeaponPropFloat(iClient, iWeapon, "m_flHypeMeter"); 149 | g_iHypeMeterLoaded[iClient] = iWeapon; 150 | } 151 | 152 | // Medigun beams doesnt show if player is not medic, and we can't fix that in SDK because it all in clientside 153 | if (TF2_GetPlayerClass(iClient) == TFClass_Medic) 154 | return; 155 | 156 | static char sParticle[][] = { 157 | "", 158 | "", 159 | PARTICLE_BEAM_RED, 160 | PARTICLE_BEAM_BLU, 161 | }; 162 | 163 | iWeapon = INVALID_ENT_REFERENCE, iPos = 0; 164 | while (TF2_GetItemFromClassname(iClient, "tf_weapon_medigun", iWeapon, iPos)) 165 | { 166 | if (!IsValidEntity(g_iMedigunBeamRef[iClient])) 167 | g_iMedigunBeamRef[iClient] = TF2_SpawnParticle(sParticle[TF2_GetClientTeam(iClient)], iWeapon); 168 | 169 | int iPatient = GetEntPropEnt(iWeapon, Prop_Send, "m_hHealingTarget"); 170 | int iControlPoint = GetEntPropEnt(g_iMedigunBeamRef[iClient], Prop_Send, "m_hControlPointEnts", 0); 171 | 172 | if (0 < iPatient <= MaxClients) 173 | { 174 | //Using active weapon so beam connects to nice spot 175 | int iActiveWeapon = GetEntPropEnt(iPatient, Prop_Send, "m_hActiveWeapon"); 176 | if (iActiveWeapon != iControlPoint) 177 | { 178 | //We just started healing someone 179 | SetEntPropEnt(g_iMedigunBeamRef[iClient], Prop_Send, "m_hControlPointEnts", iActiveWeapon, 0); 180 | SetEntProp(g_iMedigunBeamRef[iClient], Prop_Send, "m_iControlPointParents", iActiveWeapon, _, 0); 181 | 182 | ActivateEntity(g_iMedigunBeamRef[iClient]); 183 | AcceptEntityInput(g_iMedigunBeamRef[iClient], "Start"); 184 | } 185 | } 186 | 187 | if (iPatient <= 0 && iControlPoint > 0) 188 | { 189 | //We just stopped healing someone 190 | SetEntPropEnt(g_iMedigunBeamRef[iClient], Prop_Send, "m_hControlPointEnts", -1, 0); 191 | SetEntProp(g_iMedigunBeamRef[iClient], Prop_Send, "m_iControlPointParents", -1, _, 0); 192 | 193 | AcceptEntityInput(g_iMedigunBeamRef[iClient], "Stop"); 194 | } 195 | } 196 | } 197 | 198 | public void Client_PreThinkPost(int iClient) 199 | { 200 | Patch_DisableIsPlayerClass(); 201 | 202 | //m_flEnergyDrinkMeter meant to be used for scout drinks, but TFCond_CritCola shared Buffalo Steak and Cleaner's Carbine 203 | //TODO fix this when player have multiple weapons 204 | if (TF2_IsPlayerInCondition(iClient, TFCond_CritCola)) 205 | { 206 | int iWeapon, iPos; 207 | if (TF2_GetItemFromClassname(iClient, "tf_weapon_lunchbox_drink", iWeapon, iPos)) 208 | SetEntPropFloat(iClient, Prop_Send, "m_flEnergyDrinkMeter", 100.0); 209 | } 210 | 211 | //Save revenge crits from weapons fired or manmelter crit collected 212 | Properties_SaveActiveWeaponPropInt(iClient, "m_iRevengeCrits"); 213 | 214 | //Save hype meter drainage to all soda popper 215 | g_iHypeMeterLoaded[iClient] = INVALID_ENT_REFERENCE; 216 | float flHypeMeter = GetEntPropFloat(iClient, Prop_Send, "m_flHypeMeter"); 217 | int iWeapon, iPos; 218 | while (TF2_GetItemFromClassname(iClient, "tf_weapon_soda_popper", iWeapon, iPos)) 219 | Properties_SetWeaponPropFloat(iWeapon, "m_flHypeMeter", flHypeMeter); 220 | 221 | Properties_LoadActiveWeaponPropFloat(iClient, "m_flHypeMeter"); 222 | } 223 | 224 | public void Client_PostThink(int iClient) 225 | { 226 | //CTFPlayerShared::UpdateItemChargeMeters is called inside CTFPlayer::ItemPostFrame/PostThink 227 | // Update charge meters ourself and forget changes TF2 did after PostThink 228 | 229 | int iWeapon, iPos; 230 | while (TF2_GetItemFromAttribute(iClient, "item_meter_charge_type", iWeapon, iPos)) 231 | { 232 | float flRate = TF2Attrib_HookValueFloat(0.0, "item_meter_charge_rate", iWeapon); 233 | if (!flRate) 234 | continue; 235 | 236 | flRate = GetGameFrameTime() / flRate * 100.0; 237 | Properties_AddWeaponChargeMeter(iClient, iWeapon, flRate); 238 | } 239 | } 240 | 241 | public void Client_PostThinkPost(int iClient) 242 | { 243 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 244 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 245 | { 246 | //If meter is increased, its from TF2 charge meters itself, reverse it 247 | // Otherwise it might've decreased as consumed, save it 248 | int iSlot = TF2_GetSlot(iActiveWeapon); 249 | if (Properties_GetWeaponPropFloat(iActiveWeapon, "m_flItemChargeMeter") < GetEntPropFloat(iClient, Prop_Send, "m_flItemChargeMeter", iSlot)) 250 | Properties_LoadWeaponPropFloat(iClient, iActiveWeapon, "m_flItemChargeMeter", iSlot); 251 | else 252 | Properties_SaveWeaponPropFloat(iClient, iActiveWeapon, "m_flItemChargeMeter", iSlot); 253 | } 254 | } 255 | 256 | public Action Client_WeaponEquip(int iClient, int iWeapon) 257 | { 258 | SetEntPropEnt(iWeapon, Prop_Send, "m_hOwnerEntity", iClient); //So client's class can be attempted first for TF2_GetDefaultClassFromItem 259 | 260 | ViewModels_UpdateArms(iClient, iWeapon); // Set arms for the weapon were about to equip 261 | 262 | //Change class before equipping the weapon, otherwise anims and reload times are odd 263 | SetClientClass(iClient, TF2_GetDefaultClassFromItem(iWeapon)); 264 | 265 | // Don't allow robotarm model screw up anims 266 | if (TF2Attrib_HookValueFloat(0.0, "wrench_builds_minisentry", iClient) == 1.0) 267 | TF2Attrib_SetByName(iClient, "mod wrench builds minisentry", -1.0); // 1.0 + -1.0 = 0.0 268 | 269 | return Plugin_Continue; 270 | } 271 | 272 | public void Client_WeaponEquipPost(int iClient, int iWeapon) 273 | { 274 | TF2Attrib_RemoveByName(iClient, "mod wrench builds minisentry"); 275 | 276 | RevertClientClass(iClient); 277 | 278 | ViewModels_UpdateArms(iClient); 279 | 280 | //Refresh controls and huds 281 | Controls_RefreshClient(iClient); 282 | Huds_RefreshClient(iClient); 283 | } 284 | 285 | public Action Client_WeaponSwitch(int iClient, int iWeapon) 286 | { 287 | ViewModels_UpdateArms(iClient); // Incase if weapons were to be not properly set up yet for draw animation 288 | 289 | //Save current active weapon properties before potentally switched out 290 | Properties_SaveActiveWeaponAmmo(iClient); 291 | 292 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 293 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 294 | { 295 | Properties_SaveWeaponPropInt(iClient, iActiveWeapon, "m_iRevengeCrits"); 296 | Properties_SaveWeaponPropFloat(iClient, iActiveWeapon, "m_flItemChargeMeter", TF2_GetSlot(iActiveWeapon)); 297 | } 298 | 299 | return Plugin_Continue; 300 | } 301 | 302 | public void Client_WeaponSwitchPost(int iClient, int iWeapon) 303 | { 304 | ViewModels_UpdateArms(iClient); // Update arms model with new active weapon 305 | 306 | //Update ammo for new active weapon 307 | Properties_UpdateActiveWeaponAmmo(iClient); 308 | 309 | int iActiveWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hActiveWeapon"); 310 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 311 | { 312 | int iRevengeCrits = GetEntProp(iClient, Prop_Send, "m_iRevengeCrits"); 313 | if (iRevengeCrits > 0 && Properties_GetWeaponPropInt(iActiveWeapon, "m_iRevengeCrits") == 0) 314 | TF2_RemoveCondition(iClient, TFCond_Kritzkrieged); 315 | else if (iRevengeCrits == 0 && Properties_GetWeaponPropInt(iActiveWeapon, "m_iRevengeCrits") > 0) 316 | TF2_AddCondition(iClient, TFCond_Kritzkrieged, TFCondDuration_Infinite); 317 | 318 | Properties_LoadRageProps(iClient, iActiveWeapon); 319 | Properties_LoadWeaponPropInt(iClient, iActiveWeapon, "m_iDecapitations"); 320 | Properties_LoadWeaponPropInt(iClient, iActiveWeapon, "m_iRevengeCrits"); 321 | Properties_LoadWeaponPropFloat(iClient, iActiveWeapon, "m_flHypeMeter"); 322 | Properties_LoadWeaponPropFloat(iClient, iActiveWeapon, "m_flItemChargeMeter", TF2_GetSlot(iActiveWeapon)); 323 | } 324 | } 325 | 326 | public Action Client_WeaponCanSwitchTo(int iClient, int iWeapon) 327 | { 328 | if (iWeapon == INVALID_ENT_REFERENCE) 329 | return Plugin_Continue; 330 | 331 | Properties_SaveActiveWeaponAmmo(iClient); 332 | 333 | //Set ammo to weapon wanting to switch, see if allow or deny 334 | int iAmmoType = GetEntProp(iWeapon, Prop_Send, "m_iPrimaryAmmoType"); 335 | if (iAmmoType != -1 && iAmmoType != TF_AMMO_METAL) 336 | Properties_LoadWeaponPropInt(iClient, iWeapon, "m_iAmmo", iAmmoType); 337 | 338 | return Plugin_Continue; 339 | } 340 | 341 | public void Client_WeaponCanSwitchToPost(int iClient, int iWeapon) 342 | { 343 | if (iWeapon == INVALID_ENT_REFERENCE) 344 | return; 345 | 346 | //Update ammo back to whatever active weapon is 347 | Properties_UpdateActiveWeaponAmmo(iClient); 348 | } 349 | 350 | public Action Weapon_Reload(int iWeapon) 351 | { 352 | //Weapon unable to be reloaded from cloak, but coded in revolver only, and only for Spy class 353 | int iClient = GetEntPropEnt(iWeapon, Prop_Send, "m_hOwnerEntity"); 354 | if (TF2_IsPlayerInCondition(iClient, TFCond_Cloaked)) 355 | return Plugin_Handled; 356 | 357 | return Plugin_Continue; 358 | } 359 | 360 | public void HealthKit_SpawnPost(int iHealthKit) 361 | { 362 | //Feigh death drops health pack if have Candy Cane active. Why? No idea 363 | int iClient = GetEntPropEnt(iHealthKit, Prop_Send, "m_hOwnerEntity"); 364 | if (0 < iClient <= MaxClients && g_bFeignDeath[iClient]) 365 | RemoveEntity(iHealthKit); 366 | } 367 | 368 | public Action Item_Touch(int iItem, int iToucher) 369 | { 370 | if (iToucher <= 0 || iToucher > MaxClients) 371 | return Plugin_Continue; 372 | 373 | //All items using this hook has class check on picking up 374 | Patch_EnableIsPlayerClass(); 375 | g_iTouchItem = iItem; 376 | g_iTouchToucher = iToucher; 377 | g_iTouchLunchbox = INVALID_ENT_REFERENCE; 378 | 379 | char sClassname[256]; 380 | GetEntityClassname(iItem, sClassname, sizeof(sClassname)); 381 | if (StrContains(sClassname, "item_healthkit") == 0) 382 | { 383 | //Find sandvich to use to refill 384 | float flTargetMeter = 100.0; 385 | 386 | int iWeapon, iPos; 387 | while (TF2_GetItemFromClassname(iToucher, "tf_weapon_lunchbox", iWeapon, iPos)) 388 | { 389 | float flChargeMeter = Properties_GetWeaponPropFloat(iWeapon, "m_flItemChargeMeter"); 390 | if (flChargeMeter < flTargetMeter) 391 | { 392 | g_iTouchLunchbox = iWeapon; 393 | flTargetMeter = flChargeMeter; 394 | } 395 | } 396 | 397 | if (g_iTouchLunchbox != INVALID_ENT_REFERENCE) 398 | { 399 | Properties_SetForceWeaponAmmo(g_iTouchLunchbox); 400 | 401 | int iActiveWeapon = GetEntPropEnt(iToucher, Prop_Send, "m_hActiveWeapon"); 402 | if (iActiveWeapon != INVALID_ENT_REFERENCE) 403 | Properties_SaveWeaponPropFloat(iToucher, iActiveWeapon, "m_flItemChargeMeter", TF2_GetSlot(iActiveWeapon)); 404 | 405 | Properties_LoadWeaponPropFloat(iToucher, g_iTouchLunchbox, "m_flItemChargeMeter", TF2_GetSlot(g_iTouchLunchbox)); 406 | } 407 | } 408 | else if (StrEqual(sClassname, "tf_projectile_stun_ball")) 409 | { 410 | //Find sandman that could pick up this ball 411 | int iTargetWeapon = INVALID_ENT_REFERENCE; 412 | float flTargetTime; 413 | 414 | int iWeapon, iPos; 415 | while (TF2_GetItemFromClassname(iToucher, "tf_weapon_bat_wood", iWeapon, iPos)) 416 | { 417 | float flTime = GetEntPropFloat(iWeapon, Prop_Send, "m_flEffectBarRegenTime"); 418 | if (flTime > flTargetTime) 419 | { 420 | iTargetWeapon = iWeapon; 421 | flTargetTime = flTime; 422 | } 423 | } 424 | 425 | if (iTargetWeapon != INVALID_ENT_REFERENCE) 426 | Properties_SetForceWeaponAmmo(iTargetWeapon, 1); //Set priority to 1 so other hooks dont reset it 427 | } 428 | 429 | return Plugin_Continue; 430 | } 431 | 432 | public void Item_TouchPost(int iItem, int iToucher) 433 | { 434 | if (iToucher <= 0 || iToucher > MaxClients) 435 | return; 436 | 437 | Patch_DisableIsPlayerClass(); 438 | Properties_ResetForceWeaponAmmo(1); 439 | 440 | if (g_iTouchLunchbox != INVALID_ENT_REFERENCE) 441 | Properties_SaveWeaponPropFloat(iToucher, g_iTouchLunchbox, "m_flItemChargeMeter", TF2_GetSlot(g_iTouchLunchbox)); 442 | 443 | g_iTouchItem = INVALID_ENT_REFERENCE; 444 | g_iTouchToucher = INVALID_ENT_REFERENCE; 445 | g_iTouchLunchbox = INVALID_ENT_REFERENCE; 446 | } -------------------------------------------------------------------------------- /scripting/randomizer/stocks.sp: -------------------------------------------------------------------------------- 1 | static char g_sPlayerCondProp[][] = { 2 | "m_nPlayerCond", 3 | "m_nPlayerCondEx", 4 | "m_nPlayerCondEx2", 5 | "m_nPlayerCondEx3", 6 | "m_nPlayerCondEx4", 7 | }; 8 | 9 | stock int TF2_CreateWeapon(int iClient, int iIndex, int iSlot) 10 | { 11 | char sClassname[256]; 12 | TF2Econ_GetItemClassName(iIndex, sClassname, sizeof(sClassname)); 13 | 14 | //We want to translate classname to correct classname AND slot wanted 15 | //First, try current class client playing 16 | if (TF2_GetSlotFromIndex(iIndex, TF2_GetPlayerClass(iClient)) == iSlot) 17 | { 18 | TF2Econ_TranslateWeaponEntForClass(sClassname, sizeof(sClassname), TF2_GetPlayerClass(iClient)); 19 | } 20 | else 21 | { 22 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 23 | { 24 | if (TF2_GetSlotFromIndex(iIndex, view_as(iClass)) == iSlot) 25 | { 26 | TF2Econ_TranslateWeaponEntForClass(sClassname, sizeof(sClassname), view_as(iClass)); 27 | break; 28 | } 29 | } 30 | } 31 | 32 | int iWeapon = CreateEntityByName(sClassname); 33 | if (IsValidEntity(iWeapon)) 34 | { 35 | SetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex", iIndex); 36 | SetEntProp(iWeapon, Prop_Send, "m_bInitialized", 1); 37 | 38 | SetEntProp(iWeapon, Prop_Send, "m_iEntityQuality", TFQual_Unique); 39 | SetEntProp(iWeapon, Prop_Send, "m_iEntityLevel", 1); 40 | 41 | if (TF2Econ_GetItemLoadoutSlot(iIndex, TFClass_Spy) == LoadoutSlot_Building) 42 | { 43 | SetEntProp(iWeapon, Prop_Send, "m_iObjectType", TFObject_Sapper); 44 | SetEntProp(iWeapon, Prop_Data, "m_iSubType", TFObject_Sapper); 45 | } 46 | 47 | DispatchSpawn(iWeapon); 48 | } 49 | else 50 | { 51 | PrintToChat(iClient, "Unable to create weapon! index (%d) classname (%s)", iIndex, sClassname); 52 | LogError("Unable to create weapon! index (%d), classname (%s)", iIndex, sClassname); 53 | } 54 | 55 | return iWeapon; 56 | } 57 | 58 | stock int TF2_GiveNamedItem(int iClient, Address pItem, int iSlot = -1) 59 | { 60 | int iIndex = LoadFromAddress(pItem + view_as
(g_iOffsetItemDefinitionIndex), NumberType_Int16); 61 | 62 | //We want to translate classname to correct classname AND slot wanted 63 | //First, try current class client playing 64 | TFClassType nClass = TF2_GetPlayerClass(iClient); 65 | if (TF2_GetSlotFromIndex(iIndex, nClass) != iSlot) 66 | { 67 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 68 | { 69 | if (TF2_GetSlotFromIndex(iIndex, view_as(iClass)) == iSlot) 70 | { 71 | nClass = view_as(iClass); 72 | break; 73 | } 74 | } 75 | } 76 | 77 | char sClassname[256]; 78 | TF2Econ_GetItemClassName(iIndex, sClassname, sizeof(sClassname)); 79 | TF2Econ_TranslateWeaponEntForClass(sClassname, sizeof(sClassname), nClass); 80 | 81 | int iSubType = 0; 82 | if (TF2Econ_GetItemLoadoutSlot(iIndex, TFClass_Spy) == LoadoutSlot_Building) 83 | iSubType = view_as(TFObject_Sapper); 84 | 85 | g_bAllowGiveNamedItem = true; 86 | int iWeapon = SDKCall_GiveNamedItem(iClient, sClassname, iSubType, pItem, true); 87 | g_bAllowGiveNamedItem = false; 88 | 89 | if (TF2Econ_GetItemLoadoutSlot(iIndex, TFClass_Engineer) == LoadoutSlot_Building) 90 | { 91 | SetEntProp(iWeapon, Prop_Send, "m_aBuildableObjectTypes", true, _, view_as(TFObject_Dispenser)); 92 | SetEntProp(iWeapon, Prop_Send, "m_aBuildableObjectTypes", true, _, view_as(TFObject_Teleporter)); 93 | SetEntProp(iWeapon, Prop_Send, "m_aBuildableObjectTypes", true, _, view_as(TFObject_Sentry)); 94 | SetEntProp(iWeapon, Prop_Send, "m_aBuildableObjectTypes", false, _, view_as(TFObject_Sapper)); 95 | } 96 | 97 | return iWeapon; 98 | } 99 | 100 | stock void TF2_EquipWeapon(int iClient, int iWeapon) 101 | { 102 | SetEntProp(iWeapon, Prop_Send, "m_bValidatedAttachedEntity", true); 103 | 104 | if (TF2_IsWearable(iWeapon)) 105 | SDKCall_EquipWearable(iClient, iWeapon); 106 | else 107 | EquipPlayerWeapon(iClient, iWeapon); 108 | } 109 | 110 | stock Address TF2_FindReskinItem(int iClient, int iIndex) 111 | { 112 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 113 | { 114 | int iSlot = TF2Econ_GetItemLoadoutSlot(iIndex, view_as(iClass)); 115 | Address pItem = SDKCall_GetLoadoutItem(iClient, view_as(iClass), iSlot); 116 | if (TF2_IsValidEconItemView(pItem) && Weapons_GetReskinIndex(LoadFromAddress(pItem + view_as
(g_iOffsetItemDefinitionIndex), NumberType_Int16)) == iIndex) 117 | return pItem; 118 | } 119 | 120 | return Address_Null; 121 | } 122 | 123 | stock bool TF2_IsValidEconItemView(Address pItem) 124 | { 125 | if (!pItem) 126 | return false; 127 | 128 | int iIndex = LoadFromAddress(pItem + view_as
(g_iOffsetItemDefinitionIndex), NumberType_Int16); 129 | 130 | // 65535 is basically unsigned -1 in int16 131 | return 0 <= iIndex < 65535; 132 | } 133 | 134 | stock bool TF2_IsWearable(int iWeapon) 135 | { 136 | char sClassname[256]; 137 | GetEntityClassname(iWeapon, sClassname, sizeof(sClassname)); 138 | return StrContains(sClassname, "tf_wearable") == 0 || StrEqual(sClassname, "tf_powerup_bottle"); 139 | } 140 | 141 | 142 | stock int TF2_GetWearableCount(int iClient) 143 | { 144 | return GetEntData(iClient, g_iOffsetMyWearables + 0x0C); 145 | } 146 | 147 | stock int TF2_GetWearable(int iClient, int iIndex) 148 | { 149 | Address pData = view_as
(LoadFromAddress(GetEntityAddress(iClient) + view_as
(g_iOffsetMyWearables), NumberType_Int32)); 150 | return EntRefToEntIndex(LoadFromAddress(pData + view_as
(0x04 * iIndex), NumberType_Int32) | (1 << 31)); 151 | } 152 | 153 | stock bool TF2_IndexFindAttribute(int iIndex, const char[] sAttrib, float &flVal) 154 | { 155 | ArrayList aAttribs = TF2Econ_GetItemStaticAttributes(iIndex); 156 | int iAttrib = TF2Econ_TranslateAttributeNameToDefinitionIndex(sAttrib); 157 | 158 | int iPos = aAttribs.FindValue(iAttrib, 0); 159 | if (iPos >= 0) 160 | { 161 | flVal = aAttribs.Get(iPos, 1); 162 | delete aAttribs; 163 | return true; 164 | } 165 | 166 | delete aAttribs; 167 | return false; 168 | } 169 | 170 | stock float TF2_GetAttributeValue(int iEntity, const char[] sAttrib, float flDefault) 171 | { 172 | Address pAttrib = TF2Attrib_GetByName(iEntity, sAttrib); 173 | if (pAttrib) 174 | return TF2Attrib_GetValue(pAttrib); 175 | else 176 | return flDefault; 177 | } 178 | 179 | stock bool TF2_GetItem(int iClient, int &iWeapon, int &iPos, bool bCosmetic = false) 180 | { 181 | //Could be looped through client slots, but would cause issues with >1 weapons in same slot 182 | int iMaxWeapons = GetMaxWeapons(); 183 | 184 | //Loop though all weapons (non-wearables) 185 | while (iPos < iMaxWeapons) 186 | { 187 | iWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", iPos); 188 | iPos++; 189 | 190 | if (iWeapon != INVALID_ENT_REFERENCE) 191 | return true; 192 | } 193 | 194 | int iWearableIndex = iPos - iMaxWeapons; 195 | int iWearableCount = TF2_GetWearableCount(iClient); 196 | 197 | //Loop through all wearables 198 | while (iWearableCount > iWearableIndex) 199 | { 200 | iWeapon = TF2_GetWearable(iClient, iWearableIndex); 201 | iPos++; 202 | iWearableIndex++; 203 | if (iWeapon == INVALID_ENT_REFERENCE) 204 | continue; 205 | 206 | int iIndex = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex"); 207 | if (iIndex < 0 || iIndex >= 65535) 208 | continue; //Probably attached wearable from weapon 209 | 210 | if (bCosmetic) 211 | return true; 212 | 213 | //Check if it not cosmetic 214 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 215 | { 216 | int iSlot = TF2_GetSlotFromIndex(iIndex, view_as(iClass)); 217 | if (0 <= iSlot <= WeaponSlot_Building) 218 | return true; 219 | } 220 | } 221 | 222 | //No more weapons to loop 223 | iWeapon = INVALID_ENT_REFERENCE; 224 | iPos = 0; 225 | return false; 226 | } 227 | 228 | stock bool TF2_GetItemFromClassname(int iClient, const char[] sClassname, int &iWeapon, int &iPos) 229 | { 230 | while (TF2_GetItem(iClient, iWeapon, iPos, true)) 231 | if (IsClassname(iWeapon, sClassname)) 232 | return true; 233 | 234 | return false; 235 | } 236 | 237 | stock bool TF2_GetItemFromLoadoutSlot(int iClient, int iSlot, int &iWeapon, int &iPos) 238 | { 239 | while (TF2_GetItem(iClient, iWeapon, iPos, true)) 240 | { 241 | int iIndex = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex"); 242 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 243 | { 244 | if (TF2Econ_GetItemLoadoutSlot(iIndex, view_as(iClass)) == iSlot) 245 | return true; 246 | } 247 | } 248 | 249 | return false; 250 | } 251 | 252 | stock bool TF2_GetItemFromAttribute(int iClient, const char[] sAttrib, int &iWeapon, int &iPos) 253 | { 254 | while (TF2_GetItem(iClient, iWeapon, iPos, true)) 255 | if (TF2Attrib_HookValueFloat(0.0, sAttrib, iWeapon)) 256 | return true; 257 | 258 | return false; 259 | } 260 | 261 | stock int TF2_GetSlot(int iWeapon) 262 | { 263 | int iIndex = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex"); 264 | TFClassType nClass = TF2_GetDefaultClassFromItem(iWeapon); 265 | int iSlot = TF2_GetSlotFromIndex(iIndex, nClass); 266 | if (iSlot == -1) 267 | { 268 | char sClassname[256]; 269 | GetEntityClassname(iWeapon, sClassname, sizeof(sClassname)); 270 | ThrowError("Could not find slot from item def index '%d' and classname '%s'", iIndex, sClassname); 271 | } 272 | 273 | return iSlot; 274 | } 275 | 276 | stock int TF2_GetSlotFromIndex(int iIndex, TFClassType nClass = TFClass_Unknown) 277 | { 278 | int iSlot = TF2Econ_GetItemLoadoutSlot(iIndex, nClass); 279 | if (iSlot == LoadoutSlot_Action) 280 | { 281 | iSlot = WeaponSlot_Building; 282 | } 283 | else 284 | { 285 | // Econ reports wrong slots for Engineer and Spy 286 | switch (nClass) 287 | { 288 | case TFClass_Engineer: 289 | { 290 | switch (iSlot) 291 | { 292 | case LoadoutSlot_Building: iSlot = WeaponSlot_Building; // Toolbox 293 | case LoadoutSlot_PDA: iSlot = WeaponSlot_PDA; // Construction PDA 294 | case LoadoutSlot_PDA2: iSlot = WeaponSlot_PDA2; // Destruction PDA 295 | } 296 | } 297 | case TFClass_Spy: 298 | { 299 | switch (iSlot) 300 | { 301 | case LoadoutSlot_Secondary: iSlot = WeaponSlot_Primary; // Revolver 302 | case LoadoutSlot_Building: iSlot = WeaponSlot_Secondary; // Sapper 303 | case LoadoutSlot_PDA: iSlot = WeaponSlot_PDA; // Disguise Kit 304 | case LoadoutSlot_PDA2: iSlot = WeaponSlot_PDA2; // Invis Watch 305 | } 306 | } 307 | } 308 | } 309 | 310 | return iSlot; 311 | } 312 | 313 | stock TFClassType TF2_GetDefaultClassFromItem(int iWeapon) 314 | { 315 | int iIndex = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex"); 316 | 317 | char sWeaponClassname[256], sIndexClassname[256]; 318 | GetEntityClassname(iWeapon, sWeaponClassname, sizeof(sWeaponClassname)); 319 | TF2Econ_GetItemClassName(iIndex, sIndexClassname, sizeof(sIndexClassname)); 320 | 321 | //Try client class first 322 | int iClient = GetEntPropEnt(iWeapon, Prop_Send, "m_hOwnerEntity"); 323 | if (0 < iClient <= MaxClients) 324 | { 325 | TFClassType nClass = TF2_GetPlayerClass(iClient); 326 | if (TF2_GetSlotFromIndex(iIndex, nClass) != -1) 327 | { 328 | char sClassClassname[256]; 329 | sClassClassname = sIndexClassname; 330 | TF2Econ_TranslateWeaponEntForClass(sClassClassname, sizeof(sClassClassname), nClass); 331 | 332 | if (StrEqual(sWeaponClassname, sClassClassname)) 333 | return nClass; 334 | } 335 | } 336 | 337 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 338 | { 339 | if (TF2_GetSlotFromIndex(iIndex, view_as(iClass)) >= WeaponSlot_Primary) 340 | { 341 | char sClassClassname[256]; 342 | sClassClassname = sIndexClassname; 343 | TF2Econ_TranslateWeaponEntForClass(sClassClassname, sizeof(sClassClassname), view_as(iClass)); 344 | 345 | if (StrEqual(sWeaponClassname, sClassClassname)) 346 | return view_as(iClass); 347 | } 348 | } 349 | 350 | return TFClass_Unknown; 351 | } 352 | 353 | stock TFClassType TF2_GetRandomClass() 354 | { 355 | return view_as(GetRandomInt(CLASS_MIN, CLASS_MAX)); 356 | } 357 | 358 | stock int TF2_GetSapper(int iObject) 359 | { 360 | if (!GetEntProp(iObject, Prop_Send, "m_bHasSapper")) 361 | return INVALID_ENT_REFERENCE; 362 | 363 | return GetEntPropEnt(iObject, Prop_Data, "m_hMoveChild"); 364 | } 365 | 366 | stock bool TF2_CanSwitchTo(int iClient, int iWeapon) 367 | { 368 | char sClassname[256]; 369 | GetEntityClassname(iWeapon, sClassname, sizeof(sClassname)); 370 | if (StrContains(sClassname, "tf_weapon") != 0) 371 | return false; 372 | 373 | return SDKCall_WeaponCanSwitchTo(iClient, iWeapon); 374 | } 375 | 376 | stock void TF2_SwitchToWeapon(int iClient, int iWeapon) 377 | { 378 | //Deatch other weapons first as some may have same classname 379 | int iMaxWeapons = GetMaxWeapons(); 380 | int[] iWeapons = new int[iMaxWeapons]; 381 | 382 | for (int i = 0; i < iMaxWeapons; i++) 383 | { 384 | iWeapons[i] = GetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", i); 385 | if (iWeapons[i] != iWeapon) 386 | SetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", -1, i); 387 | } 388 | 389 | char sClassname[256]; 390 | GetEntityClassname(iWeapon, sClassname, sizeof(sClassname)); 391 | FakeClientCommand(iClient, "use %s", sClassname); 392 | 393 | for (int i = 0; i < iMaxWeapons; i++) 394 | SetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", iWeapons[i], i); 395 | } 396 | 397 | stock int TF2_GiveAmmo(int iClient, int iWeapon, int iCurrent, int iAdd, int iAmmoType, bool bSuppressSound, EAmmoSource eAmmoSource) 398 | { 399 | //Basically CTFPlayer::GiveAmmo but without interfering m_iAmmo and other weapons 400 | if (iAdd <= 0 || iAmmoType < 0 || iAmmoType >= TF_AMMO_COUNT) //TF2 using MAX_AMMO_SLOTS (32) instead of TF_AMMO_COUNT... 401 | return 0; 402 | 403 | if (eAmmoSource == kAmmoSource_Resupply) 404 | { 405 | switch (iAmmoType) 406 | { 407 | case TF_AMMO_GRENADES1: 408 | { 409 | if (TF2Attrib_HookValueFloat(0.0, "grenades1_resupply_denied", iWeapon)) 410 | return 0; 411 | } 412 | case TF_AMMO_GRENADES2: 413 | { 414 | if (TF2Attrib_HookValueFloat(0.0, "grenades2_resupply_denied", iWeapon)) 415 | return 0; 416 | } 417 | case TF_AMMO_GRENADES3: 418 | { 419 | if (TF2Attrib_HookValueFloat(0.0, "grenades3_resupply_denied", iWeapon)) 420 | return 0; 421 | } 422 | } 423 | } 424 | else if (iAmmoType == TF_AMMO_METAL) //Must not be from kAmmoSource_Resupply 425 | { 426 | float flVal = TF2Attrib_HookValueFloat(1.0, "mult_metal_pickup", iClient); 427 | iAdd = RoundToFloor(flVal * float(iAdd)); 428 | } 429 | 430 | int iMaxAmmo = TF2_GetMaxAmmo(iClient, iWeapon, iAmmoType); 431 | if (iAdd + iCurrent > iMaxAmmo) 432 | iAdd = iMaxAmmo - iCurrent; 433 | 434 | if (iAdd <= 0) 435 | return 0; 436 | 437 | if (!bSuppressSound) 438 | EmitGameSoundToClient(iClient, "BaseCombatCharacter.AmmoPickup"); 439 | 440 | return iAdd; 441 | } 442 | 443 | stock int TF2_GetMaxAmmo(int iClient, int iWeapon, int iAmmoType) 444 | { 445 | //CTFPlayer::GetMaxAmmo gets attribute by whole from client, which we dont want all weapons. 446 | //We only want to scale attrib with weapon itself and all other weapons not using same ammotype 447 | 448 | int iMaxAmmo = SDKCall_GetMaxAmmo(iClient, iAmmoType, TF2_GetDefaultClassFromItem(iWeapon)); 449 | float flMultiClient = TF2_GetMultiMaxAmmo(1.0, iAmmoType, iClient); 450 | float flMultiWeapon = 1.0; 451 | 452 | int iMaxWeapons = GetMaxWeapons(); 453 | for (int i = 0; i < iMaxWeapons; i++) 454 | { 455 | int iTempWeapon = GetEntPropEnt(iClient, Prop_Send, "m_hMyWeapons", i); 456 | if (iTempWeapon == INVALID_ENT_REFERENCE) 457 | continue; 458 | 459 | if (iTempWeapon != iWeapon && GetEntProp(iTempWeapon, Prop_Send, "m_iPrimaryAmmoType") == iAmmoType) 460 | continue; 461 | 462 | flMultiWeapon = TF2_GetMultiMaxAmmo(flMultiWeapon, iAmmoType, iTempWeapon); 463 | } 464 | 465 | return RoundToFloor(float(iMaxAmmo) / flMultiClient * flMultiWeapon); 466 | } 467 | 468 | stock float TF2_GetMultiMaxAmmo(float flInitial, int iAmmoType, int iEntity) 469 | { 470 | switch (iAmmoType) 471 | { 472 | case TF_AMMO_PRIMARY: return TF2Attrib_HookValueFloat(flInitial, "mult_maxammo_primary", iEntity); 473 | case TF_AMMO_SECONDARY: return TF2Attrib_HookValueFloat(flInitial, "mult_maxammo_secondary", iEntity); 474 | case TF_AMMO_METAL: return TF2Attrib_HookValueFloat(flInitial, "mult_maxammo_metal", iEntity); 475 | case TF_AMMO_GRENADES1: return TF2Attrib_HookValueFloat(flInitial, "mult_maxammo_grenades1", iEntity); 476 | default: return flInitial; 477 | } 478 | } 479 | 480 | stock void TF2_RemoveItem(int iClient, int iWeapon) 481 | { 482 | if (TF2_IsWearable(iWeapon)) 483 | { 484 | //If wearable, just simply use TF2_RemoveWearable 485 | TF2_RemoveWearable(iClient, iWeapon); 486 | return; 487 | } 488 | 489 | //Below similar to TF2_RemoveWeaponSlot, but only removes 1 weapon instead of all weapons in 1 slot 490 | 491 | int iExtraWearable = GetEntPropEnt(iWeapon, Prop_Send, "m_hExtraWearable"); 492 | if (iExtraWearable != -1) 493 | TF2_RemoveWearable(iClient, iExtraWearable); 494 | 495 | iExtraWearable = GetEntPropEnt(iWeapon, Prop_Send, "m_hExtraWearableViewModel"); 496 | if (iExtraWearable != -1) 497 | TF2_RemoveWearable(iClient, iExtraWearable); 498 | 499 | RemovePlayerItem(iClient, iWeapon); 500 | 501 | //Add to list to remove later instead of removing all weapons at once 502 | g_aEntityToRemove.Push(EntIndexToEntRef(iWeapon)); 503 | } 504 | 505 | stock void TF2_AddConditionFake(int iClient, TFCond nCond) 506 | { 507 | int iCond = view_as(nCond); 508 | int iArray = iCond / 32; 509 | int iBit = (1 << (iCond - (iArray * 32))); 510 | SetEntProp(iClient, Prop_Send, g_sPlayerCondProp[iArray], GetEntProp(iClient, Prop_Send, g_sPlayerCondProp[iArray]) | iBit); 511 | } 512 | 513 | stock void TF2_RemoveConditionFake(int iClient, TFCond nCond) 514 | { 515 | int iCond = view_as(nCond); 516 | int iArray = iCond / 32; 517 | int iBit = (1 << (iCond - (iArray * 32))); 518 | SetEntProp(iClient, Prop_Send, g_sPlayerCondProp[iArray], GetEntProp(iClient, Prop_Send, g_sPlayerCondProp[iArray]) & ~iBit); 519 | 520 | if (iArray == 0) //Thanks legacy TF2 521 | SetEntProp(iClient, Prop_Send, "_condition_bits", GetEntProp(iClient, Prop_Send, "_condition_bits") & ~iBit); 522 | } 523 | 524 | stock int TF2_SpawnParticle(const char[] sParticle, int iEntity) 525 | { 526 | int iParticle = CreateEntityByName("info_particle_system"); 527 | DispatchKeyValue(iParticle, "effect_name", sParticle); 528 | DispatchSpawn(iParticle); 529 | 530 | float vecOrigin[3]; 531 | GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", vecOrigin); 532 | TeleportEntity(iParticle, vecOrigin, NULL_VECTOR, NULL_VECTOR); 533 | 534 | SetVariantString("!activator"); 535 | AcceptEntityInput(iParticle, "SetParent", iEntity); 536 | 537 | SetVariantString("weapon_bone_L"); 538 | AcceptEntityInput(iParticle, "SetParentAttachment"); 539 | 540 | //Return ref of entity 541 | return EntIndexToEntRef(iParticle); 542 | } 543 | 544 | stock float min(float a, float b) 545 | { 546 | return a < b ? a : b; 547 | } 548 | 549 | stock float max(float a, float b) 550 | { 551 | return a > b ? a : b; 552 | } 553 | 554 | stock float clamp(float val, float minVal, float maxVal) 555 | { 556 | if (maxVal < minVal) 557 | return maxVal; 558 | else if (val < minVal) 559 | return minVal; 560 | else if (val > maxVal) 561 | return maxVal; 562 | else 563 | return val; 564 | } 565 | 566 | stock float RemapValClamped(float val, float A, float B, float C, float D) 567 | { 568 | if ( A == B ) 569 | return val >= B ? D : C; 570 | 571 | float cVal = (val - A) / (B - A); 572 | cVal = clamp(cVal, 0.0, 1.0); 573 | 574 | return C + (D - C) * cVal; 575 | } 576 | 577 | stock bool IsClassname(int iEntity, const char[] sClassname) 578 | { 579 | char sBuffer[256]; 580 | GetEntityClassname(iEntity, sBuffer, sizeof(sBuffer)); 581 | return StrEqual(sBuffer, sClassname); 582 | } 583 | 584 | stock void AddEntityEffect(int iEntity, int iFlag) 585 | { 586 | SetEntProp(iEntity, Prop_Send, "m_fEffects", GetEntProp(iEntity, Prop_Send, "m_fEffects") | iFlag); 587 | } 588 | 589 | stock void RemoveEntityEffect(int iEntity, int iFlag) 590 | { 591 | SetEntProp(iEntity, Prop_Send, "m_fEffects", GetEntProp(iEntity, Prop_Send, "m_fEffects") & ~iFlag); 592 | } 593 | 594 | stock bool ItemIsAllowed(int iIndex) 595 | { 596 | if (GameRules_GetProp("m_bPlayingMedieval") || (GameRules_GetRoundState() == RoundState_Stalemate && FindConVar("mp_stalemate_meleeonly").BoolValue)) 597 | { 598 | //TF2 hack! 599 | char sClassname[256]; 600 | TF2Econ_GetItemClassName(iIndex, sClassname, sizeof(sClassname)); 601 | if (StrEqual(sClassname, "tf_weapon_passtime_gun")) 602 | return true; 603 | 604 | //For medieval and melee stalemate, allow melee and spy PDA 605 | for (int iClass = CLASS_MIN; iClass <= CLASS_MAX; iClass++) 606 | { 607 | int iSlot = TF2_GetSlotFromIndex(iIndex, view_as(iClass)); 608 | if (iSlot == WeaponSlot_Melee) 609 | return true; 610 | else if ((iSlot == WeaponSlot_PDA || iSlot == WeaponSlot_PDA2) && view_as(iClass) == TFClass_Spy) 611 | return true; 612 | } 613 | 614 | //For medieval, allow medieval weapons 615 | if (GameRules_GetProp("m_bPlayingMedieval")) 616 | { 617 | float flVal; 618 | if (TF2_IndexFindAttribute(iIndex, "allowed in medieval mode", flVal) && flVal) 619 | return true; 620 | } 621 | 622 | return false; 623 | } 624 | 625 | return true; 626 | } 627 | 628 | stock int GetMaxWeapons() 629 | { 630 | static int iMaxWeapons; 631 | if (!iMaxWeapons) 632 | { 633 | for (int iClient = 1; iClient <= MaxClients; iClient++) 634 | { 635 | if (IsClientInGame(iClient)) 636 | { 637 | iMaxWeapons = GetEntPropArraySize(iClient, Prop_Send, "m_hMyWeapons"); 638 | break; 639 | } 640 | } 641 | } 642 | 643 | return iMaxWeapons; 644 | } 645 | 646 | stock void GetEntityModel(int iEntity, char[] sModel, int iMaxSize, const char[] sProp = "m_nModelIndex") 647 | { 648 | int iIndex = GetEntProp(iEntity, Prop_Send, sProp); 649 | int iTable = FindStringTable("modelprecache"); 650 | ReadStringTable(iTable, iIndex, sModel, iMaxSize); 651 | } 652 | 653 | stock int GetModelIndex(const char[] sModel) 654 | { 655 | int iTable = FindStringTable("modelprecache"); 656 | return FindStringIndex(iTable, sModel); 657 | } 658 | 659 | stock int PrecacheParticleSystem(const char[] sParticle) 660 | { 661 | static int iParticleEffectNames = INVALID_STRING_TABLE; 662 | if (iParticleEffectNames == INVALID_STRING_TABLE) 663 | if ((iParticleEffectNames = FindStringTable("ParticleEffectNames")) == INVALID_STRING_TABLE) 664 | return INVALID_STRING_INDEX; 665 | 666 | int iIndex = FindStringIndex2(iParticleEffectNames, sParticle); 667 | if (iIndex == INVALID_STRING_INDEX) 668 | { 669 | int iNumStrings = GetStringTableNumStrings(iParticleEffectNames); 670 | if (iNumStrings >= GetStringTableMaxStrings(iParticleEffectNames)) 671 | return INVALID_STRING_INDEX; 672 | 673 | AddToStringTable(iParticleEffectNames, sParticle); 674 | iIndex = iNumStrings; 675 | } 676 | 677 | return iIndex; 678 | } 679 | 680 | stock int FindStringIndex2(int iTableId, const char[] sParticle) 681 | { 682 | char sBuffer[1024]; 683 | int iNumStrings = GetStringTableNumStrings(iTableId); 684 | for (int i = 0; i < iNumStrings; i++) 685 | { 686 | ReadStringTable(iTableId, i, sBuffer, sizeof(sBuffer)); 687 | if (StrEqual(sBuffer, sParticle)) 688 | return i; 689 | } 690 | 691 | return INVALID_STRING_INDEX; 692 | } --------------------------------------------------------------------------------