diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index f66d152..319bd36 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -7,20 +7,32 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + sm-version: [ '1.11.x', '1.12.x'] steps: - - name: Checkout + - name: Install Checkout uses: actions/checkout@v1 - - name: Environments - run: | - echo "SM_VERSION=1.10" >> $GITHUB_ENV + - name: Install Setup SP ${{ matrix.sm-version }} + uses: rumblefrog/setup-sp@master + with: + version: ${{ matrix.sm-version }} - - name: Install + - name: Get GitHub Env run: | - bash scripts/install.sh + echo "PLUGIN_VERSION_REVISION<> $GITHUB_ENV + git rev-list --count HEAD >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV - - name: Compile + - name: Install Misc run: | - cd build/addons/sourcemod/scripting - ./spcomp -E tf2_custom_taunts.sp -o ../plugins/tf2_custom_taunts.smx \ No newline at end of file + cd addons/sourcemod/scripting/include + wget "https://raw.githubusercontent.com/FlaminSarge/tf2attributes/master/scripting/include/tf2attributes.inc" + wget "https://raw.githubusercontent.com/nosoop/SMExt-TF2Items/main/pawn/tf2items.inc" + working-directory: ./ + + - name: Compile ${{ matrix.sm-version }} + run: spcomp -E -O2 -v2 -i "include" -o "tf2_custom_taunts" tf2_custom_taunts.sp + working-directory: addons/sourcemod/scripting \ No newline at end of file diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 248eec7..b9502a0 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -1,5 +1,8 @@ name: Package +permissions: + contents: write + on: push: branches: master @@ -9,32 +12,36 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout + - name: Install Checkout uses: actions/checkout@v1 - - name: Environments - run: | - echo "SM_VERSION=1.10" >> $GITHUB_ENV + - name: Install Setup SP + uses: rumblefrog/setup-sp@master + with: + version: '1.11.x' - - name: Install + - name: Get GitHub Env run: | - bash scripts/install.sh - - - name: Set Version + echo "PLUGIN_VERSION_REVISION<> $GITHUB_ENV + git rev-list --count HEAD >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Install Misc run: | - bash scripts/date.sh + cd addons/sourcemod/scripting/include + wget "https://raw.githubusercontent.com/FlaminSarge/tf2attributes/master/scripting/include/tf2attributes.inc" + wget "https://raw.githubusercontent.com/nosoop/SMExt-TF2Items/main/pawn/tf2items.inc" + working-directory: ./ - name: Compile - run: | - cd build/addons/sourcemod/scripting - ./spcomp tf2_custom_taunts.sp -o ../plugins/tf2_custom_taunts.smx - - - name: Package - run: | - bash scripts/package.sh + run: spcomp -E -O2 -v2 -i "include" -o "tf2_custom_taunts" tf2_custom_taunts.sp + working-directory: addons/sourcemod/scripting - - name: Upload Artifacts - uses: actions/upload-artifact@master + - name: Release + uses: softprops/action-gh-release@master with: - name: TF2-Custom-Taunts-${{env.DATE_VERSION}} - path: ./build/package \ No newline at end of file + tag_name: ${{env.PLUGIN_VERSION}}.${{env.PLUGIN_VERSION_REVISION}} + files: | + addons/sourcemod/scripting/tf2_custom_taunts.smx + addons/sourcemod/gamedata/tf2.tauntem.txt + addons/sourcemod/configs/custom_taunts/taunts.cfg \ No newline at end of file diff --git a/addons/sourcemod/configs/customtaunts.cfg b/addons/sourcemod/configs/custom_taunts/taunts.cfg similarity index 89% rename from addons/sourcemod/configs/customtaunts.cfg rename to addons/sourcemod/configs/custom_taunts/taunts.cfg index 5a0b386..79f37e6 100644 --- a/addons/sourcemod/configs/customtaunts.cfg +++ b/addons/sourcemod/configs/custom_taunts/taunts.cfg @@ -1,661 +1,661 @@ -"customtaunts" -{ - "Battin' a Thousand" - { - "scout" - { - "index" "1117" - } - } - "Deep Fried Desire" - { - "scout" - { - "index" "1119" - } - } - "The Carlton" - { - "scout" - { - "index" "1168" - } - } - "The Scooty Scoot" - { - "scout" - { - "index" "1197" - } - } - "The Boston Breakdance" - { - "scout" - { - "index" "30572" - } - } - "The Trackman's Touchdown" - { - "scout" - { - "index" "30917" - } - } - "The Bunnyhopper" - { - "scout" - { - "index" "30920" - } - } - "Runner's Rhythm" - { - "scout" - { - "index" "30921" - } - } - "Boston Boarder" - { - "scout" - { - "index" "31156" - } - } - "Spin-to-Win" - { - "scout" - { - "index" "31161" - } - } - "The Homerunner's Hobby" - { - "scout" - { - "index" "31233" - } - } - "The Killer Signature" - { - "scout" - { - "index" "31354" - } - } - "Foul Play" - { - "scout" - { - "index" "31414" - } - } - "Fresh Brewed Victory" - { - "soldier" - { - "index" "1113" - } - } - "Panzer Pants" - { - "soldier" - { - "index" "1196" - } - } - "Soldier's Requiem" - { - "soldier" - { - "index" "30673" - } - } - "The Fubar Fanfare" - { - "soldier" - { - "index" "30761" - } - } - "Rocket Jockey" - { - "soldier" - { - "index" "31155" - } - } - "The Profane Puppeteer" - { - "soldier" - { - "index" "31202" - } - } - "Star-Spangled Strategy!" - { - "soldier" - { - "index" "31347" - } - } - "Neck Snap" - { - "soldier" - { - "index" "31381" - } - } - "Party Trick" - { - "pyro" - { - "index" "1112" - } - } - "Pool Party" - { - "pyro" - { - "index" "30570" - } - } - "The Balloonibouncer" - { - "pyro" - { - "index" "30763" - } - } - "The Skating Scorcher" - { - "pyro" - { - "index" "30919" - } - } - "Scorcher's Solo" - { - "pyro" - { - "index" "31157" - } - } - "The Hot Wheeler" - { - "pyro" - { - "index" "31239" - } - } - "Roasty Toasty" - { - "pyro" - { - "index" "31322" - } - } - "Spent Well Spirits" - { - "demoman" - { - "index" "1114" - } - } - "Oblooterated" - { - "demoman" - { - "index" "1120" - } - } - "Bad Pipes" - { - "demoman" - { - "index" "30671" - } - } - "Scotsmann's Stagger" - { - "demoman" - { - "index" "30840" - } - } - "Pooped Deck" - { - "demoman" - { - "index" "31153" - } - } - "The Drunken Sailor" - { - "demoman" - { - "index" "31201" - } - } - "Drunk Mann's Cannon" - { - "demoman" - { - "index" "31291" - } - } - "Shanty Shipmate" - { - "demoman" - { - "index" "31292" - } - } - "Roar O'War" - { - "demoman" - { - "index" "31380" - } - } - "Table Tantrum" - { - "heavy" - { - "index" "1174" - } - } - "Boiling Point" - { - "heavy" - { - "index" "1175" - } - } - "Proletariat Posedown" - { - "heavy" - { - "index" "30616" - } - } - "Russian Arms Race" - { - "heavy" - { - "index" "30843" - } - } - "Soviet Strongarm" - { - "heavy" - { - "index" "30844" - } - } - "Bare Knuckle Beatdown" - { - "heavy" - { - "index" "31207" - } - } - "Russian Rubdown" - { - "heavy" - { - "index" "31320" - } - } - "The Road Rager" - { - "heavy" - { - "index" "31352" - } - } - "Rancho Relaxo" - { - "engineer" - { - "index" "1115" - } - } - "Bucking Bronco" - { - "engineer" - { - "index" "30618" - } - } - "Dueling Banjo" - { - "engineer" - { - "index" "30842" - } - } - "Jumping Jack" - { - "engineer" - { - "index" "30845" - } - } - "Texas Truckin" - { - "engineer" - { - "index" "31160" - } - } - "Texas Twirl 'Em" - { - "engineer" - { - "index" "31286" - } - } - "Meet the Medic" - { - "medic" - { - "index" "477" - } - } - "Results Are In" - { - "medic" - { - "index" "1109" - } - } - "Surgeon's Squeezebox" - { - "medic" - { - "index" "30918" - } - } - "Time Out Therapy" - { - "medic" - { - "index" "31154" - } - } - "The Mannbulance!" - { - "medic" - { - "index" "31203" - } - } - "Doctor's Defibrillators" - { - "medic" - { - "index" "31236" - } - } - "The Head Doctor" - { - "medic" - { - "index" "31349" - } - } - "Borrowed Bones" - { - "medic" - { - "index" "31382" - } - } - "I See You" - { - "sniper" - { - "index" "1116" - } - } - "The Killer Solo" - { - "sniper" - { - "index" "30609" - } - } - "Most Wanted" - { - "sniper" - { - "index" "30614" - } - } - "Didgeridrongo" - { - "sniper" - { - "index" "30839" - } - } - "Shooter's Stakeout" - { - "sniper" - { - "index" "31237" - } - } - "Buy A Life" - { - "spy" - { - "index" "1108" - } - } - "Box Trot" - { - "spy" - { - "index" "30615" - } - } - "Disco Fever" - { - "spy" - { - "index" "30762" - } - } - "Luxury Lounge" - { - "spy" - { - "index" "30922" - } - } - "The Travel Agent" - { - "spy" - { - "index" "31290" - } - } - "Tailored Terminal" - { - "spy" - { - "index" "31321" - } - } - "Tuefort Tango" - { - "spy" - { - "index" "31351" - } - } - "The Crypt Creeper" - { - "spy" - { - "index" "31289" - } - } - "High Five!" - { - "any" - { - "index" "167" - } - } - "Director's Vision" - { - "any" - { - "index" "438" - } - } - "Schadenfreude" - { - "any" - { - "index" "463" - } - } - "Shred Alert" - { - "any" - { - "index" "1015" - } - } - "Square Dance" - { - "any" - { - "index" "1106" - } - } - "Flippin' Awesome" - { - "any" - { - "index" "1107" - } - } - "Rock, Paper, Scissors" - { - "any" - { - "index" "1110" - } - } - "Skullcracker" - { - "any" - { - "index" "1111" - } - } - "Conga" - { - "any" - { - "index" "1118" - } - } - "Kazotsky Kick" - { - "any" - { - "index" "1157" - } - } - "Mannrobics" - { - "any" - { - "index" "1162" - } - } - "Victory Lap" - { - "any" - { - "index" "1172" - } - } - "Yeti Punch" - { - "any" - { - "index" "1182" - } - } - "Yeti Smash" - { - "any" - { - "index" "1183" - } - } - "Burstchester" - { - "any" - { - "index" "30621" - } - } - "Zoomin' Broom" - { - "any" - { - "index" "30672" - } - } - "Second Rate Sorcery" - { - "any" - { - "index" "30816" - } - } - "Fist Bump" - { - "any" - { - "index" "31162" - } - } - "The Scaredy-cat!" - { - "any" - { - "index" "31288" - } - } - "Killer Joke" - { - "any" - { - "index" "31348" - } - } - "Cheers!" - { - "any" - { - "index" "31412" - } - } - "Mourning Mercs" - { - "any" - { - "index" "31413" - } - } -} +"customtaunts" +{ + "Battin' a Thousand" + { + "scout" + { + "index" "1117" + } + } + "Deep Fried Desire" + { + "scout" + { + "index" "1119" + } + } + "The Carlton" + { + "scout" + { + "index" "1168" + } + } + "The Scooty Scoot" + { + "scout" + { + "index" "1197" + } + } + "The Boston Breakdance" + { + "scout" + { + "index" "30572" + } + } + "The Trackman's Touchdown" + { + "scout" + { + "index" "30917" + } + } + "The Bunnyhopper" + { + "scout" + { + "index" "30920" + } + } + "Runner's Rhythm" + { + "scout" + { + "index" "30921" + } + } + "Boston Boarder" + { + "scout" + { + "index" "31156" + } + } + "Spin-to-Win" + { + "scout" + { + "index" "31161" + } + } + "The Homerunner's Hobby" + { + "scout" + { + "index" "31233" + } + } + "The Killer Signature" + { + "scout" + { + "index" "31354" + } + } + "Foul Play" + { + "scout" + { + "index" "31414" + } + } + "Fresh Brewed Victory" + { + "soldier" + { + "index" "1113" + } + } + "Panzer Pants" + { + "soldier" + { + "index" "1196" + } + } + "Soldier's Requiem" + { + "soldier" + { + "index" "30673" + } + } + "The Fubar Fanfare" + { + "soldier" + { + "index" "30761" + } + } + "Rocket Jockey" + { + "soldier" + { + "index" "31155" + } + } + "The Profane Puppeteer" + { + "soldier" + { + "index" "31202" + } + } + "Star-Spangled Strategy!" + { + "soldier" + { + "index" "31347" + } + } + "Neck Snap" + { + "soldier" + { + "index" "31381" + } + } + "Party Trick" + { + "pyro" + { + "index" "1112" + } + } + "Pool Party" + { + "pyro" + { + "index" "30570" + } + } + "The Balloonibouncer" + { + "pyro" + { + "index" "30763" + } + } + "The Skating Scorcher" + { + "pyro" + { + "index" "30919" + } + } + "Scorcher's Solo" + { + "pyro" + { + "index" "31157" + } + } + "The Hot Wheeler" + { + "pyro" + { + "index" "31239" + } + } + "Roasty Toasty" + { + "pyro" + { + "index" "31322" + } + } + "Spent Well Spirits" + { + "demoman" + { + "index" "1114" + } + } + "Oblooterated" + { + "demoman" + { + "index" "1120" + } + } + "Bad Pipes" + { + "demoman" + { + "index" "30671" + } + } + "Scotsmann's Stagger" + { + "demoman" + { + "index" "30840" + } + } + "Pooped Deck" + { + "demoman" + { + "index" "31153" + } + } + "The Drunken Sailor" + { + "demoman" + { + "index" "31201" + } + } + "Drunk Mann's Cannon" + { + "demoman" + { + "index" "31291" + } + } + "Shanty Shipmate" + { + "demoman" + { + "index" "31292" + } + } + "Roar O'War" + { + "demoman" + { + "index" "31380" + } + } + "Table Tantrum" + { + "heavy" + { + "index" "1174" + } + } + "Boiling Point" + { + "heavy" + { + "index" "1175" + } + } + "Proletariat Posedown" + { + "heavy" + { + "index" "30616" + } + } + "Russian Arms Race" + { + "heavy" + { + "index" "30843" + } + } + "Soviet Strongarm" + { + "heavy" + { + "index" "30844" + } + } + "Bare Knuckle Beatdown" + { + "heavy" + { + "index" "31207" + } + } + "Russian Rubdown" + { + "heavy" + { + "index" "31320" + } + } + "The Road Rager" + { + "heavy" + { + "index" "31352" + } + } + "Rancho Relaxo" + { + "engineer" + { + "index" "1115" + } + } + "Bucking Bronco" + { + "engineer" + { + "index" "30618" + } + } + "Dueling Banjo" + { + "engineer" + { + "index" "30842" + } + } + "Jumping Jack" + { + "engineer" + { + "index" "30845" + } + } + "Texas Truckin" + { + "engineer" + { + "index" "31160" + } + } + "Texas Twirl 'Em" + { + "engineer" + { + "index" "31286" + } + } + "Meet the Medic" + { + "medic" + { + "index" "477" + } + } + "Results Are In" + { + "medic" + { + "index" "1109" + } + } + "Surgeon's Squeezebox" + { + "medic" + { + "index" "30918" + } + } + "Time Out Therapy" + { + "medic" + { + "index" "31154" + } + } + "The Mannbulance!" + { + "medic" + { + "index" "31203" + } + } + "Doctor's Defibrillators" + { + "medic" + { + "index" "31236" + } + } + "The Head Doctor" + { + "medic" + { + "index" "31349" + } + } + "Borrowed Bones" + { + "medic" + { + "index" "31382" + } + } + "I See You" + { + "sniper" + { + "index" "1116" + } + } + "The Killer Solo" + { + "sniper" + { + "index" "30609" + } + } + "Most Wanted" + { + "sniper" + { + "index" "30614" + } + } + "Didgeridrongo" + { + "sniper" + { + "index" "30839" + } + } + "Shooter's Stakeout" + { + "sniper" + { + "index" "31237" + } + } + "Buy A Life" + { + "spy" + { + "index" "1108" + } + } + "Box Trot" + { + "spy" + { + "index" "30615" + } + } + "Disco Fever" + { + "spy" + { + "index" "30762" + } + } + "Luxury Lounge" + { + "spy" + { + "index" "30922" + } + } + "The Travel Agent" + { + "spy" + { + "index" "31290" + } + } + "Tailored Terminal" + { + "spy" + { + "index" "31321" + } + } + "Tuefort Tango" + { + "spy" + { + "index" "31351" + } + } + "The Crypt Creeper" + { + "spy" + { + "index" "31289" + } + } + "High Five!" + { + "any" + { + "index" "167" + } + } + "Director's Vision" + { + "any" + { + "index" "438" + } + } + "Schadenfreude" + { + "any" + { + "index" "463" + } + } + "Shred Alert" + { + "any" + { + "index" "1015" + } + } + "Square Dance" + { + "any" + { + "index" "1106" + } + } + "Flippin' Awesome" + { + "any" + { + "index" "1107" + } + } + "Rock, Paper, Scissors" + { + "any" + { + "index" "1110" + } + } + "Skullcracker" + { + "any" + { + "index" "1111" + } + } + "Conga" + { + "any" + { + "index" "1118" + } + } + "Kazotsky Kick" + { + "any" + { + "index" "1157" + } + } + "Mannrobics" + { + "any" + { + "index" "1162" + } + } + "Victory Lap" + { + "any" + { + "index" "1172" + } + } + "Yeti Punch" + { + "any" + { + "index" "1182" + } + } + "Yeti Smash" + { + "any" + { + "index" "1183" + } + } + "Burstchester" + { + "any" + { + "index" "30621" + } + } + "Zoomin' Broom" + { + "any" + { + "index" "30672" + } + } + "Second Rate Sorcery" + { + "any" + { + "index" "30816" + } + } + "Fist Bump" + { + "any" + { + "index" "31162" + } + } + "The Scaredy-cat!" + { + "any" + { + "index" "31288" + } + } + "Killer Joke" + { + "any" + { + "index" "31348" + } + } + "Cheers!" + { + "any" + { + "index" "31412" + } + } + "Mourning Mercs" + { + "any" + { + "index" "31413" + } + } +} diff --git a/addons/sourcemod/scripting/tf2_custom_taunts.sp b/addons/sourcemod/scripting/tf2_custom_taunts.sp index 6e3bd24..ce2d03d 100644 --- a/addons/sourcemod/scripting/tf2_custom_taunts.sp +++ b/addons/sourcemod/scripting/tf2_custom_taunts.sp @@ -1,105 +1,207 @@ -#pragma semicolon 1 - #include +#include #include -#include #include +#include +#undef REQUIRE_EXTENSIONS +#undef REQUIRE_PLUGIN +#pragma semicolon 1 #pragma newdecls required -#define PLUGIN_VERSION "0.1.1" - -#define FAR_FUTURE 100000000.0 -#define MAX_SOUND_LENGTH 80 -#define MAX_MODEL_LENGTH 128 -#define MAX_MATERIAL_LENGTH 128 -#define MAX_ENTITY_LENGTH 48 -#define MAX_EFFECT_LENGTH 48 -#define MAX_ATTACHMENT_LENGTH 48 -#define MAX_ICON_LENGTH 48 -#define MAX_INFO_LENGTH 128 -#define HEX_OR_DEC_LENGTH 12 -#define MAX_ATTRIBUTE_LENGTH 256 -#define MAX_CONDITION_LENGTH 256 -#define MAX_CLASSNAME_LENGTH 64 -#define MAX_PLUGIN_LENGTH 64 -#define MAX_ITEM_LENGTH 48 -#define MAX_DESC_LENGTH 256 -#define MAX_TITLE_LENGTH 192 -#define MAX_NUM_LENGTH 5 -#define VOID_ARG -1 - -#define MAXTF2PLAYERS MAXPLAYERS+1 -#define TAUNT Client[client].Taunt -#define MODELS Taunt[Taunts].Models -#define SOUNDS Models[Taunts][Taunt[Taunts].Models].Sounds - -#define CONFIG_PATH "configs/customtaunts.cfg" - -#define MAXNAME 64 -#define MAXTAUNTS 128 -#define MAXSOUNDS 12 -#define MAXMODELS 12 +#define PLUGIN_VERSION "1" +#define PLUGIN_VERSION_REVISION "custom" +#define PLUGIN_VERSION_FULL PLUGIN_VERSION ... "." ... PLUGIN_VERSION_REVISION + +#define FILE_TAUNTS "configs/custom_taunts/taunts.cfg" enum struct SoundEnum { + char Match[PLATFORM_MAX_PATH]; char Replace[PLATFORM_MAX_PATH]; - char New[PLATFORM_MAX_PATH]; + + void Setup(KeyValues kv) + { + kv.GetSectionName(this.Match, sizeof(this.Match)); + kv.GetString("sound", this.Replace, sizeof(this.Replace), "vo/null.mp3"); + } } enum struct ModelEnum { + char Match[PLATFORM_MAX_PATH]; + ArrayList Sounds; + char Replace[PLATFORM_MAX_PATH]; char Sound[PLATFORM_MAX_PATH]; - char New[PLATFORM_MAX_PATH]; - bool IsSoundBlock; - bool IsMusicBlock; - bool IsHideWeapon; - bool IsHideHat; + float SoundVolume; + int SoundLevel; + bool BlockSound; + bool BlockMusic; + bool HideWeapon; + bool HideCosmetic; + bool BodyModel; float Duration; float Speed; - int Sounds; int Index; TFClassType Class; + + void Setup(KeyValues kv) + { + kv.GetSectionName(this.Match, sizeof(this.Match)); + ReplaceString(this.Match, sizeof(this.Match), "\\", "/"); + + this.Index = kv.GetNum("index"); + + bool all = StrEqual(this.Match, "any", false); + if(!all) + { + this.Class = TF2_GetClass(this.Match); + if(this.Class != TFClass_Unknown) + all = true; + } + + if(all) + { + this.Match[0] = 0; + this.Replace[0] = 0; + } + else + { + kv.GetString("model", this.Replace, sizeof(this.Replace)); + if(this.Replace[0]) + { + ReplaceString(this.Replace, sizeof(this.Replace), "\\", "/"); + + if(StrEqual(this.Replace, this.Match, false)) + { + this.Replace[0] = 0; + } + else if(FileExists(this.Replace, true)) + { + PrecacheModel(this.Replace); + } + else + { + LogError("'%s' has missing model '%s'.", this.Match, this.Replace); + this.Replace[0] = 0; + } + } + } + + this.BlockSound = view_as(kv.GetNum("block_sound", kv.GetNum("existing_sound_block"))); + this.BlockMusic = view_as(kv.GetNum("block_music", kv.GetNum("existing_music_block"))); + this.HideWeapon = view_as(kv.GetNum("hide_weapon")); + this.HideCosmetic = view_as(kv.GetNum("hide_hat")); + this.BodyModel = view_as(kv.GetNum("bodymodel")); + this.Speed = kv.GetFloat("speed", 1.0); + this.Duration = kv.GetFloat("duration"); + + kv.GetString("sound", this.Sound, sizeof(this.Sound)); + if(this.Sound[0]) + { + PrecacheSound(this.Sound); + this.SoundVolume = kv.GetFloat("volume", 1.0); + this.SoundLevel = kv.GetNum("soundlevel", SNDLEVEL_NORMAL); + } + + if(kv.GotoFirstSubKey()) + { + SoundEnum sound; + this.Sounds = new ArrayList(sizeof(SoundEnum)); + + do + { + sound.Setup(kv); + this.Sounds.PushArray(sound); + } + while(kv.GotoNextKey()); + kv.GoBack(); + } + else + { + this.Sounds = null; + } + } + void Delete() + { + delete this.Sounds; + } } enum struct TauntEnum { - char Name[MAXNAME]; - int Models; + char Name[64]; + ArrayList Models; + int Admin; -} + + bool Setup(KeyValues kv) + { + kv.GetString("admin", this.Name, sizeof(this.Name)); + this.Admin = this.Name[0] ? ReadFlagString(this.Name) : 0; -enum struct ClientEnum -{ - float EndAt; - int Taunt; - int Model; + kv.GetSectionName(this.Name, sizeof(this.Name)); + + ModelEnum model; + this.Models = new ArrayList(sizeof(ModelEnum)); + if(kv.GotoFirstSubKey()) + { + do + { + model.Setup(kv); + this.Models.PushArray(model); + } + while(kv.GotoNextKey()); + kv.GoBack(); + } + + if(this.Models.Length) + return true; + + delete this.Models; + LogError("'%s' has no model entries", this.Name); + return false; + } + void Delete() + { + ModelEnum model; + int length = this.Models.Length; + for(int i; i < length; i++) + { + this.Models.GetArray(i, model); + model.Delete(); + } + + delete this.Models; + } } -SoundEnum Sound[MAXTAUNTS][MAXMODELS][MAXSOUNDS]; -ModelEnum Models[MAXTAUNTS][MAXMODELS]; -TauntEnum Taunt[MAXTAUNTS]; -ClientEnum Client[MAXTF2PLAYERS]; Handle SDKPlayTaunt; -int Taunts; -bool Enabled; +Handle SDKEquipWearable; +ArrayList Taunts; +Handle TauntTimer[MAXPLAYERS+1]; +int CurrentTaunt[MAXPLAYERS+1] = {-1, ...}; +int CurrentModel[MAXPLAYERS+1]; +bool LastAnims[MAXPLAYERS+1]; +char LastModel[MAXPLAYERS+1][PLATFORM_MAX_PATH]; +int WearableModel[MAXPLAYERS+1] = {-1, ...}; +RenderFx LastRender[MAXPLAYERS+1]; public Plugin myinfo = { name = "TF2: Custom Taunts", author = "Batfoxkid", description = "Custom taunts for players to use", - version = PLUGIN_VERSION -}; - -// SourceMod Events + version = PLUGIN_VERSION_FULL, + url = "github.com/Batfoxkid/TF2-Custom-Taunts" +} public void OnPluginStart() { GameData gameData = new GameData("tf2.tauntem"); if(gameData == INVALID_HANDLE) - SetFailState("Failed to find gamedata/tf2.tauntem.txt."); + SetFailState("Could not find gamedata/tf2.tauntem.txt"); StartPrepSDKCall(SDKCall_Player); PrepSDKCall_SetFromConf(gameData, SDKConf_Signature, "CTFPlayer::PlayTauntSceneFromItem"); @@ -107,323 +209,198 @@ public void OnPluginStart() PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain); SDKPlayTaunt = EndPrepSDKCall(); if(SDKPlayTaunt == INVALID_HANDLE) - SetFailState("Failed to create call: CTFPlayer::PlayTauntSceneFromItem."); + SetFailState("Could not find CTFPlayer::PlayTauntSceneFromItem."); delete gameData; - CreateConVar("customtaunts_version", PLUGIN_VERSION, "Custom Taunts Plugin Version", FCVAR_NOTIFY|FCVAR_DONTRECORD); - - AddNormalSoundHook(HookSound); + gameData = new GameData("sm-tf2.games"); + + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetVirtual(gameData.GetOffset("RemoveWearable") - 1); + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); + SDKEquipWearable = EndPrepSDKCall(); + if(!SDKEquipWearable) + SetFailState("Could not find RemoveWearable"); + + delete gameData; RegConsoleCmd("sm_taunt", CommandMenu, "Open a menu of taunts"); RegConsoleCmd("sm_taunts", CommandList, "View a list of taunt ids"); - HookEvent("post_inventory_application", OnPlayerSpawn, EventHookMode_Pre); - HookEvent("player_death", OnPlayerDeath, EventHookMode_Post); - AddCommandListener(OnJoinClass, "joinclass"); - AddCommandListener(OnJoinClass, "join_class"); + AddCommandListener(OnTaunt, "taunt"); LoadTranslations("common.phrases"); + CreateConVar("customtaunts_version", PLUGIN_VERSION, "Custom Taunts Plugin Version", FCVAR_NOTIFY|FCVAR_DONTRECORD); + + AddNormalSoundHook(HookSound); HookUserMessage(GetUserMessageId("PlayerTauntSoundLoopStart"), HookTauntMessage, true); - - for(int i; i MaxClients) return Plugin_Continue; - EndTaunt(client, true); - - /* - static char arg[16]; - GetCmdArg(1, arg, sizeof(arg)); - TFClassType class = TF2_GetClass(arg); - if(class != TFClass_Unknown) - SetEntProp(client, Prop_Send, "m_iDesiredPlayerClass", class); - */ - - return Plugin_Continue; -} - -public void OnGameFrame() -{ - if(!Enabled) - return; - - float gameTime = GetGameTime(); - Enabled = false; - for(int client=1; client<=MaxClients; client++) + if(CurrentTaunt[entity] != -1) { - if(!IsValidClient(client) || !Client[client].EndAt) - continue; + static TauntEnum taunt; + Taunts.GetArray(CurrentTaunt[entity], taunt); - Enabled = true; - if(Client[client].EndAt < gameTime) - EndTaunt(client, true); - } -} + static ModelEnum model; + taunt.Models.GetArray(CurrentModel[entity], model); -public Action HookTauntMessage(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init) -{ - int byte = msg.ReadByte(); - - if (Client[byte].Taunt < 0 || Client[byte].Model < 0)return Plugin_Continue; - - if (Models[Client[byte].Taunt][Client[byte].Model].IsMusicBlock)return Plugin_Handled; + if(model.Sounds) + { + int length = model.Sounds.Length; + for(int i; i < length; i++) + { + static SoundEnum sound; + model.Sounds.GetArray(i, sound); - return Plugin_Continue; -} + if(StrContains(sample, sound.Match, false) == -1) + continue; -public Action HookSound(int clients[MAXPLAYERS], int &numClients, char sound[PLATFORM_MAX_PATH], int &client, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) -{ - if(!IsValidClient(client) || !TF2_IsPlayerInCondition(client, TFCond_Taunting) || Client[client].Taunt<0 || Client[client].Model<0) - return Plugin_Continue; + if(!sound.Replace[0]) + return Plugin_Stop; - for(int i; i 0) + + if(model.Duration > 0) { - TF2Attrib_SetByDefIndex(client, 201, 0.01); // Extends the taunt's lifetime + TF2Attrib_SetByDefIndex(client, 201, 0.01); } else { - TF2Attrib_SetByDefIndex(client, 201, Models[taunt][model].Speed); + TF2Attrib_SetByDefIndex(client, 201, model.Speed); } - + address += view_as
(offset); - if(!SDKCall(SDKPlayTaunt, client, address)) + if(SDKCall(SDKPlayTaunt, client, address)) { - AcceptEntityInput(entity, "Kill"); - TF2Attrib_SetByDefIndex(client, 201, 1.0); - return true; - } - AcceptEntityInput(entity, "Kill"); + if(model.BodyModel) + { + char buffer[PLATFORM_MAX_PATH]; + GetClientModel(client, buffer, sizeof(buffer)); + if(buffer[0]) + { + // Apply a fake playermodel + int playermodel = CreateEntityByName("tf_wearable"); + if(playermodel != -1) + { + SetEntProp(playermodel, Prop_Send, "m_nModelIndex", PrecacheModel(buffer)); + SetEntProp(playermodel, Prop_Send, "m_fEffects", 129); + SetEntProp(playermodel, Prop_Send, "m_iTeamNum", GetEntProp(client, Prop_Send, "m_iTeamNum")); + SetEntProp(playermodel, Prop_Send, "m_nSkin", GetEntProp(client, Prop_Send, "m_nSkin")); + SetEntProp(playermodel, Prop_Send, "m_usSolidFlags", 4); + SetEntityCollisionGroup(playermodel, 11); + SetEntProp(playermodel, Prop_Send, "m_bValidatedAttachedEntity", true); + DispatchSpawn(playermodel); + SetVariantString("!activator"); + ActivateEntity(playermodel); + SDKCall(SDKEquipWearable, client, playermodel); + WearableModel[client] = EntIndexToEntRef(playermodel); + } + } - if(Models[taunt][model].New[0]) - { - SetVariantString(Models[taunt][model].New); - AcceptEntityInput(client, "SetCustomModel"); - SetEntProp(client, Prop_Send, "m_bUseClassAnimations", 1); - } + LastRender[client] = GetEntityRenderFx(client); + SetEntityRenderFx(client, RENDERFX_FADE_FAST); + } + + if(model.Replace[0]) + { + // Replace the model, remember previous custom model + LastAnims[client] = view_as(GetEntProp(client, Prop_Send, "m_bUseClassAnimations")); + GetEntPropString(client, Prop_Send, "m_iszCustomModel", LastModel[client], sizeof(LastModel[])); - if(Models[taunt][model].Sound[0]) - EmitSoundToAll(Models[taunt][model].Sound, client); + SetVariantString(model.Replace); + AcceptEntityInput(client, "SetCustomModelWithClassAnimations"); + } - if(Models[taunt][model].Duration > 0) - TF2Attrib_SetByDefIndex(client, 201, Models[taunt][model].Speed); + if(model.Sound[0]) + EmitSoundToAll(model.Sound, client, SNDCHAN_STATIC, model.SoundLevel, _, model.SoundVolume); - if (Models[taunt][model].IsHideWeapon) - { - HideWeapons(client, false); - } + if(model.Duration > 0) + { + TauntTimer[client] = CreateTimer(model.Duration, Timer_EndTaunt, client); + TF2Attrib_SetByDefIndex(client, 201, model.Speed); + } - if (Models[taunt][model].IsHideHat) + if(model.HideWeapon) + ToggleWeapons(client, false); + + if(model.HideCosmetic) + ToggleCosmetics(client, false); + + CurrentTaunt[client] = index; + CurrentModel[client] = modelIndex; + } + else { - HideHat(client, false); + TF2Attrib_SetByDefIndex(client, 201, 1.0); } - Client[client].Taunt = taunt; - Client[client].Model = model; - - Client[client].EndAt = Models[taunt][model].Duration>0 ? Models[taunt][model].Duration+GetGameTime() : 0.0; - Enabled = true; - return false; + AcceptEntityInput(entity, "Kill"); + return true; } -// Menu Events - -public Action CommandMenu(int client, int args) +Action Timer_EndTaunt(Handle timer, int client) { - static char buffer[MAX_TARGET_LENGTH]; - if(!args && client) - { - if(!IsPlayerAlive(client) || TF2_IsPlayerInCondition(client, TFCond_Taunting)) - return Plugin_Handled; - - Menu menu = new Menu(CommandMenuH); - menu.SetTitle("Taunt Menu\n "); - - for(int i; i"); - return Plugin_Handled; - } + TauntEnum taunt; + Taunts.GetArray(CurrentTaunt[client], taunt); - GetCmdArg(1, buffer, sizeof(buffer)); - int taunt = StringToInt(buffer); - if((!taunt && !StrEqual(buffer, "0"))) - { - for(int i; i "); - return Plugin_Handled; - } +bool CanAccessTaunt(int client, int index) +{ + TauntEnum taunt; + Taunts.GetArray(index, taunt); - GetCmdArg(2, buffer, sizeof(buffer)); - int taunt = StringToInt(buffer); - if((!taunt && !StrEqual(buffer, "0"))) + if(taunt.Admin) { - for(int i; i=Taunts) - return false; + TauntEnum taunt; + int length = Taunts.Length; + for(int i; i < length; i++) + { + if(!CanAccessTaunt(client, i) || GetTaunt(client, i) == -1) + continue; - if(Taunt[taunt].Admin && !CheckCommandAccess(client, "customtaunts_all", Taunt[taunt].Admin, true)) - return false; + IntToString(i, buffer, sizeof(buffer)); + Taunts.GetArray(i, taunt); + menu.AddItem(buffer, taunt.Name); + } - return true; -} + if(!menu.ItemCount) + menu.AddItem("-1", "None", ITEMDRAW_DISABLED); -stock int CheckTauntModel(int client, int taunt) -{ - static char model[PLATFORM_MAX_PATH]; - GetClientModel(client, model, PLATFORM_MAX_PATH); - TFClassType class = TF2_GetPlayerClass(client); - for(int i; i= 0 && index < length && CanAccessTaunt(client, index) && GetTaunt(client, index) != -1) + { + if(IsPlayerAlive(client) && !TF2_IsPlayerInCondition(client, TFCond_Taunting)) + StartTaunt(client, index); + } + else + { + ReplyToCommand(client, "[SM] Invalid taunt for this class"); + } - continue; + return Plugin_Handled; + } } + case 2: + { + if(client == 0 || CheckCommandAccess(client, "customtaunts_targetting", ADMFLAG_CHEATS)) + { + GetCmdArg(1, buffer, sizeof(buffer)); + int index = StringToInt(buffer); - if(!Models[taunt][i].Replace[0]) - return i; + int length = Taunts.Length; + if(index >= 0 && index < length && CanAccessTaunt(client, index)) + { + GetCmdArg(2, buffer, sizeof(buffer)); + char targetName[MAX_TARGET_LENGTH]; + int targets[MAXPLAYERS], matches; + bool tn_is_ml; + + if((matches=ProcessTargetString(buffer, client, targets, sizeof(targets), COMMAND_FILTER_ALIVE, targetName, sizeof(targetName), tn_is_ml)) < 1) + { + ReplyToTargetError(client, matches); + return Plugin_Handled; + } + + bool failed = true; + for(int target; target < matches; target++) + { + if(StartTaunt(targets[target], index)) + failed = false; + } + + if(failed) + { + ReplyToCommand(client, "[SM] Invalid taunt for this class"); + } + else if(tn_is_ml) + { + ShowActivity2(client, "[SM] ", "Forced taunt on %t", targetName); + } + else + { + ShowActivity2(client, "[SM] ", "Forced taunt on %s", targetName); + } + } + else + { + ReplyToCommand(client, "[SM] Invalid taunt id"); + } + } + else + { + ReplyToCommand(client, "[SM] Usage: sm_taunt [tauntid]"); + } - if(StrEqual(Models[taunt][i].Replace, model, false)) - return i; + return Plugin_Handled; + } + default: + { + if(client != 0) + { + if(CheckCommandAccess(client, "customtaunts_targetting", ADMFLAG_CHEATS)) + { + ReplyToCommand(client, "[SM] Usage: sm_taunt [tauntid] [target]"); + } + else + { + ReplyToCommand(client, "[SM] Usage: sm_taunt [tauntid]"); + } + return Plugin_Handled; + } + } } - return -1; -} -stock bool IsValidClient(int client) -{ - if(client<=0 || client>MaxClients) - return false; - - if(!IsClientInGame(client)) - return false; - - if(GetEntProp(client, Prop_Send, "m_bIsCoaching")) - return false; - - if(IsClientSourceTV(client) || IsClientReplay(client)) - return false; - - return true; + ReplyToCommand(client, "[SM] Usage: sm_taunt "); + return Plugin_Handled; } -stock void HideWeapons(int client, bool unhide = false) +int CommandMenuH(Menu menu, MenuAction action, int client, int choice) { - HideWeaponWearables(client, unhide); - int m_hMyWeapons = FindSendPropInfo("CTFPlayer", "m_hMyWeapons"); - - char classname[64]; - for (int i = 0, weapon; i < 47; i += 4) + switch(action) { - weapon = GetEntDataEnt2(client, m_hMyWeapons + i); - - if (weapon > MaxClients && IsValidEdict(weapon) && GetEdictClassname(weapon, classname, sizeof(classname)) && StrContains(classname, "weapon") != -1) + case MenuAction_End: { - SetEntityRenderMode(weapon, (unhide ? RENDER_NORMAL : RENDER_ENVIRONMENTAL)); - SetEntityRenderColor(weapon, 255, 255, 255, (unhide ? 255 : 5)); + delete menu; } - } -} -stock void HideWeaponWearables(int client, bool unhide = false) -{ - int edict = MaxClients + 1; - - char netclass[32]; - while ((edict = FindEntityByClassname(edict, "tf_wearable")) != -1) - { - if (GetEntityNetClass(edict, netclass, sizeof(netclass)) && strcmp(netclass, "CTFWearable") == 0) + case MenuAction_Select: { - int idx = GetEntProp(edict, Prop_Send, "m_iItemDefinitionIndex"); - if (idx != 57 && idx != 133 && idx != 231 && idx != 444 && idx != 405 && idx != 608 && idx != 642)continue; - if (GetEntPropEnt(edict, Prop_Send, "m_hOwnerEntity") == client) - { - SetEntityRenderMode(edict, (unhide ? RENDER_NORMAL : RENDER_ENVIRONMENTAL)); - SetEntityRenderColor(edict, 255, 255, 255, (unhide ? 255 : 0)); - } + char buffer[16]; + menu.GetItem(choice, buffer, sizeof(buffer)); + + int index = StringToInt(buffer); + FakeClientCommand(client, "sm_taunt %d", index); } } + return 0; } -stock void HideHat(int client, bool unhide = false) +Action CommandList(int client, int args) { - int edict = MaxClients + 1; - char netclass[32]; - - while ((edict = FindEntityByClassname(edict, "tf_wearable")) != -1) - { - - if (GetEntityNetClass(edict, netclass, sizeof(netclass)) && strcmp(netclass, "CTFWearable") == 0) - { - int idx = GetEntProp(edict, Prop_Send, "m_iItemDefinitionIndex"); - if (idx != 57 && idx != 133 && idx != 231 && idx != 444 && idx != 405 && idx != 608 && idx != 642 && GetEntPropEnt(edict, Prop_Send, "m_hOwnerEntity") == client) - { - SetEntityRenderMode(edict, (unhide ? RENDER_NORMAL : RENDER_ENVIRONMENTAL)); - SetEntityRenderColor(edict, 255, 255, 255, (unhide ? 255 : 0)); - } - } - } - edict = MaxClients + 1; - while ((edict = FindEntityByClassname(edict, "tf_powerup_bottle")) != -1) + TauntEnum taunt; + int length = Taunts.Length; + for(int i; i < length; i++) { - if (GetEntityNetClass(edict, netclass, sizeof(netclass)) && strcmp(netclass, "CTFPowerupBottle") == 0) + if(CanAccessTaunt(client, i)) { - int idx = GetEntProp(edict, Prop_Send, "m_iItemDefinitionIndex"); - if (idx != 57 && idx != 133 && idx != 231 && idx != 444 && idx != 405 && idx != 608 && idx != 642 && GetEntPropEnt(edict, Prop_Send, "m_hOwnerEntity") == client) - { - SetEntityRenderMode(edict, (unhide ? RENDER_NORMAL : RENDER_ENVIRONMENTAL)); - SetEntityRenderColor(edict, 255, 255, 255, (unhide ? 255 : 0)); - } + Taunts.GetArray(i, taunt); + PrintToConsole(client, "#%d - %s", i, taunt.Name); } } + + if(GetCmdReplySource() == SM_REPLY_TO_CHAT) + ReplyToCommand(client, "[SM] %t", "See console for output"); + + return Plugin_Handled; } \ No newline at end of file diff --git a/scripts/date.sh b/scripts/date.sh deleted file mode 100644 index a43eb04..0000000 --- a/scripts/date.sh +++ /dev/null @@ -1,5 +0,0 @@ -SEC=$(date "+%s") -MIN=$(expr $((SEC)) / 60) -echo "DATA_VERSION<> $GITHUB_ENV -echo $MIN >> $GITHUB_ENV -echo 'EOF' >> $GITHUB_ENV \ No newline at end of file diff --git a/scripts/install.sh b/scripts/install.sh deleted file mode 100644 index 933390f..0000000 --- a/scripts/install.sh +++ /dev/null @@ -1,11 +0,0 @@ -mkdir build -cd build - -wget --input-file=http://sourcemod.net/smdrop/$SM_VERSION/sourcemod-latest-linux -tar -xzf $(cat sourcemod-latest-linux) - -cp -r ../addons/sourcemod/scripting addons/sourcemod -cd addons/sourcemod/scripting - -wget "https://raw.githubusercontent.com/asherkin/TF2Items/master/pawn/tf2items.inc" -O include/tf2items.inc -wget "https://raw.githubusercontent.com/FlaminSarge/tf2attributes/master/scripting/include/tf2attributes.inc" -O include/tf2attributes.inc diff --git a/scripts/package.sh b/scripts/package.sh deleted file mode 100644 index 0c955a9..0000000 --- a/scripts/package.sh +++ /dev/null @@ -1,9 +0,0 @@ -cd build - -mkdir -p package/addons/sourcemod/plugins -mkdir -p package/addons/sourcemod/gamedata -mkdir -p package/addons/sourcemod/configs - -cp -r addons/sourcemod/plugins/tf2_custom_taunts.smx package/addons/sourcemod/plugins -cp -r ../addons/sourcemod/gamedata/tf2.tauntem.txt package/addons/sourcemod/gamedata -cp -r ../addons/sourcemod/configs/customtaunts.cfg package/addons/sourcemod/configs \ No newline at end of file