diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 959a9ab2..effc80c2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,33 +2,38 @@ name: Compile and release on: push: - branches: - - master + pull_request: + workflow_dispatch: env: PLUGIN_NAME: chaos SCRIPTS_PATH: addons/sourcemod/scripting + SM_VERSION: '1.12.x' jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - sm-version: [ '1.12.x' ] + permissions: + contents: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - name: Prepare include directory - run: mkdir -p ${{ env.SCRIPTS_PATH }}/include + - name: Cache include files + id: cache-includes + uses: actions/cache@v5 + with: + path: ${{ env.SCRIPTS_PATH }}/include + key: includes-${{ hashFiles('.github/workflows/main.yml') }} - name: Download include files + if: steps.cache-includes.outputs.cache-hit != 'true' run: | + set -e includes=( "https://raw.githubusercontent.com/DoctorMcKay/sourcemod-plugins/master/scripting/include/morecolors.inc" "https://raw.githubusercontent.com/FlaminSarge/tf2attributes/refs/heads/master/scripting/include/tf2attributes.inc" - "https://raw.githubusercontent.com/nosoop/SMExt-SourceScramble/refs/heads/master/scripting/include/sourcescramble.inc" "https://raw.githubusercontent.com/asherkin/TF2Items/refs/heads/master/pawn/tf2items.inc" "https://raw.githubusercontent.com/nosoop/SM-TFUtils/refs/heads/master/scripting/include/tf2utils.inc" "https://raw.githubusercontent.com/FortyTwoFortyTwo/VScript/refs/heads/main/scripting/include/vscript.inc" @@ -39,14 +44,14 @@ jobs: for url in "${includes[@]}"; do echo "Downloading $url" - curl -sSL -O --output-dir "${{ env.SCRIPTS_PATH }}/include" "$url" + curl --fail -sSL -O --output-dir "${{ env.SCRIPTS_PATH }}/include" "$url" done - - name: Setup SourcePawn compiler ${{ matrix.sm-version }} + - name: Setup SourcePawn compiler id: setup_sp uses: rumblefrog/setup-sp@master with: - version: ${{ matrix.sm-version }} + version: ${{ env.SM_VERSION }} version-file: ${{ env.SCRIPTS_PATH }}/${{ env.PLUGIN_NAME }}.sp define-name: PLUGIN_VERSION @@ -54,32 +59,14 @@ jobs: run: | mkdir -p ../plugins spcomp -v2 -E -i "include" -o "../plugins/${{ env.PLUGIN_NAME }}.smx" "${{ env.PLUGIN_NAME }}.sp" - echo "=== OUT FILES ===" - ls ../plugins - echo "=== VERSION ===" - echo ${{ steps.setup_sp.outputs.plugin-version }} working-directory: ${{ env.SCRIPTS_PATH }} - - name: Install zip utility - uses: montudor/action-zip@v1 - - name: Zip output - run: | - zip -qq -r ${{ github.event.repository.name }}.zip addons/sourcemod scripts - - - name: List build outputs - run: | - echo "::group::Directory contents" - ls -R - echo "::endgroup::" - echo "::group::Zip contents" - unzip -l ${{ github.event.repository.name }}.zip - echo "::endgroup::" + run: zip -qq -r ${{ github.event.repository.name }}.zip addons materials scripts shaders - name: Create GitHub release + if: github.ref_name == 'master' uses: ncipollo/release-action@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ steps.setup_sp.outputs.plugin-version }} diff --git a/README.MD b/README.MD index 77d26bf8..3c8216cc 100644 --- a/README.MD +++ b/README.MD @@ -1,14 +1,17 @@ # TF2 Chaos Mod -**As seen on [TFConnect 2023](https://tfconnect.org)!** +TF2 Chaos Mod Logo -TF2 Chaos Mod Logo +Logo created by [RatX](https://steamcommunity.com/profiles/76561198058574997) + +--- **Welcome to the official home of the TF2 Chaos Mod!** -Every couple seconds, one of over 150 different effects is activated. Some effects are good and empower certain classes, but others can be bad, hindering gameplay. +Every couple of seconds, one of over 150 different effects is activated. Some effects are good and empower certain classes, but others can be bad, hindering gameplay. -You can easily add your own effects using the provided Squirrel (VScript) or SourcePawn API. See [here](https://github.com/Mikusch/ChaosModTF2/wiki) for more information on effect creation. +You can easily add your own effects using the provided Squirrel (VScript) or SourcePawn API. +For more information on effect creation, see [here](https://github.com/Mikusch/ChaosModTF2/wiki). ## Requirements @@ -18,12 +21,12 @@ You can easily add your own effects using the provided Squirrel (VScript) or Sou * [nosoop/SM-TFUtils](https://github.com/nosoop/SM-TFUtils) * [nosoop/SM-TFEconData](https://github.com/nosoop/SM-TFEconData) * [FortyTwoFortyTwo/VScript](https://github.com/FortyTwoFortyTwo/VScript) -* [DoctorMcKay/MoreColors](https://github.com/DoctorMcKay/sourcemod-plugins/blob/master/scripting/include/morecolors.inc) (compile +* [DoctorMcKay/MoreColors](https://github.com/DoctorMcKay/sourcemod-plugins/blob/master/scripting/include/morecolors.inc) ( + compile only) ## Special Thanks -* [ficool2](https://github.com/ficool2) - For assistance in setting up the VScript API -* [RatX](https://steamcommunity.com/id/ratx15) - For creating the Chaos Mod logo -* [Red Sun Over Paradise](https://redsun.tf) - For playtesting and giving feedback -* [TFConnect](https://tfconnect.org) - For featuring Chaos Mod in TFConnect 2023, supporting SpecialEffect's mission to aid & empower gamers with physical disabilities by helping them play video games \ No newline at end of file +* [ficool2](https://github.com/ficool2) – For assistance in setting up the VScript API +* [Red Sun Over Paradise](https://redsun.tf) – For playtesting and giving feedback +* [TFConnect](https://tfconnect.org) – For featuring Chaos Mod on TFConnect, a gaming charity fundraising event supporting SpecialEffect's mission to aid & empower gamers with physical disabilities by helping them play video games diff --git a/addons/sourcemod/configs/chaos/effects.cfg b/addons/sourcemod/configs/chaos/effects.cfg index 4492d99c..dc2e0b0b 100644 --- a/addons/sourcemod/configs/chaos/effects.cfg +++ b/addons/sourcemod/configs/chaos/effects.cfg @@ -1,8 +1,8 @@ "effects" { - "0" + "invert_accel" { - "name" "#Chaos_Effect_InvertAcceleration" + "name" "#Chaos_Effect_InvertAccel" "duration" "60" "effect_class" "InvertConVar" "data" @@ -10,49 +10,52 @@ "convar" "sv_accelerate" } } - "1" + "low_friction" { "name" "#Chaos_Effect_LowFriction" - "duration" "120" + "duration" "90" "effect_class" "SetConVar" "start_sound" "General.banana_slip" "data" { - "convar" "sv_friction" - "value" "0.4" + "convars" + { + "sv_friction" "0.4" + } } } - "2" + "slow_motion" { "name" "#Chaos_Effect_SlowMotion" "duration" "30" - "effect_class" "SetConVar" + "effect_class" "TimeScale" "start_sound" "replay/enterperformancemode.wav" "end_sound" "replay/exitperformancemode.wav" + "tags" "sound" "data" { - "convar" "host_timescale" - "value" "0.5" - "replicate_cheats" "1" + "timescale" "0.5" } } - "3" + "kill_random_player" { "name" "#Chaos_Effect_KillRandomPlayer" "effect_class" "KillRandomPlayer" } - "4" + "friendly_fire" { "name" "#Chaos_Effect_FriendlyFire" "duration" "30" "effect_class" "SetConVar" "data" { - "convar" "mp_friendlyfire" - "value" "1" + "convars" + { + "mp_friendlyfire" "1" + } } } - "5" + "invert_gravity" { "name" "#Chaos_Effect_InvertGravity" "duration" "30" @@ -63,39 +66,39 @@ "convar" "sv_gravity" } } - "6" + "truce" { "name" "#Chaos_Effect_Truce" - "duration" "45" + "duration" "30" "effect_class" "Truce" } - "7" + "no_transmit" { - "name" "#Chaos_Effect_WhereDidEverythingGo" - "duration" "45" - "effect_class" "WhereDidEverythingGo" + "name" "#Chaos_Effect_NoTransmit" + "duration" "30" + "effect_class" "NoTransmit" } - "8" + "eternal_screams" { "name" "#Chaos_Effect_EternalScreams" "duration" "60" - "script_file" "eternalscreams" + "script_file" "eternal_screams" } - "9" + "low_hp" { - "name" "#Chaos_Effect_1HP" - "effect_class" "SetHealth" - "duration" "60" + "name" "#Chaos_Effect_LowHP" + "effect_class" "SetMaxHealth" + "duration" "30" "data" { "health" "1" } } - "10" + "no_torso" { - "name" "#Chaos_Effect_ShortMercs" + "name" "#Chaos_Effect_NoTorso" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -104,45 +107,59 @@ } } } - "11" + "big_hands" + { + "name" "#Chaos_Effect_BigHands" + "duration" "120" + "effect_class" "AddAttribute" + "start_sound" "vo/taunts/heavy_taunts15.mp3" + "data" + { + "attributes" + { + "hand scale" "3" + } + } + } + "watermark" { "name" "#Chaos_Effect_Watermark" "duration" "180" "effect_class" "Watermark" } - "12" + "thriller" { - "name" "#Chaos_Effect_ThrillerDance" - "duration" "12" + "name" "#Chaos_Effect_Thriller" + "duration" "10" "script_file" "thriller" "start_sound" "Halloween.MerasmusWheelDance" } - "13" + "show_scoreboard" { "name" "#Chaos_Effect_ShowScoreboard" "effect_class" "ShowScoreboard" } - "14" + "high_fov" { - "name" "#Chaos_Effect_HighFOV" + "name" "#Chaos_Effect_HighFov" "duration" "60" "effect_class" "SetFOV" "data" { - "fov" "160" + "fov" "135" } } - "15" + "low_fov" { - "name" "#Chaos_Effect_LowFOV" + "name" "#Chaos_Effect_LowFov" "duration" "60" "effect_class" "SetFOV" "data" { - "fov" "30" + "fov" "45" } } - "16" + "crits" { "name" "#Chaos_Effect_Crits" "duration" "45" @@ -150,10 +167,13 @@ "start_sound" "Halloween.MerasmusWheelCrits" "data" { - "condition" "56" + "conditions" + { + "TF_COND_CRITBOOSTED_CARD_EFFECT" "56" + } } } - "17" + "ubercharge" { "name" "#Chaos_Effect_Ubercharge" "duration" "30" @@ -161,10 +181,13 @@ "start_sound" "Halloween.MerasmusWheelUber" "data" { - "condition" "57" + "conditions" + { + "TF_COND_INVULNERABLE_CARD_EFFECT" "57" + } } } - "18" + "bumper_cars" { "name" "#Chaos_Effect_BumperCars" "duration" "30" @@ -173,39 +196,44 @@ "tags" "camera" "data" { - "condition" "82" + "conditions" + { + "TF_COND_HALLOWEEN_KART" "82" + } } } - "19" + "view_roll" { "name" "#Chaos_Effect_ViewRoll" - "duration" "150" + "duration" "120" "effect_class" "SetConVar" "tags" "camera" "data" { - "convar" "sv_rollangle" - "value" "5" + "convars" + { + "sv_rollangle" "5" + } } } - "20" + "silence" { "name" "#Chaos_Effect_Silence" "duration" "90" "effect_class" "Silence" "tags" "sound" } - "21" + "remove_wearables" { "name" "#Chaos_Effect_RemoveWearables" - "script_file" "removewearables" + "script_file" "remove_wearables" "duration" "150" } - "22" + "squid_footsteps" { "name" "#Chaos_Effect_SquidFootsteps" - "duration" "150" - "effect_class" "SetAttribute" + "duration" "120" + "effect_class" "AddAttribute" "data" { "attributes" @@ -214,11 +242,11 @@ } } } - "23" + "jingle_footsteps" { - "name" "#Chaos_Effect_JingleAllTheWay" - "duration" "150" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_JingleFootsteps" + "duration" "120" + "effect_class" "AddAttribute" "data" { "attributes" @@ -227,10 +255,10 @@ } } } - "24" + "skeleton_models" { "name" "#Chaos_Effect_SkeletonModels" - "duration" "150" + "duration" "120" "effect_class" "SetCustomModel" "start_sound" "Halloween.spell_skeleton_horde_cast" "tags" "playermodel" @@ -239,48 +267,52 @@ "model" "models/bots/skeleton_sniper/skeleton_sniper.mdl" } } - "25" + "lag" { "name" "#Chaos_Effect_Lag" "duration" "45" "effect_class" "SetConVar" "data" { - "convar" "net_fakelag" - "value" "500" + "convars" + { + "net_fakelag" "500" + } } } - "26" + "solid_teammates" { "name" "#Chaos_Effect_SolidTeammates" "duration" "45" "effect_class" "SetConVar" "data" { - "convar" "tf_avoidteammates" - "value" "0" + "convars" + { + "tf_avoidteammates" "0" + } } } - "27" + "midas_touch" { "name" "#Chaos_Effect_MidasTouch" "duration" "150" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "start_sound" "vo/engineer_goldenwrenchkill02.mp3" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "turn to gold" "1" } } } - "28" + "speed_limit" { "name" "#Chaos_Effect_SpeedLimit" - "duration" "90" - "effect_class" "SetAttribute" + "duration" "60" + "effect_class" "AddAttribute" "data" { "attributes" @@ -289,11 +321,11 @@ } } } - "29" + "super_speed" { "name" "#Chaos_Effect_SuperSpeed" "duration" "90" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "start_sound" "Halloween.MerasmusWheelSuperSpeed" "data" { @@ -303,70 +335,84 @@ } } } - "30" + "high_hp" { - "name" "#Chaos_Effect_1000HP" - "effect_class" "SetHealth" - "duration" "120" + "name" "#Chaos_Effect_HighHP" + "effect_class" "SetMaxHealth" + "duration" "90" "data" { "health" "1000" } } - "31" + "third_person" { "name" "#Chaos_Effect_ThirdPerson" "duration" "120" - "script_file" "thirdperson" + "script_file" "third_person" "tags" "camera" } - "32" + "swap_positions" { "name" "#Chaos_Effect_SwapPositions" - "script_file" "swappositions" + "script_file" "swap_positions" "start_sound" "Halloween.spell_teleport" } - "33" + "respawn_dead" { - "name" "#Chaos_Effect_RespawnAllDead" - "script_file" "respawnalldead" + "name" "#Chaos_Effect_RespawnDead" + "script_file" "respawn_dead" } - "34" + "screenfade_black" { - "name" "#Chaos_Effect_Blindness" - "duration" "15" + "name" "#Chaos_Effect_ScreenFadeBlack" + "duration" "10" "effect_class" "ScreenFade" "data" { "color" "0 0 0 255" } } - "35" + "greyscale" { "name" "#Chaos_Effect_Greyscale" "duration" "60" "effect_class" "ScreenOverlay" "data" { - "material" "debug/yuv" + "material" "chaos/shaders/greyscale" + "shader" "chaos_greyscale_ps20b" } } - "36" + "deep_fried" { - "name" "#Chaos_Effect_PipBoyOverlay" + "name" "#Chaos_Effect_DeepFried" "duration" "60" "effect_class" "ScreenOverlay" - "start_sound" "CYOA.ObjectivePanelExpand" "data" { - "material" "vgui/pipboy_overlay" + "material" "chaos/shaders/deepfried" + "shader" "chaos_deepfried_ps20b" + "dsp" "38" } } - "37" + "blur" { - "name" "#Chaos_Effect_NoJumpingAndCrouching" - "duration" "120" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_Blur" + "duration" "60" + "effect_class" "ScreenOverlay" + "start_sound" "vo/scout_award08.mp3" + "data" + { + "material" "chaos/shaders/blur" + "shader" "chaos_blur_ps20b" + } + } + "no_jump_duck" + { + "name" "#Chaos_Effect_NoJumpDuck" + "duration" "90" + "effect_class" "AddAttribute" "tags" "jumping" "data" { @@ -377,54 +423,61 @@ } } } - "38" + "heavy_overlay" { - "name" "#Chaos_Effect_CompanionHeavy" + "name" "#Chaos_Effect_HeavyOverlay" "duration" "60" "effect_class" "ScreenOverlay" + "start_sound" "vo/heavy_cheers08.mp3" "data" { "material" "console/characters/heavy" } } - "39" + "ghosts" { "name" "#Chaos_Effect_Ghosts" - "duration" "15" + "duration" "20" "effect_class" "AddCond" "start_sound" "Halloween.MerasmusWheelGhosts" "data" { - "condition" "77" + "conditions" + { + "TF_COND_HALLOWEEN_GHOST_MODE" "77" + } } } - "40" + "bleed" { "name" "#Chaos_Effect_Bleed" "script_file" "bleed" "start_sound" "Halloween.MerasmusWheelBleed" + "data" "{ endless = true }" } - "41" + "shuffle_classes" { "name" "#Chaos_Effect_ShuffleClasses" - "script_file" "shuffleclasses" + "script_file" "shuffle_classes" } - "43" + "bouncy" { "name" "#Chaos_Effect_Bouncy" "duration" "120" "effect_class" "SetConVar" "data" { - "convar" "sv_bounce" - "value" "9999" + "convars" + { + "sv_bounce" "9999" + } } } - "44" + "fly_voices" { "name" "#Chaos_Effect_FlyVoices" - "duration" "150" - "effect_class" "SetAttribute" + "duration" "120" + "effect_class" "AddAttribute" "data" { "attributes" @@ -433,11 +486,11 @@ } } } - "45" + "pyrovision" { "name" "#Chaos_Effect_Pyrovision" "duration" "180" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "start_sound" "pyro.music_backpack" "data" { @@ -447,77 +500,86 @@ } } } - "46" + "taunt" { "name" "#Chaos_Effect_Taunt" "script_file" "taunt" } - "47" + "randomize_skybox" { "name" "#Chaos_Effect_RandomizeSkybox" - "script_file" "randomizeskybox" + "script_file" "randomize_skybox" } - "48" + "noclip" { "name" "#Chaos_Effect_Noclip" - "duration" "15" + "duration" "20" "script_file" "noclip" "start_sound" "vo/taunts/spy/spy_taunt_flip_fun_12.mp3" } - "49" + "hide_hud" { - "name" "#Chaos_Effect_HideHUD" - "duration" "120" - "script_file" "hidehud" + "name" "#Chaos_Effect_HideHud" + "duration" "90" + "script_file" "hide_hud" } - "50" + "remove_random_entity" { "name" "#Chaos_Effect_RemoveRandomEntity" "effect_class" "RemoveRandomEntity" } - "51" + "remove_pickups" { - "name" "#Chaos_Effect_RemoveHealthAndAmmo" - "effect_class" "RemoveHealthAndAmmo" + "name" "#Chaos_Effect_RemovePickups" + "effect_class" "RemovePickups" } - "52" + "bullet_immunity" { - "name" "#Chaos_Effect_BulletImmune" + "name" "#Chaos_Effect_BulletImmunity" "duration" "45" "effect_class" "AddCond" "start_sound" "player/invuln_on_vaccinator.wav" "data" { - "condition" "67" + "conditions" + { + "TF_COND_BULLET_IMMUNE" "67" + } } } - "53" + "blast_immunity" { - "name" "#Chaos_Effect_BlastImmune" + "name" "#Chaos_Effect_BlastImmunity" "duration" "45" "effect_class" "AddCond" "start_sound" "player/invuln_on_vaccinator.wav" "data" { - "condition" "68" + "conditions" + { + "TF_COND_BLAST_IMMUNE" "68" + } } } - "54" + "fire_immunity" { - "name" "#Chaos_Effect_FireImmune" + "name" "#Chaos_Effect_FireImmunity" "duration" "45" "effect_class" "AddCond" "start_sound" "player/invuln_on_vaccinator.wav" "data" { - "condition" "69" + "conditions" + { + "TF_COND_FIRE_IMMUNE" "69" + } } } - "55" + "fast_capture" { - "name" "#Chaos_Effect_10xCaptureRate" + "name" "#Chaos_Effect_FastCapture" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -526,11 +588,11 @@ } } } - "56" + "laugh_on_kill" { "name" "#Chaos_Effect_LaughOnKill" "duration" "90" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -539,25 +601,25 @@ } } } - "57" + "hit_self_on_miss" { "name" "#Chaos_Effect_HitSelfOnMiss" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "hit self on miss" "1" } } } - "58" + "knockback_on_hit" { - "name" "#Chaos_Effect_DamageCausesAirblast" + "name" "#Chaos_Effect_KnockbackOnHit" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -566,62 +628,75 @@ } } } - "59" + "random_crits_on" { - "name" "#Chaos_Effect_EnableRandomCrits" + "name" "#Chaos_Effect_RandomCritsOn" "effect_class" "SetConVar" "data" { - "convar" "tf_weapon_criticals" - "value" "1" + "convars" + { + "tf_weapon_criticals" "1" + } } } - "60" + "random_crits_off" { - "name" "#Chaos_Effect_DisableRandomCrits" + "name" "#Chaos_Effect_RandomCritsOff" "effect_class" "SetConVar" "data" { - "convar" "tf_weapon_criticals" - "value" "0" + "convars" + { + "tf_weapon_criticals" "0" + } } } - "61" + "stop" { - "name" "#Chaos_Effect_StopAndStare" + "name" "#Chaos_Effect_Stop" "duration" "10" "effect_class" "AddCond" "data" { - "condition" "87" + "conditions" + { + "TF_COND_FREEZE_INPUT" "87" + "TF_COND_GRAPPLED_TO_PLAYER" "120" + } } } - "62" + "grappling_hooks" { "name" "#Chaos_Effect_GrapplingHooks" - "duration" "120" + "duration" "150" "effect_class" "SetConVar" "data" { - "convar" "tf_grapplinghook_enable" - "value" "1" + "convars" + { + "tf_grapplinghook_enable" "1" + } } } - "63" + "stealthed" { "name" "#Chaos_Effect_Stealthed" "duration" "30" "effect_class" "AddCond" "data" { - "condition" "66" + "conditions" + { + "TF_COND_STEALTHED_USER_BUFF" "66" + } } } - "64" + "big_head" { "name" "#Chaos_Effect_BigHead" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "start_sound" "Halloween.MerasmusWheelBigHead" "data" { @@ -632,39 +707,43 @@ } } } - "65" + "extreme_fog" { "name" "#Chaos_Effect_ExtremeFog" "duration" "60" - "script_file" "extremefog" + "script_file" "extreme_fog" } - "66" + "cheap_objects" { "name" "#Chaos_Effect_CheapObjects" - "duration" "60" + "duration" "120" "effect_class" "SetConVar" "data" { - "convar" "tf_cheapobjects" - "value" "1" + "convars" + { + "tf_cheapobjects" "1" + } } } - "67" + "fast_build" { "name" "#Chaos_Effect_FastBuild" "duration" "120" "effect_class" "SetConVar" "data" { - "convar" "tf_fastbuild" - "value" "1" + "convars" + { + "tf_fastbuild" "1" + } } } - "68" + "high_overheal" { - "name" "#Chaos_Effect_10xOverhealLimit" - "duration" "45" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_HighOverheal" + "duration" "90" + "effect_class" "AddAttribute" "data" { "attributes" @@ -673,30 +752,27 @@ } } } - "69" + "unlimited_charge" { - "name" "#Chaos_Effect_10xChargeSpeed" + "name" "#Chaos_Effect_UnlimitedCharge" "duration" "120" - "effect_class" "SetConVar" - "data" - { - "convar" "tf_max_charge_speed" - "value" "7500" - } + "script_file" "unlimited_charge" } - "70" + "always_loser" { "name" "#Chaos_Effect_AlwaysLoser" - "duration" "45" + "duration" "60" "effect_class" "SetConVar" "tags" "camera" "data" { - "convar" "tf_always_loser" - "value" "1" + "convars" + { + "tf_always_loser" "1" + } } } - "71" + "low_gravity" { "name" "#Chaos_Effect_LowGravity" "duration" "120" @@ -705,11 +781,13 @@ "tags" "gravity" "data" { - "convar" "sv_gravity" - "value" "200" + "convars" + { + "sv_gravity" "200" + } } } - "72" + "high_gravity" { "name" "#Chaos_Effect_HighGravity" "duration" "90" @@ -717,66 +795,66 @@ "tags" "gravity" "data" { - "convar" "sv_gravity" - "value" "2400" - } - } - "73" - { - "name" "#Chaos_Effect_LoudSpeaker" - "duration" "150" - "effect_class" "SetAttribute" - "data" - { - "attributes" + "convars" { - "SET BONUS: special dsp" "38" + "sv_gravity" "2400" } } } - "74" + "random_gravity" + { + "name" "#Chaos_Effect_RandomGravity" + "duration" "90" + "script_file" "random_gravity" + "tags" "gravity" + } + "loudness" { "name" "#Chaos_Effect_Loudness" "duration" "20" "effect_class" "Loudness" "tags" "sound" } - "75" + "launch_up" { "name" "#Chaos_Effect_LaunchUp" - "script_file" "launchup" + "script_file" "set_velocity" + "data" "{ velocity = Vector(0, 0, 1000), abs = true }" } - "76" + "fling" { "name" "#Chaos_Effect_Fling" - "script_file" "fling" + "script_file" "set_velocity" + "data" "{ velocity = Vector(RandomFloat(-1000, 1000), RandomFloat(-1000, 1000), RandomFloat(500, 1000)) }" } - "77" + "spawn_ball" { - "name" "#Chaos_Effect_SpawnOrangeBall" + "name" "#Chaos_Effect_SpawnBall" "effect_class" "SpawnBall" "data" { "model" "models/props_halloween/hwn_kart_ball01.mdl" } } - "78" + "remove_currency" { "name" "#Chaos_Effect_RemoveCurrency" - "script_file" "removecurrency" + "script_file" "add_currency" "start_sound" "MVM.MoneyVanish" + "data" "{ amount = -30000 }" } - "79" + "add_currency" { "name" "#Chaos_Effect_AddCurrency" - "script_file" "addcurrency" + "script_file" "add_currency" "start_sound" "MVM.MoneyPickup" + "data" "{ amount = 30000 }" } - "80" + "big_torso" { - "name" "#Chaos_Effect_TallMercs" + "name" "#Chaos_Effect_BigTorso" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -785,24 +863,26 @@ } } } - "81" + "extra_jumps" { - "name" "#Chaos_Effect_ExtraAirDashes" + "name" "#Chaos_Effect_ExtraJumps" "duration" "120" "effect_class" "SetConVar" "data" { - "convar" "tf_scout_air_dash_count" - "value" "32" + "convars" + { + "tf_scout_air_dash_count" "32" + } } } - "84" + "paint_items" { "name" "#Chaos_Effect_PaintItems" - "script_file" "paintitems" + "script_file" "paint_items" "duration" "180" } - "85" + "nothing" { "name" "#Chaos_Effect_Nothing" "effect_class" "Nothing" @@ -812,43 +892,47 @@ { "0" "#Chaos_Effect_Nothing_SpawnAir" "1" "#Chaos_Effect_Nothing_GiveEveryoneANose" - "2" "#Chaos_Effect_Nothing_TeleportEveryone1Millimeter" + "2" "#Chaos_Effect_Nothing_Teleport1Millimeter" "3" "#Chaos_Effect_Nothing_KillAllDeadPlayers" "4" "#Chaos_Effect_Nothing_RemoveTheObserver" "5" "#Chaos_Effect_Nothing_IncreasePingBy1" - "6" "#Chaos_Effect_Nothing_AllPlayersArePlayers" + "6" "#Chaos_Effect_Nothing_TurnPlayersIntoPlayers" "7" "#Chaos_Effect_Nothing_Blank" "8" "#Chaos_Effect_Nothing_Error" "9" "#Chaos_Effect_Nothing_Mikusch" "10" "#Chaos_Effect_Nothing_NoCrash" + "11" "#Chaos_Effect_Nothing_RotateBy0Degrees" + "12" "#Chaos_Effect_Nothing_PlaySilence" } } } - "86" + "player_glow" { "name" "#Chaos_Effect_PlayerGlow" - "script_file" "playerglow" + "script_file" "player_glow" "duration" "120" } - "87" + "instant_respawn" { - "name" "#Chaos_Effect_DisableRespawnTimes" + "name" "#Chaos_Effect_InstantRespawn" "effect_class" "SetConVar" "duration" "120" "data" { - "convar" "mp_disable_respawn_times" - "value" "1" + "convars" + { + "mp_disable_respawn_times" "1" + } } } - "88" + "increased_spread" { "name" "#Chaos_Effect_IncreasedSpread" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "duration" "60" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "spread penalty" "6" @@ -856,46 +940,55 @@ } } } - "89" + "fake_crash" { "name" "#Chaos_Effect_FakeCrash" "effect_class" "FakeCrash" - "cooldown" "100" + "cooldown" "120" + "data" + { + "min_duration" "6.0" + "max_duration" "12.0" + } } - "90" + "tracer_rounds" { "name" "#Chaos_Effect_TracerRounds" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "duration" "120" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "sniper fires tracer" "1" } } } - "91" + "slap" { "name" "#Chaos_Effect_Slap" "effect_class" "Slap" - "duration" "45" + "duration" "60" } - "92" + "burn" { - "name" "#Chaos_Effect_FloorIsLava" - "effect_class" "FloorIsLava" - "duration" "30" + "name" "#Chaos_Effect_Burn" + "effect_class" "BurnPlayer" + "start_sound" "Halloween.MerasmusWheelBurn" + "data" + { + "duration" "10" + } } - "93" + "tilted_camera" { "name" "#Chaos_Effect_TiltedCamera" - "script_file" "tiltedcamera" - "duration" "45" + "script_file" "tilted_camera" + "duration" "60" "tags" "camera" } - "94" + "no_step_size" { "name" "#Chaos_Effect_NoStepSize" "effect_class" "StepSize" @@ -906,19 +999,19 @@ "stepsize" "0" } } - "95" + "disassemble_map" { "name" "#Chaos_Effect_DisassembleMap" "effect_class" "DisassembleMap" - "cooldown" "100" + "cooldown" "120" } - "96" + "regenerate" { - "name" "#Chaos_Effect_RegeneratePlayers" + "name" "#Chaos_Effect_Regenerate" "script_file" "regenerate" "start_sound" "Regenerate.Touch" } - "97" + "mann_in_the_machine" { "name" "#Chaos_Effect_MannInTheMachine" "effect_class" "MannInTheMachine" @@ -927,27 +1020,42 @@ "end_sound" "music.mvm_end_mid_wave" "tags" "playermodel" } - "98" + "headshots" { "name" "#Chaos_Effect_Headshots" "effect_class" "Headshots" "duration" "60" } - "99" + "vr_camera" { - "name" "#Chaos_Effect_EyeCamera" - "script_file" "eyecamera" - "duration" "90" + "name" "#Chaos_Effect_VRCamera" + "script_file" "vr_camera" + "duration" "60" "tags" "camera" } - "100" + "delayed_camera" { - "name" "#Chaos_Effect_LargeSmokeExplosions" - "effect_class" "SetAttribute" - "duration" "120" + "name" "#Chaos_Effect_DelayedCamera" + "script_file" "delayed_camera" + "duration" "60" + "tags" "camera" + "data" "{ delay = 0.5 }" + } + "leave_camera" + { + "name" "#Chaos_Effect_LeaveCamera" + "script_file" "leave_camera" + "duration" "20" + "tags" "camera" + } + "smoke_explosions" + { + "name" "#Chaos_Effect_SmokeExplosions" + "effect_class" "AddAttribute" + "duration" "90" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "damage causes airblast" "1" @@ -956,38 +1064,40 @@ } } } - "101" + "infinite_melee_range" { "name" "#Chaos_Effect_InfiniteMeleeRange" - "effect_class" "SetAttribute" - "duration" "120" + "effect_class" "AddAttribute" + "duration" "90" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "melee range multiplier" "9999" } } } - "102" + "no_air_accel" { - "name" "#Chaos_Effect_NoAirAcceleration" - "duration" "120" + "name" "#Chaos_Effect_NoAirAccel" + "duration" "90" "effect_class" "SetConVar" "data" { - "convar" "sv_airaccelerate" - "value" "0" + "convars" + { + "sv_airaccelerate" "0" + } } } - "103" + "no_ammo" { - "name" "#Chaos_Effect_CattoGuns" - "duration" "60" - "effect_class" "CattoGuns" + "name" "#Chaos_Effect_NoAmmo" + "script_file" "no_ammo" + "start_sound" "Engineer.AutoDejectedTie01" } - "104" + "give_gibus" { "name" "#Chaos_Effect_GiveGibus" "effect_class" "GiveItem" @@ -995,14 +1105,13 @@ { "items" { - "item" + "Ghostly Gibus" { - "name" "Ghostly Gibus" } } } } - "105" + "give_top_notch" { "name" "#Chaos_Effect_GiveTopNotch" "effect_class" "GiveItem" @@ -1010,30 +1119,28 @@ { "items" { - "item" + "The Top Notch" { - "name" "The Top Notch" } } } } - "106" + "flip_viewmodels" { "name" "#Chaos_Effect_FlipViewModels" "effect_class" "FlipViewModels" "duration" "120" } - "107" + "econ_violation" { - "name" "#Chaos_Effect_GiveGoldPan" + "name" "#Chaos_Effect_EconViolation" "effect_class" "GiveItem" "data" { "items" { - "item" + "Gold Frying Pan" { - "name" "Gold Frying Pan" "attributes" { "item style override" "0" @@ -1042,18 +1149,30 @@ } } } - "108" + "decompiled" { "name" "#Chaos_Effect_Decompiled" "effect_class" "Decompiled" "duration" "150" } - "109" + "small_head" + { + "name" "#Chaos_Effect_SmallHead" + "effect_class" "AddAttribute" + "duration" "120" + "data" + { + "attributes" + { + "head scale" "0.5" + } + } + } + "no_head" { - "name" "#Chaos_Effect_NoHeads" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_NoHead" + "effect_class" "AddAttribute" "duration" "120" - "tags" "head_scale" "data" { "attributes" @@ -1062,10 +1181,10 @@ } } } - "110" + "backstab_immunity" { - "name" "#Chaos_Effect_BackstabImmune" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_BackstabImmunity" + "effect_class" "AddAttribute" "duration" "60" "data" { @@ -1075,70 +1194,80 @@ } } } - "111" + "uturn" { - "name" "#Chaos_Effect_UTurn" + "name" "#Chaos_Effect_Uturn" "script_file" "uturn" } - "112" + "grant_upgrades" { - "name" "#Chaos_Effect_GrantAllUpgrades" + "name" "#Chaos_Effect_GrantUpgrades" "effect_class" "GrantOrRemoveAllUpgrades" "data" { "remove" "0" } } - "113" + "remove_upgrades" { - "name" "#Chaos_Effect_RemoveAllUpgrades" + "name" "#Chaos_Effect_RemoveUpgrades" "effect_class" "GrantOrRemoveAllUpgrades" "data" { "remove" "1" } } - "114" + "low_pitch" { "name" "#Chaos_Effect_LowPitch" "effect_class" "ModifyPitch" - "duration" "150" + "duration" "120" "tags" "sound" "data" { "pitch" "-50" } } - "115" + "high_pitch" { "name" "#Chaos_Effect_HighPitch" "effect_class" "ModifyPitch" - "duration" "150" + "duration" "120" "tags" "sound" "data" { "pitch" "50" } } - "116" + "force_forward" { "name" "#Chaos_Effect_ForceForward" - "effect_class" "ForceForward" + "effect_class" "ForceMove" + "duration" "60" + "data" + { + "direction" "0" + } + } + "force_back" + { + "name" "#Chaos_Effect_ForceBack" + "effect_class" "ForceMove" "duration" "60" "data" { - "velocity" "450" + "direction" "1" } } - "117" + "swap_teams" { "name" "#Chaos_Effect_SwapTeams" - "script_file" "swapteams" + "script_file" "swap_teams" } - "118" + "fast_taunt" { - "name" "#Chaos_Effect_IncreasedGestureSpeed" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_FastTaunt" + "effect_class" "AddAttribute" "duration" "150" "data" { @@ -1148,10 +1277,10 @@ } } } - "119" + "slow_taunt" { - "name" "#Chaos_Effect_DecreasedGestureSpeed" - "effect_class" "SetAttribute" + "name" "#Chaos_Effect_SlowTaunt" + "effect_class" "AddAttribute" "duration" "150" "data" { @@ -1161,76 +1290,75 @@ } } } - "120" + "spawn_birds" { "name" "#Chaos_Effect_SpawnBirds" "effect_class" "SpawnBirds" - "duration" "120" + "duration" "150" } - "121" + "speed_up" { "name" "#Chaos_Effect_SpeedUp" - "duration" "60" - "effect_class" "SetConVar" + "duration" "120" + "effect_class" "TimeScale" "start_sound" "replay/exitperformancemode.wav" "end_sound" "replay/enterperformancemode.wav" + "tags" "sound" "data" { - "convar" "host_timescale" - "value" "2" - "replicate_cheats" "1" + "timescale" "2.0" } } - "122" + "earthquake" { "name" "#Chaos_Effect_Earthquake" "duration" "45" "effect_class" "Earthquake" "start_sound" "ambient/atmosphere/terrain_rumble1.wav" } - "123" + "bouncy_projectiles" { "name" "#Chaos_Effect_BouncyProjectiles" "duration" "90" - "script_file" "bouncyprojectiles" + "script_file" "bouncy_projectiles" } - "124" + "screenfade_white" { - "name" "#Chaos_Effect_Flashbang" - "duration" "15" + "name" "#Chaos_Effect_ScreenFadeWhite" + "duration" "10" "effect_class" "ScreenFade" "data" { "color" "255 255 255 255" } } - "125" + "enable_all_holidays" { "name" "#Chaos_Effect_EnableAllHolidays" "duration" "180" "effect_class" "EnableAllHolidays" } - "126" + "teleport_to_zero" { "name" "#Chaos_Effect_TeleportToZero" - "script_file" "teleporttozero" + "script_file" "teleport_to_zero" } - "128" + "force_jump" { - "name" "#Chaos_Effect_JumpJump" + "name" "#Chaos_Effect_ForceJump" "duration" "60" - "effect_class" "JumpJump" + "effect_class" "ForceJump" "tags" "jumping" } - "129" + "disable_direction" { - "name" "#Chaos_Effect_DisableRandomDirection" + "name" "#Chaos_Effect_DisableDirection" "duration" "60" - "effect_class" "DisableRandomDirection" + "effect_class" "DisableDirection" } - "130" + "grill_overlay" { - "name" "#Chaos_Effect_Grill" + "name" "#Chaos_Effect_GrillOverlay" "duration" "60" "effect_class" "ScreenOverlay" "data" @@ -1238,49 +1366,56 @@ "material" "console/characters/bbq_summer2023" } } - "131" + "swimming" { "name" "#Chaos_Effect_Swimming" "duration" "90" "effect_class" "AddCond" + "tags" "camera" "start_sound" "Announcer.SD_Event_SwimmingCurse" "data" { - "condition" "86" + "conditions" + { + "TF_COND_SWIMMING_CURSE" "86" + } } } - "132" + "recoil" { - "name" "#Chaos_Effect_UncontrollableRecoil" + "name" "#Chaos_Effect_Recoil" "duration" "60" - "script_file" "uncontrollablerecoil" + "script_file" "recoil" "tags" "camera" } - "133" + "overtime" { "name" "#Chaos_Effect_Overtime" "duration" "60" "script_file" "overtime" } - "134" + "pushback_immunity" { - "name" "#Chaos_Effect_ImmuneToPushback" + "name" "#Chaos_Effect_PushbackImmunity" "duration" "60" "effect_class" "AddCond" "data" { - "condition" "130" + "conditions" + { + "TF_COND_IMMUNE_TO_PUSHBACK" "130" + } } } - "135" + "callouts" { "name" "#Chaos_Effect_Callouts" "duration" "120" "script_file" "callouts" } - "136" + "invert_air_accel" { - "name" "#Chaos_Effect_InvertAirAcceleration" + "name" "#Chaos_Effect_InvertAirAccel" "duration" "60" "effect_class" "InvertConVar" "data" @@ -1288,45 +1423,46 @@ "convar" "sv_airaccelerate" } } - "137" + "weapons_fire_rockets" { "name" "#Chaos_Effect_WeaponsFireRockets" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "duration" "60" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "override projectile type" "2" } } } - "139" + "medieval" { - "name" "#Chaos_Effect_MedievalMode" + "name" "#Chaos_Effect_Medieval" "script_file" "medieval" - "duration" "120" + "duration" "90" "start_sound" "Medieval.DoorOpen" "end_sound" "Medieval.DoorClose" } - "140" + "no_spread" { "name" "#Chaos_Effect_NoSpread" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "duration" "60" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { - "spread penalty" "0" + "spread penalty" "0" + "Projectile speed increased" "2.5" } } } - "141" + "high_fall_damage" { - "name" "#Chaos_Effect_10xFallingDamage" + "name" "#Chaos_Effect_HighFallDamage" "effect_class" "FallDamage" "duration" "90" "data" @@ -1334,32 +1470,21 @@ "multiplier" "10" } } - "142" - { - "name" "#Chaos_Effect_IncreasedStepSize" - "effect_class" "StepSize" - "duration" "120" - "start_sound" "vo/heavy_mvm_get_upgrade03.mp3" - "data" - { - "stepsize" "128" - } - } - "143" + "identity_theft" { "name" "#Chaos_Effect_IdentityTheft" "effect_class" "IdentityTheft" "duration" "90" } - "144" + "drunk" { "name" "#Chaos_Effect_Drunk" "effect_class" "Drunk" "duration" "45" } - "145" + "binocular_overlay" { - "name" "#Chaos_Effect_Binoculars" + "name" "#Chaos_Effect_BinocularOverlay" "effect_class" "ScreenOverlay" "duration" "60" "data" @@ -1367,28 +1492,29 @@ "material" "effects/combine_binocoverlay" } } - "146" + "homing_projectiles" { "name" "#Chaos_Effect_HomingProjectiles" "duration" "90" - "script_file" "homingprojectiles" + "script_file" "homing_projectiles" + "tags" "think_functions" } - "147" + "randomize_weapon_order" { "name" "#Chaos_Effect_RandomizeWeaponOrder" "duration" "60" "effect_class" "RandomizeWeaponOrder" } - "148" + "play_commentary" { - "name" "#Chaos_Effect_PlayDevCommentary" - "script_file" "playcommentary" + "name" "#Chaos_Effect_PlayCommentary" + "script_file" "play_commentary" } - "149" + "super_jump" { "name" "#Chaos_Effect_SuperJump" "duration" "90" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "tags" "jumping" "start_sound" "Halloween.MerasmusWheelSuperJump" "data" @@ -1400,11 +1526,11 @@ } } } - "150" + "small_heads" { - "name" "#Chaos_Effect_SmallHead" + "name" "#Chaos_Effect_SmallHeads" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "start_sound" "Halloween.MerasmusWheelShrunkHead" "data" { @@ -1415,55 +1541,57 @@ } } } - "151" + "spawn_gargoyle" { "name" "#Chaos_Effect_SpawnGargoyle" - "script_file" "spawngargoyle" + "script_file" "spawn_gargoyle" } - "152" + "always_gib" { "name" "#Chaos_Effect_AlwaysGib" - "duration" "120" + "duration" "150" "effect_class" "SetConVar" "data" { - "convar" "tf_playergib" - "value" "2" + "convars" + { + "tf_playergib" "2" + } } } - "153" + "honorbound" { "name" "#Chaos_Effect_Honorbound" "duration" "90" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "honorbound" "1" } } } - "154" + "no_self_blast" { - "name" "#Chaos_Effect_NoSelfBlastDamage" + "name" "#Chaos_Effect_NoSelfBlast" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { - "apply_to_weapons" "1" + "apply_to_items" "1" "attributes" { "no self blast dmg" "2" } } } - "155" + "no_overheal_decay" { "name" "#Chaos_Effect_NoOverhealDecay" "duration" "90" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -1472,24 +1600,24 @@ } } } - "156" + "fast_projectiles" { "name" "#Chaos_Effect_FastProjectiles" "duration" "90" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" { - "Projectile speed increased" "3" + "Projectile speed increased" "5" } } } - "157" + "infinite_sentry_range" { - "name" "#Chaos_Effect_IncreasedSentryRange" + "name" "#Chaos_Effect_InfiniteSentryRange" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -1498,11 +1626,11 @@ } } } - "158" + "infinite_dispenser_range" { - "name" "#Chaos_Effect_IncreasedDispenserRange" + "name" "#Chaos_Effect_InfiniteDispenserRange" "duration" "120" - "effect_class" "SetAttribute" + "effect_class" "AddAttribute" "data" { "attributes" @@ -1511,24 +1639,145 @@ } } } - "159" + "chip_damage" { "name" "#Chaos_Effect_ChipDamage" - "duration" "90" - "script_file" "chipdamage" + "duration" "60" + "script_file" "chip_damage" } - "160" + "christmas_presents" { "name" "#Chaos_Effect_ChristmasPresents" - "duration" "90" - "script_file" "christmaspresents" + "duration" "120" + "script_file" "christmas_presents" "start_sound" "sniper.SpecialWeapon08" } - "meta_0" + "big_players" + { + "name" "#Chaos_Effect_BigPlayers" + "duration" "60" + "effect_class" "ResizePlayer" + "data" + { + "scale" "2.0" + "change_duration" "5.0" + } + } + "small_players" { - "name" "#Chaos_MetaEffect_5xTimerSpeed" + "name" "#Chaos_Effect_SmallPlayers" + "duration" "60" + "effect_class" "ResizePlayer" + "data" + { + "scale" "0.5" + "change_duration" "5.0" + } + } + "cocainum" + { + "name" "#Chaos_Effect_Cocainum" + "duration" "60" + "effect_class" "ScreenOverlay" + "start_sound" "vo/heavy_sf12_badmagic07.mp3" + "data" + { + "material" "chaos/shaders/cocainum" + "shader" "chaos_cocainum_ps20b" + "dsp" "25" + } + } + "mirror" + { + "name" "#Chaos_Effect_Mirror" + "duration" "60" + "effect_class" "ScreenOverlay" + "data" + { + "material" "chaos/shaders/mirror" + "shader" "chaos_mirror_ps20b" + } + } + "axis_mirror_y" + { + "name" "#Chaos_Effect_AxisMirrorY" + "duration" "60" + "effect_class" "ScreenOverlay" + "data" + { + "material" "chaos/shaders/axismirror_y" + "shader" "chaos_axismirror_ps20b" + } + } + "axis_mirror_x" + { + "name" "#Chaos_Effect_AxisMirrorX" + "duration" "60" + "effect_class" "ScreenOverlay" + "data" + { + "material" "chaos/shaders/axismirror_x" + "shader" "chaos_axismirror_ps20b" + } + } + "scroll_x" + { + "name" "#Chaos_Effect_ScrollX" + "duration" "60" + "effect_class" "ScreenOverlay" + "data" + { + "material" "chaos/shaders/scroll_x" + "shader" "chaos_scroll_ps20b" + } + } + "scroll_y" + { + "name" "#Chaos_Effect_ScrollY" + "duration" "60" + "effect_class" "ScreenOverlay" + "data" + { + "material" "chaos/shaders/scroll_y" + "shader" "chaos_scroll_ps20b" + } + } + "tile" + { + "name" "#Chaos_Effect_Tile" + "duration" "60" + "effect_class" "ScreenOverlay" + "data" + { + "material" "chaos/shaders/tile" + "shader" "chaos_tile_ps20b" + } + } + "jumpy_props" + { + "name" "#Chaos_Effect_JumpyProps" + "duration" "150" + "script_file" "jumpy_props" + "tags" "think_functions" + } + "spin_props" + { + "name" "#Chaos_Effect_SpinProps" + "duration" "150" + "script_file" "spin_props" + "tags" "think_functions" + } + "projectile_parry" + { + "name" "#Chaos_Effect_ProjectileParry" + "duration" "90" + "script_file" "projectile_parry" + } + "meta_timer_5x" + { + "name" "#Chaos_MetaEffect_Timer5x" "duration" "30" - "cooldown" "4" + "cooldown" "3" "meta" "1" "effect_class" "TimerSpeed" "data" @@ -1536,11 +1785,11 @@ "multiplier" "5" } } - "meta_1" + "meta_timer_2x" { - "name" "#Chaos_MetaEffect_2xTimerSpeed" + "name" "#Chaos_MetaEffect_Timer2x" "duration" "120" - "cooldown" "4" + "cooldown" "3" "meta" "1" "effect_class" "TimerSpeed" "data" @@ -1548,11 +1797,11 @@ "multiplier" "2" } } - "meta_2" + "meta_timer_half" { - "name" "#Chaos_MetaEffect_0.5xTimerSpeed" + "name" "#Chaos_MetaEffect_TimerHalf" "duration" "120" - "cooldown" "4" + "cooldown" "3" "meta" "1" "effect_class" "TimerSpeed" "data" @@ -1560,19 +1809,19 @@ "multiplier" "0.5" } } - "meta_3" + "meta_no_chaos" { "name" "#Chaos_MetaEffect_NoChaos" "duration" "120" - "cooldown" "4" + "cooldown" "3" "meta" "1" "effect_class" "NoChaos" } - "meta_4" + "meta_duration_2x" { - "name" "#Chaos_MetaEffect_2xEffectDuration" + "name" "#Chaos_MetaEffect_Duration2x" "duration" "120" - "cooldown" "4" + "cooldown" "3" "meta" "1" "effect_class" "EffectDuration" "data" @@ -1580,11 +1829,11 @@ "multiplier" "2" } } - "meta_5" + "meta_duration_half" { - "name" "#Chaos_MetaEffect_0.5xEffectDuration" + "name" "#Chaos_MetaEffect_DurationHalf" "duration" "120" - "cooldown" "4" + "cooldown" "3" "meta" "1" "effect_class" "EffectDuration" "data" @@ -1592,10 +1841,10 @@ "multiplier" "0.5" } } - "meta_6" + "meta_reinvoke" { - "name" "#Chaos_MetaEffect_ReinvokePreviousEffects" - "cooldown" "4" + "name" "#Chaos_MetaEffect_Reinvoke" + "cooldown" "3" "meta" "1" "effect_class" "ReinvokeEffects" "data" @@ -1603,4 +1852,28 @@ "reinvoke_time" "120" } } + "meta_multi3" + { + "name" "#Chaos_MetaEffect_Multi3" + "duration" "10" + "cooldown" "3" + "meta" "1" + "effect_class" "MultiEffect" + "data" + { + "effect_count" "3" + } + } + "meta_multi6" + { + "name" "#Chaos_MetaEffect_Multi6" + "duration" "10" + "cooldown" "3" + "meta" "1" + "effect_class" "MultiEffect" + "data" + { + "effect_count" "6" + } + } } diff --git a/addons/sourcemod/configs/chaos/visuals.cfg b/addons/sourcemod/configs/chaos/visuals.cfg index 397d510c..df59d4cc 100644 --- a/addons/sourcemod/configs/chaos/visuals.cfg +++ b/addons/sourcemod/configs/chaos/visuals.cfg @@ -1,9 +1,5 @@ "visuals" { - "chat" - { - "tag" "[{darkorange}TF2 CHAOS{default}] " - } "timer_bar" { "num_blocks" "20" diff --git a/addons/sourcemod/gamedata/chaos.txt b/addons/sourcemod/gamedata/chaos.txt index ee17f6b8..8ec0ff71 100644 --- a/addons/sourcemod/gamedata/chaos.txt +++ b/addons/sourcemod/gamedata/chaos.txt @@ -16,12 +16,6 @@ "linux" "@_ZN9CTFPlayer22GetMaxHealthForBuffingEv" "windows" "\x55\x8B\xEC\x83\xEC\x20\x53\x56\x57\x8B\xF9\xFF\xB7\xD0\x22\x00\x00" } - "SpawnClientsideFlyingBird" - { - "library" "server" - "linux" "@_Z25SpawnClientsideFlyingBirdR6Vector" - "windows" "\x55\x8B\xEC\x83\xEC\x38\xD9\x05\x2A\x2A\x2A\x2A" - } "TF_IsHolidayActive" { "library" "server" @@ -31,18 +25,18 @@ } "Offsets" { + "IVEngineServer::SetPausedForced" + { + "library" "engine" + "linux" "123" + "windows" "123" + } "CTFPlayer::GiveNamedItem" { "library" "server" "linux" "494" "windows" "487" } - "CBaseCombatWeapon::CanDeploy" - { - "library" "server" - "linux" "267" - "windows" "261" - } "CBaseCombatWeapon::WeaponSound" { "library" "server" diff --git a/addons/sourcemod/scripting/chaos.sp b/addons/sourcemod/scripting/chaos.sp index 5a4f6408..51651217 100644 --- a/addons/sourcemod/scripting/chaos.sp +++ b/addons/sourcemod/scripting/chaos.sp @@ -13,7 +13,7 @@ #include #include -#define PLUGIN_VERSION "1.7.1" +#define PLUGIN_VERSION "2.0.0" ConVar sm_chaos_enabled; ConVar sm_chaos_effect_cooldown; @@ -23,7 +23,7 @@ ConVar sm_chaos_meta_effect_chance; ConVar sm_chaos_effect_update_interval; bool g_bEnabled; -bool g_bNoChaos; +bool g_bPaused; ArrayList g_hEffects; Handle g_hTimerBarHudSync; float g_flTimeElapsed; @@ -34,7 +34,6 @@ char g_szForceEffectId[64]; ProgressBarConfig g_stEffectBarConfig; ProgressBarConfig g_stTimerBarConfig; -ChatConfig g_stChatConfig; #include "chaos/data.sp" #include "chaos/events.sp" @@ -42,55 +41,57 @@ ChatConfig g_stChatConfig; #include "chaos/util.sp" // Meta effects -#include "chaos/effects/meta/effectduration.sp" -#include "chaos/effects/meta/nochaos.sp" -#include "chaos/effects/meta/reinvokeeffects.sp" -#include "chaos/effects/meta/timerspeed.sp" +#include "chaos/effects/meta/effect_duration.sp" +#include "chaos/effects/meta/multi_effect.sp" +#include "chaos/effects/meta/no_chaos.sp" +#include "chaos/effects/meta/reinvoke_effects.sp" +#include "chaos/effects/meta/timer_speed.sp" // Regular effects -#include "chaos/effects/addcond.sp" -#include "chaos/effects/birds.sp" -#include "chaos/effects/cattoguns.sp" +#include "chaos/effects/add_attribute.sp" +#include "chaos/effects/add_cond.sp" +#include "chaos/effects/burn_player.sp" #include "chaos/effects/decompiled.sp" -#include "chaos/effects/disablerandomdirection.sp" -#include "chaos/effects/disassemblemap.sp" +#include "chaos/effects/disable_direction.sp" +#include "chaos/effects/disassemble_map.sp" #include "chaos/effects/drunk.sp" #include "chaos/effects/earthquake.sp" -#include "chaos/effects/enableallholidays.sp" -#include "chaos/effects/fakecrash.sp" -#include "chaos/effects/falldamage.sp" -#include "chaos/effects/flipviewmodels.sp" -#include "chaos/effects/floorislava.sp" -#include "chaos/effects/forceforward.sp" -#include "chaos/effects/giveitem.sp" -#include "chaos/effects/grantorremoveallupgrades.sp" +#include "chaos/effects/enable_all_holidays.sp" +#include "chaos/effects/fake_crash.sp" +#include "chaos/effects/fall_damage.sp" +#include "chaos/effects/flip_viewmodels.sp" +#include "chaos/effects/force_jump.sp" +#include "chaos/effects/force_move.sp" +#include "chaos/effects/give_item.sp" +#include "chaos/effects/grant_or_remove_all_upgrades.sp" #include "chaos/effects/headshots.sp" -#include "chaos/effects/identitytheft.sp" -#include "chaos/effects/invertconvar.sp" -#include "chaos/effects/jumpjump.sp" -#include "chaos/effects/killrandomplayer.sp" +#include "chaos/effects/identity_theft.sp" +#include "chaos/effects/invert_convar.sp" +#include "chaos/effects/kill_random_player.sp" #include "chaos/effects/loudness.sp" -#include "chaos/effects/manninthemachine.sp" -#include "chaos/effects/modifypitch.sp" +#include "chaos/effects/mann_in_the_machine.sp" +#include "chaos/effects/modify_pitch.sp" +#include "chaos/effects/no_transmit.sp" #include "chaos/effects/nothing.sp" -#include "chaos/effects/randomizeweaponorder.sp" -#include "chaos/effects/removehealthandammo.sp" -#include "chaos/effects/removerandomentity.sp" -#include "chaos/effects/screenfade.sp" -#include "chaos/effects/screenoverlay.sp" -#include "chaos/effects/setattribute.sp" -#include "chaos/effects/setconvar.sp" -#include "chaos/effects/setcustommodel.sp" -#include "chaos/effects/setfov.sp" -#include "chaos/effects/sethealth.sp" -#include "chaos/effects/showscoreboard.sp" +#include "chaos/effects/randomize_weapon_order.sp" +#include "chaos/effects/remove_pickups.sp" +#include "chaos/effects/remove_random_entity.sp" +#include "chaos/effects/resize_player.sp" +#include "chaos/effects/screen_fade.sp" +#include "chaos/effects/screen_overlay.sp" +#include "chaos/effects/set_convar.sp" +#include "chaos/effects/set_custom_model.sp" +#include "chaos/effects/set_fov.sp" +#include "chaos/effects/set_max_health.sp" +#include "chaos/effects/show_scoreboard.sp" #include "chaos/effects/silence.sp" #include "chaos/effects/slap.sp" -#include "chaos/effects/spawnball.sp" -#include "chaos/effects/stepsize.sp" +#include "chaos/effects/spawn_ball.sp" +#include "chaos/effects/spawn_birds.sp" +#include "chaos/effects/step_size.sp" +#include "chaos/effects/time_scale.sp" #include "chaos/effects/truce.sp" #include "chaos/effects/watermark.sp" -#include "chaos/effects/wheredideverythinggo.sp" public Plugin myinfo = { @@ -113,10 +114,10 @@ public void OnPluginStart() CreateConVar("sm_chaos_version", PLUGIN_VERSION, "Plugin version.", FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_DONTRECORD); sm_chaos_enabled = CreateConVar("sm_chaos_enabled", "1", "Enable or disable the plugin."); sm_chaos_enabled.AddChangeHook(ConVarChanged_ChaosEnable); - sm_chaos_effect_cooldown = CreateConVar("sm_chaos_effect_cooldown", "50", "Default cooldown between effects.", _, true, 0.0); + sm_chaos_effect_cooldown = CreateConVar("sm_chaos_effect_cooldown", "60", "Default cooldown between effects.", _, true, 0.0); sm_chaos_effect_interval = CreateConVar("sm_chaos_effect_interval", "30", "Interval between each effect activation, in seconds."); - sm_chaos_meta_effect_interval = CreateConVar("sm_chaos_meta_effect_interval", "40", "Interval between each attempted meta effect activation, in seconds."); - sm_chaos_meta_effect_chance = CreateConVar("sm_chaos_meta_effect_chance", ".025", "Chance for a meta effect to be activated every interval, in percent.", _, true, 0.0, true, 100.0); + sm_chaos_meta_effect_interval = CreateConVar("sm_chaos_meta_effect_interval", "12", "Interval between each attempted meta effect activation, in seconds."); + sm_chaos_meta_effect_chance = CreateConVar("sm_chaos_meta_effect_chance", ".0075", "Chance for a meta effect to be activated every interval, in percent.", _, true, 0.0, true, 100.0); sm_chaos_effect_update_interval = CreateConVar("sm_chaos_effect_update_interval", ".1", "Interval at which effect update functions should be called, in seconds."); RegAdminCmd("sm_chaos_setnexteffect", ConCmd_SetNextEffect, ADMFLAG_CHEATS, "Sets the next effect."); @@ -137,20 +138,11 @@ public void OnPluginEnd() public void VScript_OnScriptVMInitialized() { static bool bInitialized = false; - + if (bInitialized) return; - - GameData hGameConf = new GameData("chaos"); - if (hGameConf) - { - bInitialized = Data_InitializeEffects(hGameConf); - delete hGameConf; - } - else - { - LogError("Failed to find chaos gamedata"); - } + + bInitialized = Data_InitializeEffects(); } public void OnMapStart() @@ -275,7 +267,7 @@ public void OnGameFrame() if (effect.script_file[0]) { VScriptExecute hExecute = new VScriptExecute(HSCRIPT_RootTable.GetValue("Chaos_UpdateEffect")); - hExecute.SetParamString(1, FIELD_CSTRING, effect.script_file); + hExecute.SetParamString(1, FIELD_CSTRING, effect.id); if (hExecute.Execute() != SCRIPT_ERROR) { float flUpdateInterval; @@ -284,30 +276,31 @@ public void OnGameFrame() else flUpdateInterval = float(hExecute.ReturnValue); - delete hExecute; - g_hEffects.Set(i, flCurTime + flUpdateInterval, ChaosEffect::next_script_update_time); } + + delete hExecute; } } } } - + + // Only run during an active round RoundState nRoundState = GameRules_GetRoundState(); - if (g_bNoChaos || (nRoundState != RoundState_RoundRunning && nRoundState != RoundState_Stalemate) || GameRules_GetProp("m_bInWaitingForPlayers") || GameRules_GetProp("m_bInSetup")) + if (g_bPaused || (nRoundState != RoundState_RoundRunning && nRoundState != RoundState_Stalemate) || GameRules_GetProp("m_bInWaitingForPlayers") || GameRules_GetProp("m_bInSetup")) return; - + float flTimerSpeed = GetGameFrameTime(); - + // Meta effects tick independently g_flMetaTimeElapsed += flTimerSpeed; - + // Check if a meta effect wants to modify the interval for (int i = 0; i < nLength; i++) { if (!g_hEffects.Get(i, ChaosEffect::active)) continue; - + ChaosEffect effect; if (g_hEffects.GetArray(i, effect)) { @@ -321,23 +314,23 @@ public void OnGameFrame() } } } - + g_flTimeElapsed += flTimerSpeed; - + // Show interval progress bar - if (g_flTimerBarDisplayTime && g_flTimerBarDisplayTime + 0.1 <= flCurTime) + if (g_flTimerBarDisplayTime > 0.0 && g_flTimerBarDisplayTime + 0.1 <= flCurTime) { g_flTimerBarDisplayTime = flCurTime; - + DisplayTimerBar(); } - + // Activate a new effect float flEffectInterval = sm_chaos_effect_interval.FloatValue; - if (flEffectInterval && g_flTimeElapsed >= flEffectInterval) + if (flEffectInterval > 0.0 && g_flTimeElapsed >= flEffectInterval) { g_flTimeElapsed = 0.0; - + if (!g_szForceEffectId[0]) { SelectRandomEffect(); @@ -348,18 +341,18 @@ public void OnGameFrame() { LogError("Failed to force effect id '%s'", g_szForceEffectId); } - + // Clear out forced effect g_szForceEffectId[0] = EOS; } } - + // Attempt to activate a new meta effect float flMetaEffectInterval = sm_chaos_meta_effect_interval.FloatValue; - if (flMetaEffectInterval && g_flMetaTimeElapsed >= flMetaEffectInterval) + if (flMetaEffectInterval > 0.0 && g_flMetaTimeElapsed >= flMetaEffectInterval) { g_flMetaTimeElapsed = 0.0; - + // Meta effects randomly activate if (GetRandomFloat() <= sm_chaos_meta_effect_chance.FloatValue) { @@ -528,48 +521,7 @@ public void TF2_OnWaitingForPlayersStart() if (!g_bEnabled) return; - SetChaosTimers(0.0); -} - -public Action TF2Items_OnGiveNamedItem(int client, char[] classname, int itemDefIndex, Handle &item) -{ - if (!g_bEnabled) - return Plugin_Continue; - - Action nReturn = Plugin_Continue; - - int nLength = g_hEffects.Length; - for (int i = 0; i < nLength; i++) - { - if (!g_hEffects.Get(i, ChaosEffect::active)) - continue; - - ChaosEffect effect; - if (g_hEffects.GetArray(i, effect)) - { - Function fnCallback = effect.GetCallbackFunction("OnGiveNamedItem"); - if (fnCallback != INVALID_FUNCTION) - { - Call_StartFunction(null, fnCallback); - Call_PushArray(effect, sizeof(effect)); - Call_PushCell(client); - Call_PushString(classname); - Call_PushCell(itemDefIndex); - Call_PushCellRef(item); - - Action nResult; - if (Call_Finish(nResult) == SP_ERROR_NONE) - { - if (nResult > nReturn) - { - nReturn = nResult; - } - } - } - } - } - - return nReturn; + StopChaosTimers(); } // --------------------------------------------------------------------------------------------------- // @@ -582,11 +534,11 @@ void TogglePlugin(bool bEnable) if (bEnable) { - SetChaosTimers(GetGameTime()); + StartChaosTimers(); } else { - SetChaosTimers(0.0); + StopChaosTimers(); ExpireAllActiveEffects(true); } @@ -644,13 +596,13 @@ bool ActivateEffectById(const char[] szEffectId, bool bForce = false) if (effect.active) { - LogError("The effect '%T' (%s) is already active!", effect.name, LANG_SERVER, effect.id); + LogError("Effect '%s' is already active!", effect.id); return false; } if (!effect.IsCompatibleWithActiveEffects()) { - LogMessage("Skipped effect '%T' (%s) because it is incompatible with other active effects", effect.name, LANG_SERVER, effect.id); + LogMessage("Skipped '%s' because it's incompatible with other active effects", effect.id); return false; } @@ -664,7 +616,7 @@ bool ActivateEffectById(const char[] szEffectId, bool bForce = false) bool bReturn; if (Call_Finish(bReturn) != SP_ERROR_NONE || !bReturn) { - LogMessage("Skipped effect '%T' (%s) because its 'OnStart' callback returned false", effect.name, LANG_SERVER, effect.id); + LogMessage("Skipped '%s' because the 'OnStart' callback returned false", effect.id); return false; } } @@ -672,15 +624,17 @@ bool ActivateEffectById(const char[] szEffectId, bool bForce = false) if (effect.script_file[0]) { VScriptExecute hExecute = new VScriptExecute(HSCRIPT_RootTable.GetValue("Chaos_StartEffect")); - hExecute.SetParamString(1, FIELD_CSTRING, effect.script_file); - hExecute.SetParam(2, FIELD_FLOAT, effect.duration); + hExecute.SetParamString(1, FIELD_CSTRING, effect.id); + hExecute.SetParamString(2, FIELD_CSTRING, effect.script_file); + hExecute.SetParam(3, FIELD_FLOAT, effect.duration); + hExecute.SetParamString(4, FIELD_CSTRING, effect.data_string); hExecute.Execute(); bool bReturn = hExecute.ReturnValue; delete hExecute; - + if (!bReturn) { - LogMessage("Skipped script file '%s' because its 'OnStart' callback returned false", effect.script_file); + LogMessage("Skipped '%s' because the 'OnStart' script function returned false", effect.id); return false; } } @@ -751,21 +705,19 @@ bool ActivateEffectById(const char[] szEffectId, bool bForce = false) PlayStaticSound(effect.start_sound); } - char szName[64]; - if (effect.GetName(szName, sizeof(szName)) && szName[0]) + for (int client = 1; client <= MaxClients; client++) { - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - char szMessage[256]; - Format(szMessage, sizeof(szMessage), "%t", "#Chaos_Effect_Activated", szName, client); - SendCustomHudNotificationCustom(client, szMessage, "ico_notify_flag_moving_alt"); - } + if (!IsClientInGame(client)) + continue; + + char szName[64]; + if (!effect.GetDisplayName(szName, sizeof(szName), client)) + continue; + + SendCustomHudNotificationCustom(client, szName, "ico_notify_flag_moving_alt"); } - // For effects that need to access modified properties + // For effects that need to access properties set after successful activation fnCallback = effect.GetCallbackFunction("OnStartPost"); if (fnCallback != INVALID_FUNCTION) { @@ -774,7 +726,7 @@ bool ActivateEffectById(const char[] szEffectId, bool bForce = false) Call_Finish(); } - LogMessage("Activated effect '%T'", effect.name, LANG_SERVER); + LogMessage("Activated effect '%s'", effect.id); return true; } @@ -817,9 +769,10 @@ void DisplayActiveEffects() { if (!IsClientInGame(client)) continue; - + + // KeyHintText has a 1 byte param char szMessage[MAX_USER_MSG_DATA - 1]; - + // Go through all effects until we find a valid one int nLength = g_hEffects.Length; for (int i = 0; i < nLength; i++) @@ -831,9 +784,11 @@ void DisplayActiveEffects() continue; char szName[64]; - if (!effect.GetName(szName, sizeof(szName)) || !szName[0]) + if (!effect.GetDisplayName(szName, sizeof(szName), client)) continue; + bool bPhraseExists = TranslationPhraseExists(szName); + char szLine[128]; // Expiring effects stay on screen while active @@ -856,15 +811,15 @@ void DisplayActiveEffects() StrCat(szProgressBar, sizeof(szProgressBar), g_stEffectBarConfig.empty); } - Format(szLine, sizeof(szLine), "%T %s", szName, client, szProgressBar); + Format(szLine, sizeof(szLine), bPhraseExists ? "%s %T" : "%s %s", szProgressBar, szName, client); } - // One-shot effects stay on screen for 60 seconds - else if (!effect.duration && GetGameTime() - effect.activate_time <= 60.0) + // One-shot effects stay on screen for some time + else if (!effect.duration && GetGameTime() - effect.activate_time <= ONESHOT_EFFECT_DISPLAY_TIME) { - Format(szLine, sizeof(szLine), "%T", szName, client); + Format(szLine, sizeof(szLine), bPhraseExists ? "%T" : "%s", szName, client); } - // -2 to include null terminators + // -1 accounts for newline if (szLine[0] && strlen(szMessage) + strlen(szLine) < sizeof(szMessage) - 1) { Format(szMessage, sizeof(szMessage), "%s\n%s", szMessage, szLine); @@ -925,7 +880,7 @@ void ForceExpireEffect(ChaosEffect effect, bool bExpireAllTags = false) if (effect.script_file[0]) { VScriptExecute hExecute = new VScriptExecute(HSCRIPT_RootTable.GetValue("Chaos_EndEffect")); - hExecute.SetParamString(1, FIELD_CSTRING, effect.script_file); + hExecute.SetParamString(1, FIELD_CSTRING, effect.id); hExecute.Execute(); delete hExecute; } @@ -974,20 +929,28 @@ void ForceExpireEffect(ChaosEffect effect, bool bExpireAllTags = false) * Returns true if the given effect class is currently active. */ bool IsEffectOfClassActive(const char[] szEffectClass) +{ + ChaosEffect effect; + return GetActiveEffectByClass(szEffectClass, effect); +} + +/** + * Retrieves the active effect with the given class. + */ +bool GetActiveEffectByClass(const char[] szEffectClass, ChaosEffect effect) { int nLength = g_hEffects.Length; for (int i = 0; i < nLength; i++) { if (!g_hEffects.Get(i, ChaosEffect::active)) continue; - - ChaosEffect effect; + if (g_hEffects.GetArray(i, effect) && StrEqual(szEffectClass, effect.effect_class)) { return true; } } - + return false; } @@ -1059,13 +1022,62 @@ bool FindKeyValuePairInActiveEffects(const char[] szEffectClass, const char[] sz return false; } +/** + * Returns true if the given key was found in the given section in active effects with the given class. + */ +bool FindKeyInSectionInActiveEffects(const char[] szEffectClass, const char[] szSection, const char[] szKey) +{ + int nLength = g_hEffects.Length; + for (int i = 0; i < nLength; i++) + { + if (!g_hEffects.Get(i, ChaosEffect::active)) + continue; + + if (!g_hEffects.Get(i, ChaosEffect::data)) + continue; + + ChaosEffect effect; + if (g_hEffects.GetArray(i, effect) && StrEqual(effect.effect_class, szEffectClass)) + { + KeyValues kv = new KeyValues("data"); + kv.Import(effect.data); + + if (FindKeyInSectionInKeyValues(kv, szSection, szKey)) + { + delete kv; + return true; + } + + delete kv; + } + } + + return false; +} + void SetChaosTimers(float flTime) { g_flTimeElapsed = 0.0; g_flMetaTimeElapsed = 0.0; + g_flTimerBarDisplayTime = flTime; } +void StartChaosTimers() +{ + SetChaosTimers(GetGameTime()); +} + +void StopChaosTimers() +{ + SetChaosTimers(-1.0); +} + +void SetChaosPaused(bool bPaused) +{ + g_bPaused = bPaused; +} + static void ConVarChanged_ChaosEnable(ConVar convar, const char[] oldValue, const char[] newValue) { if (g_bEnabled != convar.BoolValue) @@ -1090,14 +1102,14 @@ static Action ConCmd_SetNextEffect(int client, int args) int nIndex = g_hEffects.FindString(g_szForceEffectId); if (nIndex == -1) { - ReplyToCommand(client, "%t", "#Chaos_Effect_SetNextEffect_Invalid", g_szForceEffectId); + CReplyToCommand(client, "%t", "#Chaos_Effect_NotFound", g_szForceEffectId); } else { ChaosEffect effect; if (g_hEffects.GetArray(nIndex, effect)) { - ReplyToCommand(client, "%t", "#Chaos_Effect_SetNextEffect_Done", effect.name); + CReplyToCommand(client, "%t", "#Chaos_Effect_SetNextEffect_Success", effect.name); } } @@ -1121,7 +1133,7 @@ static Action ConCmd_ForceEffect(int client, int args) int nIndex = g_hEffects.FindString(szEffectId); if (nIndex == -1) { - ReplyToCommand(client, "%t", "#Chaos_Effect_SetNextEffect_Invalid", szEffectId); + CReplyToCommand(client, "%t", "#Chaos_Effect_NotFound", szEffectId); } else { diff --git a/addons/sourcemod/scripting/chaos/data.sp b/addons/sourcemod/scripting/chaos/data.sp index a601369b..a4ff39cd 100644 --- a/addons/sourcemod/scripting/chaos/data.sp +++ b/addons/sourcemod/scripting/chaos/data.sp @@ -19,6 +19,7 @@ enum struct ChaosEffect char end_sound[PLATFORM_MAX_PATH]; ArrayList tags; KeyValues data; + char data_string[2048]; // Runtime data bool active; @@ -39,6 +40,7 @@ enum struct ChaosEffect this.meta = kv.GetNum("meta") != 0; kv.GetString("effect_class", this.effect_class, sizeof(this.effect_class)); kv.GetString("script_file", this.script_file, sizeof(this.script_file)); + kv.GetString("data", this.data_string, sizeof(this.data_string)); kv.GetString("start_sound", this.start_sound, sizeof(this.start_sound)); kv.GetString("end_sound", this.end_sound, sizeof(this.end_sound)); @@ -68,16 +70,14 @@ enum struct ChaosEffect Function GetCallbackFunction(const char[] szKey, Handle hPlugin = null) { if (!this.effect_class[0]) - { return INVALID_FUNCTION; - } char szFunctionName[64]; Format(szFunctionName, sizeof(szFunctionName), "%s_%s", this.effect_class, szKey); return GetFunctionByName(hPlugin, szFunctionName); } - bool GetName(char[] szName, int iMaxLength) + bool GetDisplayName(char[] szName, int iMaxLength, int client = 0) { // This callback only applies to the current effect Function fnCallback = this.GetCallbackFunction("ModifyEffectName"); @@ -91,11 +91,15 @@ enum struct ChaosEffect bool bReturn; if (Call_Finish(bReturn) == SP_ERROR_NONE && bReturn) { - return bReturn; + if (TranslationPhraseExists(szName)) + Format(szName, iMaxLength, "%T", szName, client); + + return true; } } - return strcopy(szName, iMaxLength, this.name) != 0; + // Attempt to translate, or return the phrase as-is if it doesn't exist in translations + return TranslationPhraseExists(this.name) ? Format(szName, iMaxLength, "%T", this.name, client) : strcopy(szName, iMaxLength, this.name); } bool IsCompatibleWithActiveEffects() @@ -131,16 +135,6 @@ enum struct ChaosEffect } } -enum struct ChatConfig -{ - char tag[64]; - - void Parse(KeyValues kv) - { - kv.GetString("tag", this.tag, sizeof(this.tag)); - } -} - enum struct ProgressBarConfig { int num_blocks; @@ -161,12 +155,13 @@ enum struct ProgressBarConfig } } -bool Data_InitializeEffects(GameData hGameData) +bool Data_InitializeEffects() { char szFilePath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, szFilePath, sizeof(szFilePath), "configs/chaos/effects.cfg"); - + bool bSuccess = true; + StringMap hInitializedClasses = new StringMap(); KeyValues kv = new KeyValues("effects"); if (kv.ImportFromFile(szFilePath)) @@ -177,44 +172,58 @@ bool Data_InitializeEffects(GameData hGameData) { ChaosEffect effect; effect.Parse(kv); - + if (g_hEffects.FindString(effect.id) != -1) { LogError("Effect '%T' has duplicate ID '%s', skipping...", effect.name, LANG_SERVER, effect.id); continue; } - - Function fnCallback = effect.GetCallbackFunction("Initialize"); - if (fnCallback != INVALID_FUNCTION) + + // Only call Initialize once per effect class + if (effect.effect_class[0] && !hInitializedClasses.ContainsKey(effect.effect_class)) { - Call_StartFunction(null, fnCallback); - Call_PushArray(effect, sizeof(effect)); - Call_PushCell(hGameData); - - // If Initialize throws or returns false, the effect is not added to our list - bool bReturn; - if (Call_Finish(bReturn) != SP_ERROR_NONE || !bReturn) + Function fnCallback = effect.GetCallbackFunction("Initialize"); + if (fnCallback != INVALID_FUNCTION) { - LogMessage("Failed to add effect '%T' (%s) to effects list", effect.name, LANG_SERVER, effect.id); - continue; + Call_StartFunction(null, fnCallback); + Call_PushArray(effect, sizeof(effect)); + + // If Initialize throws or returns false, effects using this class are not added + bool bReturn; + if (Call_Finish(bReturn) != SP_ERROR_NONE || !bReturn) + { + LogMessage("Failed to initialize effect class '%s'", effect.effect_class); + hInitializedClasses.SetValue(effect.effect_class, false); + continue; + } } + + hInitializedClasses.SetValue(effect.effect_class, true); } - + else if (effect.effect_class[0]) + { + // Check if this effect class failed to initialize previously + bool bInitialized; + if (hInitializedClasses.GetValue(effect.effect_class, bInitialized) && !bInitialized) + continue; + } + g_hEffects.PushArray(effect); } while (kv.GotoNextKey(false)); kv.GoBack(); } kv.GoBack(); - + LogMessage("Registered %d effects", g_hEffects.Length); } else { - LogError("Could not read from file '%s'", szFilePath); + SetFailState("Could not read from file '%s'", szFilePath); bSuccess = false; } - + + delete hInitializedClasses; return bSuccess; } @@ -226,12 +235,6 @@ void Data_Initialize() KeyValues kv = new KeyValues("visuals"); if (kv.ImportFromFile(szFilePath)) { - if (kv.JumpToKey("chat")) - { - g_stChatConfig.Parse(kv); - } - kv.GoBack(); - if (kv.JumpToKey("timer_bar")) { g_stTimerBarConfig.Parse(kv); @@ -246,7 +249,7 @@ void Data_Initialize() } else { - LogError("Could not read from file '%s'", szFilePath); + SetFailState("Could not read from file '%s'", szFilePath); } delete kv; } diff --git a/addons/sourcemod/scripting/chaos/effects/add_attribute.sp b/addons/sourcemod/scripting/chaos/effects/add_attribute.sp new file mode 100644 index 00000000..443cfded --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/add_attribute.sp @@ -0,0 +1,133 @@ +#pragma semicolon 1 +#pragma newdecls required + +public bool AddAttribute_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + // Don't set the same attribute twice + if (IsAlreadyActive(effect)) + return false; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + ApplyAttributesToPlayer(effect, client); + } + + return true; +} + +public void AddAttribute_OnEnd(ChaosEffect effect) +{ + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + ApplyAttributesToPlayer(effect, client, true); + } +} + +public void AddAttribute_OnPlayerSpawnPost(ChaosEffect effect, int client) +{ + ApplyAttributesToPlayer(effect, client); +} + +public void AddAttribute_OnPostInventoryApplication(ChaosEffect effect, int client) +{ + ApplyAttributesToPlayer(effect, client); +} + +static bool IsAlreadyActive(ChaosEffect effect) +{ + KeyValues kv = effect.data; + + if (!kv.JumpToKey("attributes", false)) + return false; + + bool bFoundKey = false; + if (kv.GotoFirstSubKey(false)) + { + do + { + char szAttrib[64]; + if (kv.GetSectionName(szAttrib, sizeof(szAttrib)) && FindKeyInActiveEffects(effect.effect_class, szAttrib)) + { + bFoundKey = true; + break; + } + } + while (kv.GotoNextKey(false)); + kv.GoBack(); + } + kv.GoBack(); + + return bFoundKey; +} + +static void ApplyAttributesToPlayer(ChaosEffect effect, int client, bool bRemove = false) +{ + KeyValues kv = effect.data; + bool bApplyToItems = kv.GetNum("apply_to_items") != 0; + + if (!kv.JumpToKey("attributes", false)) + return; + + if (kv.GotoFirstSubKey(false)) + { + do + { + char szAttrib[64]; + if (kv.GetSectionName(szAttrib, sizeof(szAttrib))) + ApplyAttribute(client, szAttrib, kv.GetFloat(NULL_STRING), bApplyToItems, bRemove); + } + while (kv.GotoNextKey(false)); + kv.GoBack(); + } + kv.GoBack(); + + TF2Util_UpdatePlayerSpeed(client); +} + +static void ApplyAttribute(int client, const char[] szAttrib, float flValue, bool bApplyToItems, bool bRemove) +{ + if (bApplyToItems) + { + int nMaxWeapons = GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); + for (int i = 0; i < nMaxWeapons; i++) + { + int weapon = GetEntPropEnt(client, Prop_Send, "m_hMyWeapons", i); + if (weapon == -1) + continue; + + if (bRemove) + TF2Attrib_RemoveByName(weapon, szAttrib); + else + TF2Attrib_SetByName(weapon, szAttrib, flValue); + } + + int nMaxWearables = TF2Util_GetPlayerWearableCount(client); + for (int i = 0; i < nMaxWearables; i++) + { + int wearable = TF2Util_GetPlayerWearable(client, i); + if (wearable == -1) + continue; + + if (bRemove) + TF2Attrib_RemoveByName(wearable, szAttrib); + else + TF2Attrib_SetByName(wearable, szAttrib, flValue); + } + } + else + { + if (bRemove) + TF2Attrib_RemoveCustomPlayerAttribute(client, szAttrib); + else + TF2Attrib_AddCustomPlayerAttribute(client, szAttrib, flValue); + } +} diff --git a/addons/sourcemod/scripting/chaos/effects/add_cond.sp b/addons/sourcemod/scripting/chaos/effects/add_cond.sp new file mode 100644 index 00000000..fed15647 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/add_cond.sp @@ -0,0 +1,147 @@ +#pragma semicolon 1 +#pragma newdecls required + +public void AddCond_OnMapStart(ChaosEffect effect) +{ + // Halloween Ghost + PrecacheModel("models/props_halloween/ghost_no_hat.mdl"); + PrecacheModel("models/props_halloween/ghost_no_hat_red.mdl"); + PrecacheScriptSound("Halloween.GhostBoo"); + + // Bumper Cars + PrecacheModel("models/player/items/taunts/bumpercar/parts/bumpercar.mdl"); + PrecacheModel("models/props_halloween/bumpercar_cage.mdl"); + PrecacheScriptSound("BumperCar.Spawn"); + PrecacheScriptSound("BumperCar.SpawnFromLava"); + PrecacheScriptSound("BumperCar.GoLoop"); + PrecacheScriptSound("BumperCar.Screech"); + PrecacheScriptSound("BumperCar.HitGhost"); + PrecacheScriptSound("BumperCar.Bump"); + PrecacheScriptSound("BumperCar.BumpHard"); + PrecacheScriptSound("BumperCar.BumpIntoAir"); + PrecacheScriptSound("BumperCar.SpeedBoostStart"); + PrecacheScriptSound("BumperCar.SpeedBoostStop"); + PrecacheScriptSound("BumperCar.Jump"); + PrecacheScriptSound("BumperCar.JumpLand"); +} + +public bool AddCond_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + if (!effect.data.JumpToKey("conditions")) + return false; + + // Check for duplicate conditions in active effects + if (effect.data.GotoFirstSubKey(false)) + { + do + { + char szCondition[12]; + effect.data.GetString(NULL_STRING, szCondition, sizeof(szCondition)); + + if (FindKeyValuePairInActiveEffects(effect.effect_class, "conditions", szCondition)) + { + effect.data.GoBack(); // Go back to "conditions" + effect.data.GoBack(); // Go back to root + return false; + } + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + // Apply all conditions + if (effect.data.GotoFirstSubKey(false)) + { + do + { + TFCond nCondition = view_as(effect.data.GetNum(NULL_STRING)); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + TF2_AddCondition(client, nCondition); + } + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + effect.data.GoBack(); + return true; +} + +public void AddCond_OnEnd(ChaosEffect effect) +{ + if (!effect.data.JumpToKey("conditions")) + return; + + if (effect.data.GotoFirstSubKey(false)) + { + do + { + TFCond nCondition = view_as(effect.data.GetNum(NULL_STRING)); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + TF2_RemoveCondition(client, nCondition); + } + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + effect.data.GoBack(); +} + +public void AddCond_OnPlayerSpawn(ChaosEffect effect, int client) +{ + if (!effect.data.JumpToKey("conditions")) + return; + + if (effect.data.GotoFirstSubKey(false)) + { + do + { + TF2_AddCondition(client, view_as(effect.data.GetNum(NULL_STRING))); + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + effect.data.GoBack(); +} + +public void AddCond_OnConditionRemoved(ChaosEffect effect, int client, TFCond condition) +{ + if (!effect.data.JumpToKey("conditions")) + return; + + if (effect.data.GotoFirstSubKey(false)) + { + do + { + if (view_as(effect.data.GetNum(NULL_STRING)) == condition) + { + TF2_AddCondition(client, condition); + break; + } + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + effect.data.GoBack(); +} diff --git a/addons/sourcemod/scripting/chaos/effects/addcond.sp b/addons/sourcemod/scripting/chaos/effects/addcond.sp deleted file mode 100644 index 37fc2278..00000000 --- a/addons/sourcemod/scripting/chaos/effects/addcond.sp +++ /dev/null @@ -1,70 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -public void AddCond_OnMapStart(ChaosEffect effect) -{ - // Halloween Ghost - PrecacheModel("models/props_halloween/ghost_no_hat.mdl"); - PrecacheModel("models/props_halloween/ghost_no_hat_red.mdl"); - PrecacheScriptSound("Halloween.GhostBoo"); - - // Bumper Cars - PrecacheModel("models/player/items/taunts/bumpercar/parts/bumpercar.mdl"); - PrecacheModel("models/props_halloween/bumpercar_cage.mdl"); - PrecacheScriptSound("BumperCar.Spawn"); - PrecacheScriptSound("BumperCar.SpawnFromLava"); - PrecacheScriptSound("BumperCar.GoLoop"); - PrecacheScriptSound("BumperCar.Screech"); - PrecacheScriptSound("BumperCar.HitGhost"); - PrecacheScriptSound("BumperCar.Bump"); - PrecacheScriptSound("BumperCar.BumpHard"); - PrecacheScriptSound("BumperCar.BumpIntoAir"); - PrecacheScriptSound("BumperCar.SpeedBoostStart"); - PrecacheScriptSound("BumperCar.SpeedBoostStop"); - PrecacheScriptSound("BumperCar.Jump"); - PrecacheScriptSound("BumperCar.JumpLand"); -} - -public bool AddCond_OnStart(ChaosEffect effect) -{ - if (!effect.data) - return false; - - TFCond nCondition = view_as(effect.data.GetNum("condition")); - - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - TF2_AddCondition(client, nCondition); - } - - return true; -} - -public void AddCond_OnEnd(ChaosEffect effect) -{ - TFCond nCondition = view_as(effect.data.GetNum("condition")); - - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - TF2_RemoveCondition(client, nCondition); - } -} - -public void AddCond_OnPlayerSpawn(ChaosEffect effect, int client) -{ - TF2_AddCondition(client, view_as(effect.data.GetNum("condition"))); -} - -public void AddCond_OnConditionRemoved(ChaosEffect effect, int client, TFCond condition) -{ - if (view_as(effect.data.GetNum("condition")) == condition) - { - TF2_AddCondition(client, condition); - } -} diff --git a/addons/sourcemod/scripting/chaos/effects/birds.sp b/addons/sourcemod/scripting/chaos/effects/birds.sp deleted file mode 100644 index f77d8ecd..00000000 --- a/addons/sourcemod/scripting/chaos/effects/birds.sp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -static Handle g_hSDKCallSpawnClientsideFlyingBird; -static float g_flNextBirdSpawnTime[MAXPLAYERS + 1]; - -public bool SpawnBirds_Initialize(ChaosEffect effect, GameData gameconf) -{ - if (!gameconf) - return false; - - StartPrepSDKCall(SDKCall_Static); - PrepSDKCall_SetFromConf(gameconf, SDKConf_Signature, "SpawnClientsideFlyingBird"); - PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef); - g_hSDKCallSpawnClientsideFlyingBird = EndPrepSDKCall(); - - return g_hSDKCallSpawnClientsideFlyingBird != null; -} - -public bool SpawnBirds_OnStart(ChaosEffect effect) -{ - for (int client = 1; client <= MaxClients; client++) - { - g_flNextBirdSpawnTime[client] = GetGameTime(); - } - - return true; -} - -public void SpawnBirds_Update(ChaosEffect effect) -{ - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - if (!IsPlayerAlive(client)) - continue; - - if (g_flNextBirdSpawnTime[client] > GetGameTime()) - continue; - - g_flNextBirdSpawnTime[client] = GetGameTime() + GetRandomFloat(0.5, 1.0); - - float vecCenter[3]; - WorldSpaceCenter(client, vecCenter); - SDKCall(g_hSDKCallSpawnClientsideFlyingBird, vecCenter); - } -} diff --git a/addons/sourcemod/scripting/chaos/effects/burn_player.sp b/addons/sourcemod/scripting/chaos/effects/burn_player.sp new file mode 100644 index 00000000..b8a74316 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/burn_player.sp @@ -0,0 +1,23 @@ +#pragma semicolon 1 +#pragma newdecls required + +public bool BurnPlayer_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + float flDuration = effect.data.GetFloat("duration"); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + if (!IsPlayerAlive(client)) + continue; + + TF2_IgnitePlayer(client, client, flDuration); + } + + return true; +} diff --git a/addons/sourcemod/scripting/chaos/effects/cattoguns.sp b/addons/sourcemod/scripting/chaos/effects/cattoguns.sp deleted file mode 100644 index f40d5b11..00000000 --- a/addons/sourcemod/scripting/chaos/effects/cattoguns.sp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -static char g_aCatSounds[][] = -{ - "items/halloween/cat01.wav", - "items/halloween/cat02.wav", - "items/halloween/cat03.wav", -}; - -public bool CattoGuns_OnStart(ChaosEffect effect) -{ - AddNormalSoundHook(OnNormalSoundPlayed); - - return true; -} - -public void CattoGuns_OnEnd(ChaosEffect effect) -{ - RemoveNormalSoundHook(OnNormalSoundPlayed); -} - -static Action OnNormalSoundPlayed(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) -{ - int start = StrContains(sample, "weapons/"); - if (start == -1) - return Plugin_Continue; - - // Make sure to keep sound chars intact - strcopy(sample, start + 1, sample); - StrCat(sample, sizeof(sample), g_aCatSounds[GetRandomInt(0, sizeof(g_aCatSounds) - 1)]); - - PrecacheSound(sample); - - return Plugin_Changed; -} diff --git a/addons/sourcemod/scripting/chaos/effects/decompiled.sp b/addons/sourcemod/scripting/chaos/effects/decompiled.sp index df3b2488..ca0554d4 100644 --- a/addons/sourcemod/scripting/chaos/effects/decompiled.sp +++ b/addons/sourcemod/scripting/chaos/effects/decompiled.sp @@ -21,7 +21,7 @@ static ArrayList g_hCreatedVisuals; static StringMap g_hEntityToSpriteMap; static StringMap g_hEntityToModelMap; -public bool Decompiled_Initialize(ChaosEffect effect, GameData gameconf) +public bool Decompiled_Initialize(ChaosEffect effect) { showtriggers = FindConVar("showtriggers"); @@ -33,7 +33,6 @@ public bool Decompiled_Initialize(ChaosEffect effect, GameData gameconf) g_hEntityToSpriteMap.SetString("color_correction", "editor/color_correction.vmt"); g_hEntityToSpriteMap.SetString("env_cubemap", "editor/env_cubemap.vmt"); g_hEntityToSpriteMap.SetString("env_global", "editor/env_global.vmt"); - g_hEntityToSpriteMap.SetString("env_global", "editor/obsolete.vmt"); g_hEntityToSpriteMap.SetString("env_explosion", "editor/env_explosion.vmt"); g_hEntityToSpriteMap.SetString("env_fog_controller", "editor/fog_controller.vmt"); g_hEntityToSpriteMap.SetString("env_shake", "editor/env_shake.vmt"); @@ -120,7 +119,7 @@ public void Decompiled_OnMapStart(ChaosEffect effect) EntityLumpEntry entry = EntityLump.Get(i); int index = entry.FindKey("classname"); - if (index == 1) + if (index == -1) continue; char classname[64]; @@ -245,7 +244,7 @@ static void SpawnLightsFromData() { if (g_hLightData.Length == 0) { - LogMessage("No light data found! Restart the map to allow OnMapInit to parse light entities."); + LogMessage("No light data found! Restart the map to allow OnMapStart to parse light entities."); return; } @@ -369,37 +368,16 @@ static int CreateModel(const char[] szModel, const float vecOrigin[3], const flo return -1; } -static int GetCurrentEntities() -{ - int nCurrentEntities = 0; - - int entity = -1; - while ((entity = FindEntityByClassname(entity, "*")) != -1) - { - nCurrentEntities++; - } - - return nCurrentEntities; -} - static bool ShouldSpawnVisual() { // Don't spawn more entities if we're already near the limit - return float(GetCurrentEntities()) / float(GetMaxEntities()) < 0.95; + return float(GetNumEdicts()) / float(GetMaxEntities()) < 0.9; } static void ShowTriggers_Toggle() { - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - // We can't use ServerCommand because it is delayed by a frame - SetCommandFlags("showtriggers_toggle", GetCommandFlags("showtriggers_toggle") & ~FCVAR_CHEAT); - FakeClientCommand(client, "showtriggers_toggle"); - SetCommandFlags("showtriggers_toggle", GetCommandFlags("showtriggers_toggle") | FCVAR_CHEAT); - - break; - } + SetCommandFlags("showtriggers_toggle", GetCommandFlags("showtriggers_toggle") & ~FCVAR_CHEAT); + ServerCommand("showtriggers_toggle"); + ServerExecute(); + SetCommandFlags("showtriggers_toggle", GetCommandFlags("showtriggers_toggle") | FCVAR_CHEAT); } diff --git a/addons/sourcemod/scripting/chaos/effects/disable_direction.sp b/addons/sourcemod/scripting/chaos/effects/disable_direction.sp new file mode 100644 index 00000000..99269ea9 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/disable_direction.sp @@ -0,0 +1,28 @@ +#pragma semicolon 1 +#pragma newdecls required + +static Dir_t g_nDirection; + +public bool DisableDirection_OnStart(ChaosEffect effect) +{ + g_nDirection = view_as(GetRandomInt(DIR_FWD, DIR_RIGHT)); + + return true; +} + +public Action DisableDirection_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) +{ + if (!IsPlayerAlive(client)) + return Plugin_Continue; + + if ((g_nDirection == DIR_FWD && vel[0] > 0.0) || (g_nDirection == DIR_BACK && vel[0] < 0.0)) + vel[0] = 0.0; + else if ((g_nDirection == DIR_RIGHT && vel[1] > 0.0) || (g_nDirection == DIR_LEFT && vel[1] < 0.0)) + vel[1] = 0.0; + else if ((g_nDirection == DIR_UP && vel[2] > 0.0) || (g_nDirection == DIR_DOWN && vel[2] < 0.0)) + vel[2] = 0.0; + else + return Plugin_Continue; + + return Plugin_Changed; +} diff --git a/addons/sourcemod/scripting/chaos/effects/disablerandomdirection.sp b/addons/sourcemod/scripting/chaos/effects/disablerandomdirection.sp deleted file mode 100644 index 5949df25..00000000 --- a/addons/sourcemod/scripting/chaos/effects/disablerandomdirection.sp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -enum Direction -{ - Direction_Forward, - Direction_Back, - Direction_Right, - Direction_Left, -} - -static Direction g_nDirection; - -public bool DisableRandomDirection_OnStart(ChaosEffect effect) -{ - g_nDirection = view_as(GetRandomInt(view_as(Direction_Forward), view_as(Direction_Left))); - - return true; -} - -public Action DisableRandomDirection_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) -{ - if (!IsPlayerAlive(client)) - return Plugin_Continue; - - if (g_nDirection == Direction_Forward && vel[0] > 0.0 || g_nDirection == Direction_Back && vel[0] < 0.0) - { - vel[0] = 0.0; - } - else if (g_nDirection == Direction_Right && vel[1] > 0.0 || g_nDirection == Direction_Left && vel[1] < 0.0) - { - vel[1] = 0.0; - } - - return Plugin_Changed; -} diff --git a/addons/sourcemod/scripting/chaos/effects/disassemblemap.sp b/addons/sourcemod/scripting/chaos/effects/disassemble_map.sp similarity index 92% rename from addons/sourcemod/scripting/chaos/effects/disassemblemap.sp rename to addons/sourcemod/scripting/chaos/effects/disassemble_map.sp index b547a1bc..de61bc0b 100644 --- a/addons/sourcemod/scripting/chaos/effects/disassemblemap.sp +++ b/addons/sourcemod/scripting/chaos/effects/disassemble_map.sp @@ -41,11 +41,10 @@ public bool DisassembleMap_OnStart(ChaosEffect effect) } } - AcceptEntityInput(converter, "ConvertTarget"); + g_bActivated = AcceptEntityInput(converter, "ConvertTarget"); RemoveEntity(converter); - g_bActivated = true; - return true; + return g_bActivated; } public void DisassembleMap_OnRoundStart(ChaosEffect effect) diff --git a/addons/sourcemod/scripting/chaos/effects/earthquake.sp b/addons/sourcemod/scripting/chaos/effects/earthquake.sp index 9ece23ed..e99f34a5 100644 --- a/addons/sourcemod/scripting/chaos/effects/earthquake.sp +++ b/addons/sourcemod/scripting/chaos/effects/earthquake.sp @@ -1,17 +1,8 @@ #pragma semicolon 1 #pragma newdecls required -enum ShakeCommand_t -{ - SHAKE_START = 0, // Starts the screen shake for all players within the radius. - SHAKE_STOP, // Stops the screen shake for all players within the radius. - SHAKE_AMPLITUDE, // Modifies the amplitude of an active screen shake for all players within the radius. - SHAKE_FREQUENCY, // Modifies the frequency of an active screen shake for all players within the radius. - SHAKE_START_RUMBLEONLY, // Starts a shake effect that only rumbles the controller, no screen effect. - SHAKE_START_NORUMBLE, // Starts a shake that does NOT rumble the controller. -}; - -static ChaosEffect g_hEffect; +#define EARTHQUAKE_AMPLITUDE 15.0 +#define EARTHQUAKE_FREQUENCY 150.0 public void Earthquake_OnStartPost(ChaosEffect effect) { @@ -19,10 +10,10 @@ public void Earthquake_OnStartPost(ChaosEffect effect) { if (!IsClientInGame(client)) continue; - + if (GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") != -1) - Shake(client, SHAKE_START, effect.current_duration); - + UTIL_ScreenShake(client, SHAKE_START, EARTHQUAKE_AMPLITUDE, EARTHQUAKE_FREQUENCY, effect.current_duration); + SDKHook(client, SDKHook_GroundEntChangedPost, OnGroundEntChangedPost); } } @@ -33,20 +24,14 @@ public void Earthquake_OnEnd(ChaosEffect effect) { if (!IsClientInGame(client)) continue; - + if (GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") != -1) - Shake(client, SHAKE_STOP, effect.current_duration); - + UTIL_ScreenShake(client, SHAKE_STOP, EARTHQUAKE_AMPLITUDE, EARTHQUAKE_FREQUENCY, effect.current_duration); + SDKUnhook(client, SDKHook_GroundEntChangedPost, OnGroundEntChangedPost); } } -public void Earthquake_Update(ChaosEffect effect) -{ - // Update cached effect for use in GroundEntChanged hook - g_hEffect = effect; -} - public void Earthquake_OnClientPutInServer(ChaosEffect effect, int client) { SDKHook(client, SDKHook_GroundEntChangedPost, OnGroundEntChangedPost); @@ -54,16 +39,13 @@ public void Earthquake_OnClientPutInServer(ChaosEffect effect, int client) static void OnGroundEntChangedPost(int client) { - float flDuration = g_hEffect.activate_time + g_hEffect.current_duration - GetGameTime(); - Shake(client, GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1 ? SHAKE_START : SHAKE_STOP, flDuration); -} + ChaosEffect effect; + if (!GetActiveEffectByClass("Earthquake", effect)) + return; -static void Shake(int client, ShakeCommand_t eCommand, float flDuration) -{ - BfWrite bf = UserMessageToBfWrite(StartMessageOne("Shake", client)); - bf.WriteByte(view_as(eCommand)); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE) - bf.WriteFloat(15.0); // shake magnitude/amplitude - bf.WriteFloat(150.0); // shake noise frequency - bf.WriteFloat(flDuration); // shake lasts this long - EndMessage(); + bool bOnGround = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") != -1; + float flDuration = effect.activate_time + effect.current_duration - GetGameTime(); + + UTIL_ScreenShake(client, SHAKE_STOP, EARTHQUAKE_AMPLITUDE, EARTHQUAKE_FREQUENCY, 0.0); + UTIL_ScreenShake(client, SHAKE_START, EARTHQUAKE_AMPLITUDE, EARTHQUAKE_FREQUENCY, bOnGround ? 1.0 : flDuration); } diff --git a/addons/sourcemod/scripting/chaos/effects/enableallholidays.sp b/addons/sourcemod/scripting/chaos/effects/enable_all_holidays.sp similarity index 79% rename from addons/sourcemod/scripting/chaos/effects/enableallholidays.sp rename to addons/sourcemod/scripting/chaos/effects/enable_all_holidays.sp index a63d4054..43e39433 100644 --- a/addons/sourcemod/scripting/chaos/effects/enableallholidays.sp +++ b/addons/sourcemod/scripting/chaos/effects/enable_all_holidays.sp @@ -3,13 +3,9 @@ static DynamicDetour g_hDetourIsHolidayActive; -public bool EnableAllHolidays_Initialize(ChaosEffect effect, GameData gameconf) +public bool EnableAllHolidays_Initialize(ChaosEffect effect) { - if (!gameconf) - return false; - - g_hDetourIsHolidayActive = DynamicDetour.FromConf(gameconf, "TF_IsHolidayActive"); - + g_hDetourIsHolidayActive = Chaos_CreateDetour("TF_IsHolidayActive"); return g_hDetourIsHolidayActive != null; } diff --git a/addons/sourcemod/scripting/chaos/effects/fake_crash.sp b/addons/sourcemod/scripting/chaos/effects/fake_crash.sp new file mode 100644 index 00000000..23f8e7fa --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/fake_crash.sp @@ -0,0 +1,59 @@ +#pragma semicolon 1 +#pragma newdecls required + +static Handle g_hSDKCallSetPausedForced; +static ConVar net_fakeloss; + +public bool FakeCrash_Initialize(ChaosEffect effect) +{ + GameData gameconf; + if (!Chaos_LoadGameData(gameconf)) + return false; + + StartPrepSDKCall(SDKCall_Engine); + PrepSDKCall_SetFromConf(gameconf, SDKConf_Virtual, "IVEngineServer::SetPausedForced"); + PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_ByValue); + PrepSDKCall_AddParameter(SDKType_Float, SDKPass_ByValue); + g_hSDKCallSetPausedForced = EndPrepSDKCall(); + delete gameconf; + + if (!g_hSDKCallSetPausedForced) + { + LogError("Failed to create SDKCall for IVEngineServer::SetPausedForced"); + return false; + } + + net_fakeloss = FindConVar("net_fakeloss"); + + return true; +} + +public bool FakeCrash_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + // Fake crash already in progress + if (IsEffectOfClassActive(effect.effect_class) || net_fakeloss.IntValue != 0) + return false; + + float flMinDuration = effect.data.GetFloat("min_duration"); + float flMaxDuration = effect.data.GetFloat("max_duration"); + + net_fakeloss.IntValue = 100; + SetPausedForced(true); + CreateTimer(GetRandomFloat(flMinDuration, flMaxDuration), Timer_EndFakeCrash, _, TIMER_FLAG_NO_MAPCHANGE); + + return true; +} + +static void Timer_EndFakeCrash(Handle timer) +{ + SetPausedForced(false); + net_fakeloss.IntValue = 0; +} + +static void SetPausedForced(bool bPaused, float flDuration = -1.0) +{ + SDKCall(g_hSDKCallSetPausedForced, bPaused, flDuration); +} diff --git a/addons/sourcemod/scripting/chaos/effects/fakecrash.sp b/addons/sourcemod/scripting/chaos/effects/fakecrash.sp deleted file mode 100644 index d327e093..00000000 --- a/addons/sourcemod/scripting/chaos/effects/fakecrash.sp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -static ConVar net_fakeloss; -static Handle g_hFakeCrashTimer; - -public bool FakeCrash_Initialize(ChaosEffect effect, GameData gameconf) -{ - net_fakeloss = FindConVar("net_fakeloss"); - - return true; -} - -public bool FakeCrash_OnStart(ChaosEffect effect) -{ - // Fake crash already in progress - if (net_fakeloss.IntValue || g_hFakeCrashTimer) - return false; - - net_fakeloss.IntValue = 100; - g_hFakeCrashTimer = CreateTimer(GetRandomFloat(6.0, 12.0), Timer_StopFakeCrash); - - return true; -} - -static Action Timer_StopFakeCrash(Handle timer) -{ - if (g_hFakeCrashTimer != timer) - return Plugin_Continue; - - net_fakeloss.IntValue = 0; - g_hFakeCrashTimer = null; - - return Plugin_Continue; -} diff --git a/addons/sourcemod/scripting/chaos/effects/falldamage.sp b/addons/sourcemod/scripting/chaos/effects/fall_damage.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/falldamage.sp rename to addons/sourcemod/scripting/chaos/effects/fall_damage.sp diff --git a/addons/sourcemod/scripting/chaos/effects/flipviewmodels.sp b/addons/sourcemod/scripting/chaos/effects/flip_viewmodels.sp similarity index 83% rename from addons/sourcemod/scripting/chaos/effects/flipviewmodels.sp rename to addons/sourcemod/scripting/chaos/effects/flip_viewmodels.sp index ec553d30..fe8d117d 100644 --- a/addons/sourcemod/scripting/chaos/effects/flipviewmodels.sp +++ b/addons/sourcemod/scripting/chaos/effects/flip_viewmodels.sp @@ -8,7 +8,8 @@ public bool FlipViewModels_OnStart(ChaosEffect effect) if (!IsClientInGame(client)) continue; - for (int i = 0; i < GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); i++) + int nMaxWeapons = GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); + for (int i = 0; i < nMaxWeapons; i++) { int weapon = GetEntPropEnt(client, Prop_Send, "m_hMyWeapons", i); if (weapon == -1) @@ -35,7 +36,8 @@ public void FlipViewModels_OnEnd(ChaosEffect effect) if (!IsClientInGame(client)) continue; - for (int i = 0; i < GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); i++) + int nMaxWeapons = GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); + for (int i = 0; i < nMaxWeapons; i++) { int weapon = GetEntPropEnt(client, Prop_Send, "m_hMyWeapons", i); if (weapon == -1) diff --git a/addons/sourcemod/scripting/chaos/effects/floorislava.sp b/addons/sourcemod/scripting/chaos/effects/floorislava.sp deleted file mode 100644 index e63ea92a..00000000 --- a/addons/sourcemod/scripting/chaos/effects/floorislava.sp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -public void FloorIsLava_Update(ChaosEffect effect) -{ - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - if (!IsPlayerAlive(client)) - continue; - - if (!TF2_IsPlayerInCondition(client, TFCond_OnFire) && GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == 0) - { - TF2_IgnitePlayer(client, client, 3.0); - } - } -} diff --git a/addons/sourcemod/scripting/chaos/effects/jumpjump.sp b/addons/sourcemod/scripting/chaos/effects/force_jump.sp similarity index 62% rename from addons/sourcemod/scripting/chaos/effects/jumpjump.sp rename to addons/sourcemod/scripting/chaos/effects/force_jump.sp index e6c8bc50..1208cf7f 100644 --- a/addons/sourcemod/scripting/chaos/effects/jumpjump.sp +++ b/addons/sourcemod/scripting/chaos/effects/force_jump.sp @@ -1,7 +1,7 @@ #pragma semicolon 1 #pragma newdecls required -public Action JumpJump_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) +public Action ForceJump_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) { if (!IsPlayerAlive(client)) return Plugin_Continue; diff --git a/addons/sourcemod/scripting/chaos/effects/force_move.sp b/addons/sourcemod/scripting/chaos/effects/force_move.sp new file mode 100644 index 00000000..bc66f744 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/force_move.sp @@ -0,0 +1,51 @@ +#pragma semicolon 1 +#pragma newdecls required + +static Dir_t g_nDirection; + +static ConVar cl_forwardspeed; +static ConVar cl_backspeed; +static ConVar cl_sidespeed; +static ConVar cl_upspeed; + +public bool ForceMove_Initialize(ChaosEffect effect) +{ + cl_forwardspeed = FindConVar("cl_forwardspeed"); + cl_backspeed = FindConVar("cl_backspeed"); + cl_sidespeed = FindConVar("cl_sidespeed"); + cl_upspeed = FindConVar("cl_upspeed"); + + return true; +} + +public bool ForceMove_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + // Only allow one active at a time + if (IsEffectOfClassActive(effect.effect_class)) + return false; + + g_nDirection = view_as(effect.data.GetNum("direction", DIR_FWD)); + + return true; +} + +public Action ForceMove_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) +{ + if (!IsPlayerAlive(client)) + return Plugin_Continue; + + switch (g_nDirection) + { + case DIR_FWD: vel[0] = cl_forwardspeed.FloatValue; + case DIR_BACK: vel[0] = -cl_backspeed.FloatValue; + case DIR_LEFT: vel[1] = -cl_sidespeed.FloatValue; + case DIR_RIGHT: vel[1] = cl_sidespeed.FloatValue; + case DIR_UP: vel[2] = cl_upspeed.FloatValue; + default: return Plugin_Continue; + } + + return Plugin_Changed; +} diff --git a/addons/sourcemod/scripting/chaos/effects/forceforward.sp b/addons/sourcemod/scripting/chaos/effects/forceforward.sp deleted file mode 100644 index e2fc0afa..00000000 --- a/addons/sourcemod/scripting/chaos/effects/forceforward.sp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -public bool ForceForward_OnStart(ChaosEffect effect) -{ - return effect.data != null; -} - -public Action ForceForward_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) -{ - if (!IsPlayerAlive(client)) - return Plugin_Continue; - - vel[0] = effect.data.GetFloat("velocity"); - return Plugin_Changed; -} diff --git a/addons/sourcemod/scripting/chaos/effects/giveitem.sp b/addons/sourcemod/scripting/chaos/effects/give_item.sp similarity index 70% rename from addons/sourcemod/scripting/chaos/effects/giveitem.sp rename to addons/sourcemod/scripting/chaos/effects/give_item.sp index 06b74b4f..3dd79cd7 100644 --- a/addons/sourcemod/scripting/chaos/effects/giveitem.sp +++ b/addons/sourcemod/scripting/chaos/effects/give_item.sp @@ -3,19 +3,24 @@ static Handle g_hSDKCallPostInventoryApplication; -public bool GiveItem_Initialize(ChaosEffect effect, GameData gameconf) +public bool GiveItem_Initialize(ChaosEffect effect) { - if (!gameconf) + GameData gameconf; + if (!Chaos_LoadGameData(gameconf)) return false; - + + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetFromConf(gameconf, SDKConf_Signature, "CTFPlayer::PostInventoryApplication"); + g_hSDKCallPostInventoryApplication = EndPrepSDKCall(); + delete gameconf; + if (!g_hSDKCallPostInventoryApplication) { - StartPrepSDKCall(SDKCall_Player); - PrepSDKCall_SetFromConf(gameconf, SDKConf_Signature, "CTFPlayer::PostInventoryApplication"); - g_hSDKCallPostInventoryApplication = EndPrepSDKCall(); + LogError("Failed to create SDKCall for CTFPlayer::PostInventoryApplication"); + return false; } - - return g_hSDKCallPostInventoryApplication != null; + + return true; } public bool GiveItem_OnStart(ChaosEffect effect) @@ -45,14 +50,11 @@ static void AddItemsFromData(int client, KeyValues kv) { do { - char szItemName[64]; - kv.GetString("name", szItemName, sizeof(szItemName)); - - int item = AddItem(client, szItemName); - if (!IsValidEntity(item)) + int newItem = AddItem(client, kv); + if (newItem == -1) continue; - AddAttributesFromData(item, kv); + AddAttributesFromData(newItem, kv); } while (kv.GotoNextKey(false)); kv.GoBack(); @@ -61,36 +63,21 @@ static void AddItemsFromData(int client, KeyValues kv) } } -static void AddAttributesFromData(int item, KeyValues kv) +static int AddItem(int client, KeyValues kv) { - if (kv.JumpToKey("attributes", false)) - { - if (kv.GotoFirstSubKey(false)) - { - do - { - char szAttrib[64]; - if (kv.GetSectionName(szAttrib, sizeof(szAttrib))) - { - float flValue = kv.GetFloat(NULL_STRING); - TF2Attrib_SetByName(item, szAttrib, flValue); - } - } - while (kv.GotoNextKey(false)); - kv.GoBack(); - } - kv.GoBack(); - } -} + char szItemName[64]; + if (!kv.GetSectionName(szItemName, sizeof(szItemName))) + return -1; -static int AddItem(int client, const char[] szItemName) -{ int iItemDefIndex = GetItemDefinitionIndexByName(szItemName); + int iQuality = kv.GetNum("quality", 0); + int iLevel = kv.GetNum("level", 1); + if (iItemDefIndex != TF_ITEMDEF_DEFAULT) { // If we already have an item in that slot, remove it - TFClassType nClass = TF2_GetPlayerClass(client); - int iSlot = TF2Econ_GetItemLoadoutSlot(iItemDefIndex, nClass); + TFClassType iClass = TF2_GetPlayerClass(client); + int iSlot = TF2Econ_GetItemLoadoutSlot(iItemDefIndex, iClass); int nNewItemRegionMask = TF2Econ_GetItemEquipRegionMask(iItemDefIndex); if (IsWearableSlot(iSlot)) @@ -102,11 +89,10 @@ static int AddItem(int client, const char[] szItemName) if (wearable == -1) continue; - int iWearableDefIndex = GetEntProp(wearable, Prop_Send, "m_iItemDefinitionIndex"); - if (iWearableDefIndex == 0xFFFF) + if (!GetEntProp(wearable, Prop_Send, "m_bInitialized")) continue; - int nWearableRegionMask = TF2Econ_GetItemEquipRegionMask(iWearableDefIndex); + int nWearableRegionMask = TF2Econ_GetItemEquipRegionMask(GetEntProp(wearable, Prop_Send, "m_iItemDefinitionIndex")); if (nWearableRegionMask & nNewItemRegionMask) { TF2_RemoveWearable(client, wearable); @@ -127,11 +113,12 @@ static int AddItem(int client, const char[] szItemName) char szClassname[64]; TF2Econ_GetItemClassName(iItemDefIndex, szClassname, sizeof(szClassname)); - TF2Econ_TranslateWeaponEntForClass(szClassname, sizeof(szClassname), nClass); + TF2Econ_TranslateWeaponEntForClass(szClassname, sizeof(szClassname), iClass); TF2Items_SetClassname(hItem, szClassname); TF2Items_SetItemIndex(hItem, iItemDefIndex); - TF2Items_SetLevel(hItem, 1); + TF2Items_SetQuality(hItem, iQuality); + TF2Items_SetLevel(hItem, iLevel); int newItem = TF2Items_GiveNamedItem(client, hItem); if (newItem != -1) @@ -145,12 +132,12 @@ static int AddItem(int client, const char[] szItemName) EquipPlayerWeapon(client, newItem); TF2Util_SetPlayerActiveWeapon(client, newItem); } + + SetEntProp(newItem, Prop_Send, "m_bValidatedAttachedEntity", true); } - - SetEntProp(newItem, Prop_Send, "m_bValidatedAttachedEntity", true); - + SDKCall(g_hSDKCallPostInventoryApplication, client); - + delete hItem; return newItem; } @@ -164,3 +151,25 @@ static int AddItem(int client, const char[] szItemName) return -1; } + +static void AddAttributesFromData(int item, KeyValues kv) +{ + if (kv.JumpToKey("attributes", false)) + { + if (kv.GotoFirstSubKey(false)) + { + do + { + char szAttrib[64]; + if (kv.GetSectionName(szAttrib, sizeof(szAttrib))) + { + float flValue = kv.GetFloat(NULL_STRING); + TF2Attrib_SetByName(item, szAttrib, flValue); + } + } + while (kv.GotoNextKey(false)); + kv.GoBack(); + } + kv.GoBack(); + } +} diff --git a/addons/sourcemod/scripting/chaos/effects/grantorremoveallupgrades.sp b/addons/sourcemod/scripting/chaos/effects/grant_or_remove_all_upgrades.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/grantorremoveallupgrades.sp rename to addons/sourcemod/scripting/chaos/effects/grant_or_remove_all_upgrades.sp diff --git a/addons/sourcemod/scripting/chaos/effects/headshots.sp b/addons/sourcemod/scripting/chaos/effects/headshots.sp index aba26d68..78ff4af8 100644 --- a/addons/sourcemod/scripting/chaos/effects/headshots.sp +++ b/addons/sourcemod/scripting/chaos/effects/headshots.sp @@ -4,19 +4,12 @@ static DynamicHook g_hDHookOnWeaponSound; static ArrayList g_hDynamicHookIds; -public bool Headshots_Initialize(ChaosEffect effect, GameData gameconf) +public bool Headshots_Initialize(ChaosEffect effect) { - if (!gameconf) - return false; - - g_hDHookOnWeaponSound = DynamicHook.FromConf(gameconf, "CBaseCombatWeapon::WeaponSound"); - - if (!g_hDHookOnWeaponSound) - return false; - g_hDynamicHookIds = new ArrayList(); - - return true; + + g_hDHookOnWeaponSound = Chaos_CreateDynamicHook("CBaseCombatWeapon::WeaponSound"); + return g_hDHookOnWeaponSound != null; } public bool Headshots_OnStart(ChaosEffect effect) diff --git a/addons/sourcemod/scripting/chaos/effects/identity_theft.sp b/addons/sourcemod/scripting/chaos/effects/identity_theft.sp new file mode 100644 index 00000000..5ca08b51 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/identity_theft.sp @@ -0,0 +1,161 @@ +#pragma semicolon 1 +#pragma newdecls required + +static Handle g_hSDKCallGiveNamedItem; + +public bool IdentityTheft_Initialize(ChaosEffect effect) +{ + GameData gameconf; + if (!Chaos_LoadGameData(gameconf)) + return false; + + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetFromConf(gameconf, SDKConf_Virtual, "CTFPlayer::GiveNamedItem"); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_ByValue); + PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer); + g_hSDKCallGiveNamedItem = EndPrepSDKCall(); + delete gameconf; + + if (!g_hSDKCallGiveNamedItem) + { + LogError("Failed to create SDKCall for CTFPlayer::GiveNamedItem"); + return false; + } + + return true; +} + +public bool IdentityTheft_OnStart(ChaosEffect effect) +{ + HookEvent("player_death", OnPlayerDeath); + + return true; +} + +public void IdentityTheft_OnEnd(ChaosEffect effect) +{ + UnhookEvent("player_death", OnPlayerDeath); +} + +static void OnPlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + int victim = GetClientOfUserId(event.GetInt("userid")); + int attacker = GetClientOfUserId(event.GetInt("attacker")); + int death_flags = event.GetInt("death_flags"); + + if (victim != attacker && (0 < attacker <= MaxClients) && !(death_flags & TF_DEATHFLAG_DEADRINGER)) + { + TF2_SetPlayerClass(attacker, TF2_GetPlayerClass(victim), _, false); + TF2_RegeneratePlayer(attacker); + + // Copy victim's model. + char szCustomModel[PLATFORM_MAX_PATH]; + GetEntPropString(victim, Prop_Send, "m_iszCustomModel", szCustomModel, sizeof(szCustomModel)); + + SetVariantString(szCustomModel); + AcceptEntityInput(attacker, "SetCustomModel"); + + SetEntProp(attacker, Prop_Send, "m_bUseClassAnimations", GetEntProp(victim, Prop_Send, "m_bUseClassAnimations")); + SetEntProp(attacker, Prop_Send, "m_bCustomModelRotates", GetEntProp(victim, Prop_Send, "m_bCustomModelRotates")); + SetEntProp(attacker, Prop_Send, "m_bCustomModelRotationSet", GetEntProp(victim, Prop_Send, "m_bCustomModelRotationSet")); + SetEntProp(attacker, Prop_Send, "m_bCustomModelVisibleToSelf", GetEntProp(victim, Prop_Send, "m_bCustomModelVisibleToSelf")); + + float vecCustomModelOffset[3], angCustomModelRotation[3]; + GetEntPropVector(victim, Prop_Send, "m_vecCustomModelOffset", vecCustomModelOffset); + SetEntPropVector(attacker, Prop_Send, "m_vecCustomModelOffset", vecCustomModelOffset); + GetEntPropVector(victim, Prop_Send, "m_angCustomModelRotation", angCustomModelRotation); + SetEntPropVector(attacker, Prop_Send, "m_angCustomModelRotation", angCustomModelRotation); + + // Nuke items. + int nMaxWeapons = GetEntPropArraySize(attacker, Prop_Data, "m_hMyWeapons"); + for (int i = 0; i < nMaxWeapons; i++) + { + int weapon = GetEntPropEnt(attacker, Prop_Data, "m_hMyWeapons", i); + if (weapon == -1) + continue; + + if (TF2Util_GetWeaponID(weapon) == TF_WEAPON_BUILDER) + continue; + + RemovePlayerItem(attacker, weapon); + RemoveEntity(weapon); + } + + // Nuke wearables. + for (int wbl = TF2Util_GetPlayerWearableCount(attacker) - 1; wbl >= 0; wbl--) + { + int wearable = TF2Util_GetPlayerWearable(attacker, wbl); + if (wearable == -1) + continue; + + TF2_RemoveWearable(attacker, wearable); + } + + // Copy victim's weapons. + for (int i = 0; i < GetEntPropArraySize(victim, Prop_Data, "m_hMyWeapons"); i++) + { + int weapon = GetEntPropEnt(victim, Prop_Data, "m_hMyWeapons", i); + if (weapon == -1) + continue; + + int iItemOffset = FindItemOffset(weapon); + if (iItemOffset == -1) + continue; + + Address pItem = GetEntityAddress(weapon) + view_as
(iItemOffset); + if (!pItem) + continue; + + char szClassname[64]; + if (!GetEntityClassname(weapon, szClassname, sizeof(szClassname))) + continue; + + TF2Econ_TranslateWeaponEntForClass(szClassname, sizeof(szClassname), TF2_GetPlayerClass(attacker)); + + int newItem = SDKCall(g_hSDKCallGiveNamedItem, attacker, szClassname, 0, pItem, true); + if (newItem == -1) + continue; + + SetEntProp(newItem, Prop_Send, "m_bValidatedAttachedEntity", true); + EquipPlayerWeapon(attacker, newItem); + + // Switch to our victim's active weapon. + if (weapon == GetEntPropEnt(victim, Prop_Send, "m_hActiveWeapon")) + { + TF2Util_SetPlayerActiveWeapon(attacker, newItem); + } + } + + // Copy victim's wearables. + for (int wbl = TF2Util_GetPlayerWearableCount(victim) - 1; wbl >= 0; wbl--) + { + int wearable = TF2Util_GetPlayerWearable(victim, wbl); + if (wearable == -1) + continue; + + int iItemOffset = FindItemOffset(wearable); + if (iItemOffset == -1) + continue; + + Address pItem = GetEntityAddress(wearable) + view_as
(iItemOffset); + if (!pItem) + continue; + + char szClassname[64]; + if (!GetEntityClassname(wearable, szClassname, sizeof(szClassname))) + continue; + + TF2Econ_TranslateWeaponEntForClass(szClassname, sizeof(szClassname), TF2_GetPlayerClass(attacker)); + + int newItem = SDKCall(g_hSDKCallGiveNamedItem, attacker, szClassname, 0, pItem, true); + if (newItem == -1) + continue; + + SetEntProp(newItem, Prop_Send, "m_bValidatedAttachedEntity", true); + TF2Util_EquipPlayerWearable(attacker, newItem); + } + } +} diff --git a/addons/sourcemod/scripting/chaos/effects/identitytheft.sp b/addons/sourcemod/scripting/chaos/effects/identitytheft.sp deleted file mode 100644 index 9458b489..00000000 --- a/addons/sourcemod/scripting/chaos/effects/identitytheft.sp +++ /dev/null @@ -1,127 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -static Handle g_SDKCallGiveNamedItem; -static Handle g_hSDKCallPostInventoryApplication; -static Handle g_hSDKCallGetSubType; - -public bool IdentityTheft_Initialize(ChaosEffect effect, GameData gameconf) -{ - StartPrepSDKCall(SDKCall_Player); - PrepSDKCall_SetFromConf(gameconf, SDKConf_Virtual, "CTFPlayer::GiveNamedItem"); - PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_ByValue); - PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer); - g_SDKCallGiveNamedItem = EndPrepSDKCall(); - - StartPrepSDKCall(SDKCall_Player); - PrepSDKCall_SetFromConf(gameconf, SDKConf_Signature, "CTFPlayer::PostInventoryApplication"); - g_hSDKCallPostInventoryApplication = EndPrepSDKCall(); - - VScriptFunction hScriptGetSubType = VScript_GetClassFunction("CBaseCombatWeapon", "GetSubType"); - if (hScriptGetSubType) - g_hSDKCallGetSubType = hScriptGetSubType.CreateSDKCall(); - - return g_SDKCallGiveNamedItem && g_hSDKCallPostInventoryApplication && g_hSDKCallGetSubType; -} - -public bool IdentityTheft_OnStart(ChaosEffect effect) -{ - HookEvent("player_death", OnPlayerDeath); - - return true; -} - -public void IdentityTheft_OnEnd(ChaosEffect effect) -{ - UnhookEvent("player_death", OnPlayerDeath); -} - -static void OnPlayerDeath(Event event, const char[] name, bool dontBroadcast) -{ - int victim = GetClientOfUserId(event.GetInt("userid")); - int attacker = GetClientOfUserId(event.GetInt("attacker")); - int death_flags = GetClientOfUserId(event.GetInt("death_flags")); - - if (victim != attacker && 0 < attacker <= MaxClients && !(death_flags & TF_DEATHFLAG_DEADRINGER)) - { - TF2_SetPlayerClass(attacker, TF2_GetPlayerClass(victim), _, false); - - // Remove attacker's weapons - for (int i = 0; i < GetEntPropArraySize(attacker, Prop_Data, "m_hMyWeapons"); i++) - { - int weapon = GetEntPropEnt(attacker, Prop_Data, "m_hMyWeapons", i); - if (weapon == -1) - continue; - - RemovePlayerItem(attacker, weapon); - RemoveEntity(weapon); - } - - // Remove attacker's wearables - for (int wbl = TF2Util_GetPlayerWearableCount(attacker) - 1; wbl >= 0; wbl--) - { - int wearable = TF2Util_GetPlayerWearable(attacker, wbl); - if (wearable == -1) - continue; - - TF2_RemoveWearable(attacker, wearable); - } - - // Copy victim's weapons - for (int i = 0; i < GetEntPropArraySize(victim, Prop_Data, "m_hMyWeapons"); i++) - { - int weapon = GetEntPropEnt(victim, Prop_Data, "m_hMyWeapons", i); - if (weapon == -1) - continue; - - Address pItem = GetEntityAddress(weapon) + view_as
(FindItemOffset(weapon)); - if (!pItem) - continue; - - char szClassname[64]; - if (!GetEntityClassname(weapon, szClassname, sizeof(szClassname))) - continue; - - int newItem = SDKCall(g_SDKCallGiveNamedItem, attacker, szClassname, SDKCall(g_hSDKCallGetSubType, weapon), pItem, true); - if (newItem == -1) - continue; - - SetEntProp(newItem, Prop_Send, "m_bValidatedAttachedEntity", true); - EquipPlayerWeapon(attacker, newItem); - - // Switch to our victim's active weapon - if (GetEntPropEnt(victim, Prop_Send, "m_hActiveWeapon") == weapon) - { - TF2Util_SetPlayerActiveWeapon(attacker, newItem); - } - } - - // Copy victim's wearables - for (int wbl = TF2Util_GetPlayerWearableCount(victim) - 1; wbl >= 0; wbl--) - { - int wearable = TF2Util_GetPlayerWearable(victim, wbl); - if (wearable == -1) - continue; - - Address pItem = GetEntityAddress(wearable) + view_as
(FindItemOffset(wearable)); - if (!pItem) - continue; - - char szClassname[64]; - if (!GetEntityClassname(wearable, szClassname, sizeof(szClassname))) - continue; - - int newItem = SDKCall(g_SDKCallGiveNamedItem, attacker, szClassname, 0, pItem, true); - if (newItem == -1) - continue; - - SetEntProp(newItem, Prop_Send, "m_bValidatedAttachedEntity", true); - TF2Util_EquipPlayerWearable(attacker, newItem); - } - - SDKCall(g_hSDKCallPostInventoryApplication, attacker); - } -} diff --git a/addons/sourcemod/scripting/chaos/effects/invertconvar.sp b/addons/sourcemod/scripting/chaos/effects/invert_convar.sp similarity index 97% rename from addons/sourcemod/scripting/chaos/effects/invertconvar.sp rename to addons/sourcemod/scripting/chaos/effects/invert_convar.sp index 411ae58b..718903cd 100644 --- a/addons/sourcemod/scripting/chaos/effects/invertconvar.sp +++ b/addons/sourcemod/scripting/chaos/effects/invert_convar.sp @@ -27,9 +27,11 @@ public void InvertConVar_OnEnd(ChaosEffect effect) { char szName[512]; effect.data.GetString("convar", szName, sizeof(szName)); - + ConVar convar = FindConVar(szName); - + if (!convar) + return; + convar.RemoveChangeHook(OnConVarChanged); convar.FloatValue = -convar.FloatValue; } diff --git a/addons/sourcemod/scripting/chaos/effects/killrandomplayer.sp b/addons/sourcemod/scripting/chaos/effects/kill_random_player.sp similarity index 60% rename from addons/sourcemod/scripting/chaos/effects/killrandomplayer.sp rename to addons/sourcemod/scripting/chaos/effects/kill_random_player.sp index 51a0a93b..340f4855 100644 --- a/addons/sourcemod/scripting/chaos/effects/killrandomplayer.sp +++ b/addons/sourcemod/scripting/chaos/effects/kill_random_player.sp @@ -6,7 +6,9 @@ public bool KillRandomPlayer_OnStart(ChaosEffect effect) int client = GetRandomPlayer(); if (client == -1) return false; + + CPrintToChatAll("%t%t", "#Chaos_Tag", "#Chaos_Effect_KillRandomPlayer_Killed", client); + ForcePlayerSuicide(client, true); - ForcePlayerSuicide(client); return true; } diff --git a/addons/sourcemod/scripting/chaos/effects/manninthemachine.sp b/addons/sourcemod/scripting/chaos/effects/mann_in_the_machine.sp similarity index 94% rename from addons/sourcemod/scripting/chaos/effects/manninthemachine.sp rename to addons/sourcemod/scripting/chaos/effects/mann_in_the_machine.sp index 17d222dc..261474a5 100644 --- a/addons/sourcemod/scripting/chaos/effects/manninthemachine.sp +++ b/addons/sourcemod/scripting/chaos/effects/mann_in_the_machine.sp @@ -16,7 +16,7 @@ static char g_szBotModels[][] = "models/bots/engineer/bot_engineer.mdl" }; -char g_szBotBossModels[][] = +static char g_szBotBossModels[][] = { "", //TF_CLASS_UNDEFINED @@ -31,7 +31,7 @@ char g_szBotBossModels[][] = "models/bots/engineer/bot_engineer.mdl" }; -char g_szBotClassNames[][] = +static char g_szBotClassNames[][] = { "", //TF_CLASS_UNDEFINED @@ -145,7 +145,7 @@ static Action OnNormalSoundPlayed(int clients[MAXPLAYERS], int &numClients, char static bool IsValidRobotPlayer(int client) { - return (0 < client <= MaxClients) && IsClientInGame(client) && (!GameRules_GetProp("m_bPlayingMannVsMachine") || !(GetEntityFlags(client) & FL_FAKECLIENT)); + return (0 < client <= MaxClients) && IsClientInGame(client) && TF2_GetPlayerClass(client) > TFClass_Unknown && (!GameRules_GetProp("m_bPlayingMannVsMachine") || !(GetEntityFlags(client) & FL_FAKECLIENT)); } static void SetRobotModel(int client) @@ -158,14 +158,10 @@ static void SetRobotModel(int client) static void OnPlayerDeath(Event event, const char[] name, bool dontBroadcast) { int victim = GetClientOfUserId(event.GetInt("userid")); - int death_flags = GetClientOfUserId(event.GetInt("death_flags")); if (!IsValidRobotPlayer(victim)) return; - if (death_flags & TF_DEATHFLAG_DEADRINGER) - return; - if (GetEntProp(victim, Prop_Send, "m_bIsMiniBoss")) { TFClassType nClass = TF2_GetPlayerClass(victim); diff --git a/addons/sourcemod/scripting/chaos/effects/meta/effectduration.sp b/addons/sourcemod/scripting/chaos/effects/meta/effect_duration.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/meta/effectduration.sp rename to addons/sourcemod/scripting/chaos/effects/meta/effect_duration.sp diff --git a/addons/sourcemod/scripting/chaos/effects/meta/multi_effect.sp b/addons/sourcemod/scripting/chaos/effects/meta/multi_effect.sp new file mode 100644 index 00000000..a80916a5 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/meta/multi_effect.sp @@ -0,0 +1,47 @@ +// by pokemonpasta + +#pragma semicolon 1 +#pragma newdecls required + +static int g_iNumEffects; +static int g_iActivatedEffects; +static Handle g_hTimer; + +public bool MultiEffect_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + // Only allow one active at a time + if (IsEffectOfClassActive(effect.effect_class)) + return false; + + g_iNumEffects = effect.data.GetNum("effect_count"); + if (g_iNumEffects < 1) + return false; + + g_iActivatedEffects = 0; + float flNextEffectDelay = (effect.duration - 0.1) / float(g_iNumEffects); // n effects over m seconds + + g_hTimer = CreateTimer(flNextEffectDelay, Timer_NextEffect, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + + return true; +} + +public void MultiEffect_OnEnd(ChaosEffect effect) +{ + g_hTimer = null; +} + +static Action Timer_NextEffect(Handle timer) +{ + if (g_hTimer != timer) + return Plugin_Stop; + + SelectRandomEffect(false); // Don't allow meta effects within the multi + + if (++g_iActivatedEffects < g_iNumEffects) + return Plugin_Continue; + + return Plugin_Stop; +} diff --git a/addons/sourcemod/scripting/chaos/effects/meta/nochaos.sp b/addons/sourcemod/scripting/chaos/effects/meta/no_chaos.sp similarity index 57% rename from addons/sourcemod/scripting/chaos/effects/meta/nochaos.sp rename to addons/sourcemod/scripting/chaos/effects/meta/no_chaos.sp index e975d169..9f6f0a7a 100644 --- a/addons/sourcemod/scripting/chaos/effects/meta/nochaos.sp +++ b/addons/sourcemod/scripting/chaos/effects/meta/no_chaos.sp @@ -4,17 +4,14 @@ public bool NoChaos_OnStart(ChaosEffect effect) { ExpireAllActiveEffects(true); - - // Request to pause timer - g_bNoChaos = true; - SetChaosTimers(0.0); - + SetChaosPaused(true); + StopChaosTimers(); + return true; } public void NoChaos_OnEnd(ChaosEffect effect) { - // Resume chaos - g_bNoChaos = false; - SetChaosTimers(GetGameTime()); + SetChaosPaused(false); + StartChaosTimers(); } diff --git a/addons/sourcemod/scripting/chaos/effects/meta/reinvokeeffects.sp b/addons/sourcemod/scripting/chaos/effects/meta/reinvoke_effects.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/meta/reinvokeeffects.sp rename to addons/sourcemod/scripting/chaos/effects/meta/reinvoke_effects.sp diff --git a/addons/sourcemod/scripting/chaos/effects/meta/timerspeed.sp b/addons/sourcemod/scripting/chaos/effects/meta/timer_speed.sp similarity index 89% rename from addons/sourcemod/scripting/chaos/effects/meta/timerspeed.sp rename to addons/sourcemod/scripting/chaos/effects/meta/timer_speed.sp index 52df1518..7a0628f3 100644 --- a/addons/sourcemod/scripting/chaos/effects/meta/timerspeed.sp +++ b/addons/sourcemod/scripting/chaos/effects/meta/timer_speed.sp @@ -13,8 +13,8 @@ public bool TimerSpeed_OnStart(ChaosEffect effect) return true; } -public void TimerSpeed_ModifyTimerSpeed(ChaosEffect effect, float &flTimerSpeed) +public void TimerSpeed_ModifyTimerSpeed(ChaosEffect effect, float &speed) { float flMult = effect.data.GetFloat("multiplier"); - flTimerSpeed *= flMult; + speed *= flMult; } diff --git a/addons/sourcemod/scripting/chaos/effects/modifypitch.sp b/addons/sourcemod/scripting/chaos/effects/modify_pitch.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/modifypitch.sp rename to addons/sourcemod/scripting/chaos/effects/modify_pitch.sp diff --git a/addons/sourcemod/scripting/chaos/effects/wheredideverythinggo.sp b/addons/sourcemod/scripting/chaos/effects/no_transmit.sp similarity index 74% rename from addons/sourcemod/scripting/chaos/effects/wheredideverythinggo.sp rename to addons/sourcemod/scripting/chaos/effects/no_transmit.sp index 6b247ca8..7f795864 100644 --- a/addons/sourcemod/scripting/chaos/effects/wheredideverythinggo.sp +++ b/addons/sourcemod/scripting/chaos/effects/no_transmit.sp @@ -1,7 +1,7 @@ #pragma semicolon 1 #pragma newdecls required -public bool WhereDidEverythingGo_OnStart(ChaosEffect effect) +public bool NoTransmit_OnStart(ChaosEffect effect) { int entity = -1; while ((entity = FindEntityByClassname(entity, "*")) != -1) @@ -12,7 +12,7 @@ public bool WhereDidEverythingGo_OnStart(ChaosEffect effect) return true; } -public void WhereDidEverythingGo_OnEnd(ChaosEffect effect) +public void NoTransmit_OnEnd(ChaosEffect effect) { int entity = -1; while ((entity = FindEntityByClassname(entity, "*")) != -1) @@ -21,7 +21,7 @@ public void WhereDidEverythingGo_OnEnd(ChaosEffect effect) } } -public void WhereDidEverythingGo_OnEntityCreated(ChaosEffect effect, int entity, const char[] classname) +public void NoTransmit_OnEntityCreated(ChaosEffect effect, int entity, const char[] classname) { SDKHook(entity, SDKHook_SetTransmit, OnEntitySetTransmit); } @@ -31,6 +31,9 @@ static Action OnEntitySetTransmit(int entity, int client) if (entity == client) return Plugin_Continue; + if (IsClientObserver(client)) + return Plugin_Continue; + if (HasEntProp(entity, Prop_Send, "m_hOwnerEntity") && GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity") == client) return Plugin_Continue; diff --git a/addons/sourcemod/scripting/chaos/effects/nothing.sp b/addons/sourcemod/scripting/chaos/effects/nothing.sp index 4ef19655..a68a87fe 100644 --- a/addons/sourcemod/scripting/chaos/effects/nothing.sp +++ b/addons/sourcemod/scripting/chaos/effects/nothing.sp @@ -4,7 +4,7 @@ static ArrayList g_hFakeNames; static char g_szFakeName[64]; -public bool Nothing_Initialize(ChaosEffect effect, GameData gameconf) +public bool Nothing_Initialize(ChaosEffect effect) { if (!effect.data) return true; @@ -49,8 +49,8 @@ public bool Nothing_ModifyEffectName(ChaosEffect effect, char[] name, int maxlen if (!g_szFakeName[0]) return false; - if (effect.activate_time + 10.0 < GetGameTime()) + if (effect.activate_time + 8.0 < GetGameTime()) return false; - return strcopy(name, maxlength, g_szFakeName) != 0; + return strcopy(name, maxlength, g_szFakeName); } diff --git a/addons/sourcemod/scripting/chaos/effects/randomizeweaponorder.sp b/addons/sourcemod/scripting/chaos/effects/randomize_weapon_order.sp similarity index 73% rename from addons/sourcemod/scripting/chaos/effects/randomizeweaponorder.sp rename to addons/sourcemod/scripting/chaos/effects/randomize_weapon_order.sp index 09245986..faed98bd 100644 --- a/addons/sourcemod/scripting/chaos/effects/randomizeweaponorder.sp +++ b/addons/sourcemod/scripting/chaos/effects/randomize_weapon_order.sp @@ -1,29 +1,32 @@ #pragma semicolon 1 #pragma newdecls required -static Handle g_hSDKCallCanDeploy; static Handle g_hSDKCallCanBeSelected; static Handle g_hSDKCallGetSubType; -public bool RandomizeWeaponOrder_Initialize(ChaosEffect effect, GameData gameconf) +public bool RandomizeWeaponOrder_Initialize(ChaosEffect effect) { - if (!gameconf) - return false; - - StartPrepSDKCall(SDKCall_Entity); - PrepSDKCall_SetFromConf(gameconf, SDKConf_Virtual, "CBaseCombatWeapon::CanDeploy"); - PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_ByValue); - g_hSDKCallCanDeploy = EndPrepSDKCall(); - VScriptFunction hScriptGetSubType = VScript_GetClassFunction("CBaseCombatWeapon", "GetSubType"); if (hScriptGetSubType) g_hSDKCallGetSubType = hScriptGetSubType.CreateSDKCall(); - + + if (!g_hSDKCallGetSubType) + { + LogError("Failed to create SDKCall for CBaseCombatWeapon::GetSubType"); + return false; + } + VScriptFunction hScriptCanBeSelected = VScript_GetClassFunction("CBaseCombatWeapon", "CanBeSelected"); if (hScriptCanBeSelected) g_hSDKCallCanBeSelected = hScriptCanBeSelected.CreateSDKCall(); - - return g_hSDKCallCanDeploy && g_hSDKCallGetSubType && g_hSDKCallCanBeSelected; + + if (!g_hSDKCallCanBeSelected) + { + LogError("Failed to create SDKCall for CBaseCombatWeapon::CanBeSelected"); + return false; + } + + return true; } public Action RandomizeWeaponOrder_OnPlayerRunCmd(ChaosEffect effect, int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) @@ -40,8 +43,8 @@ public Action RandomizeWeaponOrder_OnPlayerRunCmd(ChaosEffect effect, int client ArrayList hWeapons = new ArrayList(); - int iLength = GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); - for (int i = 0; i < iLength; i++) + int nMaxWeapons = GetEntPropArraySize(client, Prop_Send, "m_hMyWeapons"); + for (int i = 0; i < nMaxWeapons; i++) { int myWeapon = GetEntPropEnt(client, Prop_Send, "m_hMyWeapons", i); if (myWeapon == -1) diff --git a/addons/sourcemod/scripting/chaos/effects/removehealthandammo.sp b/addons/sourcemod/scripting/chaos/effects/remove_pickups.sp similarity index 88% rename from addons/sourcemod/scripting/chaos/effects/removehealthandammo.sp rename to addons/sourcemod/scripting/chaos/effects/remove_pickups.sp index e6fbc45e..3c3c0ee7 100644 --- a/addons/sourcemod/scripting/chaos/effects/removehealthandammo.sp +++ b/addons/sourcemod/scripting/chaos/effects/remove_pickups.sp @@ -1,7 +1,7 @@ #pragma semicolon 1 #pragma newdecls required -public bool RemoveHealthAndAmmo_OnStart(ChaosEffect effect) +public bool RemovePickups_OnStart(ChaosEffect effect) { bool bRemovedHealth = false, bRemovedAmmo = false; diff --git a/addons/sourcemod/scripting/chaos/effects/removerandomentity.sp b/addons/sourcemod/scripting/chaos/effects/remove_random_entity.sp similarity index 89% rename from addons/sourcemod/scripting/chaos/effects/removerandomentity.sp rename to addons/sourcemod/scripting/chaos/effects/remove_random_entity.sp index a805bbc8..bbed32fa 100644 --- a/addons/sourcemod/scripting/chaos/effects/removerandomentity.sp +++ b/addons/sourcemod/scripting/chaos/effects/remove_random_entity.sp @@ -32,7 +32,7 @@ public bool RemoveRandomEntity_OnStart(ChaosEffect effect) strcopy(szName, sizeof(szName), szClassname); } - CPrintToChatAll("%s%t", g_stChatConfig.tag, "#Chaos_Effect_RemoveRandomEntity_Removing", szClassname, szName); + CPrintToChatAll("%t%t", "#Chaos_Tag", "#Chaos_Effect_RemoveRandomEntity_Removing", szClassname, szName); } RequestFrame(RequestFrame_RemoveEntity, EntIndexToEntRef(entity)); diff --git a/addons/sourcemod/scripting/chaos/effects/resize_player.sp b/addons/sourcemod/scripting/chaos/effects/resize_player.sp new file mode 100644 index 00000000..02c53a52 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/resize_player.sp @@ -0,0 +1,65 @@ +#pragma semicolon 1 +#pragma newdecls required + +public bool ResizePlayer_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + // Only allow one active at a time + if (IsEffectOfClassActive(effect.effect_class)) + return false; + + float flScale = effect.data.GetFloat("scale", 1.0); + float flChangeDuration = effect.data.GetFloat("change_duration"); + + if (flScale == 1.0) + return false; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + if (!IsPlayerAlive(client)) + continue; + + SetModelScale(client, flScale, flChangeDuration); + TF2Attrib_AddCustomPlayerAttribute(client, "voice pitch scale", 1.0 / flScale); + } + + return true; +} + +public void ResizePlayer_OnPlayerSpawn(ChaosEffect effect, int client) +{ + float flScale = effect.data.GetFloat("scale", 1.0); + float flChangeDuration = effect.data.GetFloat("change_duration"); + + SetModelScale(client, flScale, flChangeDuration); + TF2Attrib_AddCustomPlayerAttribute(client, "voice pitch scale", 1.0 / flScale); +} + +public void ResizePlayer_OnEnd(ChaosEffect effect) +{ + float flChangeDuration = effect.data.GetFloat("change_duration"); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + SetModelScale(client, 1.0, flChangeDuration); + TF2Attrib_RemoveCustomPlayerAttribute(client, "voice pitch scale"); + } +} + +static void SetModelScale(int client, float scale, float change_duration = 0.0) +{ + float vecScale[3]; + vecScale[0] = scale; + vecScale[1] = change_duration; + + SetVariantVector3D(vecScale); + AcceptEntityInput(client, "SetModelScale"); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/chaos/effects/screenfade.sp b/addons/sourcemod/scripting/chaos/effects/screen_fade.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/screenfade.sp rename to addons/sourcemod/scripting/chaos/effects/screen_fade.sp diff --git a/addons/sourcemod/scripting/chaos/effects/screen_overlay.sp b/addons/sourcemod/scripting/chaos/effects/screen_overlay.sp new file mode 100644 index 00000000..6cd47d73 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/screen_overlay.sp @@ -0,0 +1,78 @@ +#pragma semicolon 1 +#pragma newdecls required + +public bool ScreenOverlay_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + // Only allow one active at a time + if (IsEffectOfClassActive(effect.effect_class)) + return false; + + char szMaterial[PLATFORM_MAX_PATH]; + effect.data.GetString("material", szMaterial, sizeof(szMaterial)); + + if (!szMaterial[0]) + return false; + + int dspType = effect.data.GetNum("dsp", 0); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + SetEntPropString(client, Prop_Send, "m_szScriptOverlayMaterial", szMaterial); + + if (dspType != 0) + ClientCommand(client, "dsp_player %d", dspType); + } + + return true; +} + +public void ScreenOverlay_OnEnd(ChaosEffect effect) +{ + int dspType = effect.data.GetNum("dsp", 0); + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + SetEntPropString(client, Prop_Send, "m_szScriptOverlayMaterial", ""); + + if (dspType != 0) + ClientCommand(client, "dsp_player %d", 0); + } +} + +public void ScreenOverlay_OnPlayerSpawn(ChaosEffect effect, int client) +{ + char szMaterial[PLATFORM_MAX_PATH]; + effect.data.GetString("material", szMaterial, sizeof(szMaterial)); + + SetEntPropString(client, Prop_Send, "m_szScriptOverlayMaterial", szMaterial); + + int dspType = effect.data.GetNum("dsp", 0); + if (dspType != 0) + ClientCommand(client, "dsp_player %d", effect.data.GetNum("dsp", 0)); +} + +public void ScreenOverlay_OnMapStart(ChaosEffect effect) +{ + if (!effect.data) + return; + + char szFilePath[PLATFORM_MAX_PATH]; + effect.data.GetString("material", szFilePath, sizeof(szFilePath)); + + if (szFilePath[0] && Format(szFilePath, sizeof(szFilePath), "materials/%s.vmt", szFilePath)) + AddFileToDownloadsTable(szFilePath); + + effect.data.GetString("shader", szFilePath, sizeof(szFilePath)); + + if (szFilePath[0] && Format(szFilePath, sizeof(szFilePath), "shaders/fxc/%s.vcs", szFilePath)) + AddFileToDownloadsTable(szFilePath); +} diff --git a/addons/sourcemod/scripting/chaos/effects/screenoverlay.sp b/addons/sourcemod/scripting/chaos/effects/screenoverlay.sp deleted file mode 100644 index 64f96046..00000000 --- a/addons/sourcemod/scripting/chaos/effects/screenoverlay.sp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -public bool ScreenOverlay_OnStart(ChaosEffect effect) -{ - if (!effect.data) - return false; - - // Only allow one active at a time - if (IsEffectOfClassActive(effect.effect_class)) - return false; - - char szScreenOverlay[PLATFORM_MAX_PATH]; - effect.data.GetString("material", szScreenOverlay, sizeof(szScreenOverlay)); - - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - SetEntPropString(client, Prop_Send, "m_szScriptOverlayMaterial", szScreenOverlay); - } - - return true; -} - -public void ScreenOverlay_OnEnd(ChaosEffect effect) -{ - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - SetEntPropString(client, Prop_Send, "m_szScriptOverlayMaterial", ""); - } -} diff --git a/addons/sourcemod/scripting/chaos/effects/set_convar.sp b/addons/sourcemod/scripting/chaos/effects/set_convar.sp new file mode 100644 index 00000000..48899255 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/set_convar.sp @@ -0,0 +1,122 @@ +#pragma semicolon 1 +#pragma newdecls required + +static StringMap g_hOldConVarValues; + +public bool SetConVar_Initialize(ChaosEffect effect) +{ + if (!g_hOldConVarValues) + g_hOldConVarValues = new StringMap(); + + return true; +} + +public bool SetConVar_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + if (!effect.data.JumpToKey("convars")) + return false; + + // Check for duplicate convars in active effects + if (effect.data.GotoFirstSubKey(false)) + { + do + { + char szName[512]; + effect.data.GetSectionName(szName, sizeof(szName)); + + if (FindKeyInSectionInActiveEffects(effect.effect_class, "convars", szName)) + { + effect.data.GoBack(); // Go back to "convars" + effect.data.GoBack(); // Go back to root + return false; + } + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + // Apply all convars + bool bAnySet = false; + if (effect.data.GotoFirstSubKey(false)) + { + do + { + char szName[512], szValue[512], szOldValue[512]; + effect.data.GetSectionName(szName, sizeof(szName)); + effect.data.GetString(NULL_STRING, szValue, sizeof(szValue)); + + ConVar convar = FindConVar(szName); + if (!convar) + continue; + + convar.GetString(szOldValue, sizeof(szOldValue)); + + // Don't set if the convar value is already set to the desired value + if (StrEqual(szOldValue, szValue)) + continue; + + g_hOldConVarValues.SetString(szName, szOldValue); + convar.SetString(szValue, true); + bAnySet = true; + + // If this effect has a duration, add the change hook + if (effect.duration) + convar.AddChangeHook(OnConVarChanged); + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + effect.data.GoBack(); + return bAnySet; +} + +public void SetConVar_OnEnd(ChaosEffect effect) +{ + if (!effect.data.JumpToKey("convars")) + return; + + if (effect.data.GotoFirstSubKey(false)) + { + do + { + char szName[512], szOldValue[512]; + effect.data.GetSectionName(szName, sizeof(szName)); + + if (!g_hOldConVarValues.GetString(szName, szOldValue, sizeof(szOldValue))) + continue; + + ConVar convar = FindConVar(szName); + if (!convar) + continue; + + convar.RemoveChangeHook(OnConVarChanged); + convar.SetString(szOldValue, true); + g_hOldConVarValues.Remove(szName); + } + while (effect.data.GotoNextKey(false)); + + effect.data.GoBack(); + } + + effect.data.GoBack(); +} + +static void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + char szName[512]; + convar.GetName(szName, sizeof(szName)); + + // Restore the old value + convar.RemoveChangeHook(OnConVarChanged); + convar.SetString(oldValue, true); + convar.AddChangeHook(OnConVarChanged); + + // Update our stored value + g_hOldConVarValues.SetString(szName, newValue); +} diff --git a/addons/sourcemod/scripting/chaos/effects/setcustommodel.sp b/addons/sourcemod/scripting/chaos/effects/set_custom_model.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/setcustommodel.sp rename to addons/sourcemod/scripting/chaos/effects/set_custom_model.sp diff --git a/addons/sourcemod/scripting/chaos/effects/setfov.sp b/addons/sourcemod/scripting/chaos/effects/set_fov.sp similarity index 59% rename from addons/sourcemod/scripting/chaos/effects/setfov.sp rename to addons/sourcemod/scripting/chaos/effects/set_fov.sp index 34efe993..ff4fc8b2 100644 --- a/addons/sourcemod/scripting/chaos/effects/setfov.sp +++ b/addons/sourcemod/scripting/chaos/effects/set_fov.sp @@ -17,8 +17,7 @@ public bool SetFOV_OnStart(ChaosEffect effect) if (!IsClientInGame(client)) continue; - SetEntProp(client, Prop_Send, "m_iFOV", iFOV); - SetEntProp(client, Prop_Send, "m_iDefaultFOV", iFOV); + SetFOV(client, iFOV); } return true; @@ -31,31 +30,34 @@ public void SetFOV_OnEnd(ChaosEffect effect) if (!IsClientInGame(client)) continue; - char szFOV[64]; - if (GetClientInfo(client, "fov_desired", szFOV, sizeof(szFOV))) - { - int iFOV = StringToInt(szFOV); - SetEntProp(client, Prop_Send, "m_iFOV", iFOV); - SetEntProp(client, Prop_Send, "m_iDefaultFOV", iFOV); - } + SetDefaultFOV(client); } } public void SetFOV_OnPlayerSpawn(ChaosEffect effect, int client) { - int iFOV = effect.data.GetNum("fov"); - + SetFOV(client, effect.data.GetNum("fov")); +} + +public void SetFOV_OnConditionRemoved(ChaosEffect effect, int client, TFCond condition) +{ + if (condition == TFCond_Zoomed || condition == TFCond_Teleporting || condition == TFCond_HalloweenKartDash) + { + SetFOV(client, effect.data.GetNum("fov")); + } +} + +static void SetFOV(int client, int iFOV) +{ SetEntProp(client, Prop_Send, "m_iFOV", iFOV); SetEntProp(client, Prop_Send, "m_iDefaultFOV", iFOV); } -public void SetFOV_OnConditionRemoved(ChaosEffect effect, int client, TFCond condition) +static void SetDefaultFOV(int client) { - if (condition == TFCond_Zoomed) + char szFOV[32]; + if (GetClientInfo(client, "fov_desired", szFOV, sizeof(szFOV))) { - int iFOV = effect.data.GetNum("fov"); - - SetEntProp(client, Prop_Send, "m_iFOV", iFOV); - SetEntProp(client, Prop_Send, "m_iDefaultFOV", iFOV); + SetFOV(client, StringToInt(szFOV)); } } diff --git a/addons/sourcemod/scripting/chaos/effects/sethealth.sp b/addons/sourcemod/scripting/chaos/effects/set_max_health.sp similarity index 72% rename from addons/sourcemod/scripting/chaos/effects/sethealth.sp rename to addons/sourcemod/scripting/chaos/effects/set_max_health.sp index 8026274d..f67b123c 100644 --- a/addons/sourcemod/scripting/chaos/effects/sethealth.sp +++ b/addons/sourcemod/scripting/chaos/effects/set_max_health.sp @@ -4,18 +4,13 @@ static DynamicDetour g_hDetourGetMaxHealthForBuffing; static int g_nMaxHealth; -public bool SetHealth_Initialize(ChaosEffect effect, GameData gameconf) +public bool SetMaxHealth_Initialize(ChaosEffect effect) { - if (!gameconf) - return false; - - if (!g_hDetourGetMaxHealthForBuffing) - g_hDetourGetMaxHealthForBuffing = DynamicDetour.FromConf(gameconf, "CTFPlayer::GetMaxHealthForBuffing"); - + g_hDetourGetMaxHealthForBuffing = Chaos_CreateDetour("CTFPlayer::GetMaxHealthForBuffing"); return g_hDetourGetMaxHealthForBuffing != null; } -public bool SetHealth_OnStart(ChaosEffect effect) +public bool SetMaxHealth_OnStart(ChaosEffect effect) { if (!effect.data) return false; @@ -43,7 +38,7 @@ public bool SetHealth_OnStart(ChaosEffect effect) return true; } -public void SetHealth_OnEnd(ChaosEffect effect) +public void SetMaxHealth_OnEnd(ChaosEffect effect) { g_hDetourGetMaxHealthForBuffing.Disable(Hook_Pre, OnGetMaxHealthForBuffing); } diff --git a/addons/sourcemod/scripting/chaos/effects/setattribute.sp b/addons/sourcemod/scripting/chaos/effects/setattribute.sp deleted file mode 100644 index 14718be8..00000000 --- a/addons/sourcemod/scripting/chaos/effects/setattribute.sp +++ /dev/null @@ -1,124 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -public bool SetAttribute_OnStart(ChaosEffect effect) -{ - if (!effect.data) - return false; - - // Don't set the same attribute twice - if (IsAlreadyActive(effect)) - return false; - - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - ApplyAttributesToPlayer(effect, client); - } - - return true; -} - -public void SetAttribute_OnEnd(ChaosEffect effect) -{ - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - ApplyAttributesToPlayer(effect, client, true); - } -} - -public void SetAttribute_OnPostInventoryApplication(ChaosEffect effect, int client) -{ - ApplyAttributesToPlayer(effect, client); -} - -static bool IsAlreadyActive(ChaosEffect effect) -{ - KeyValues kv = effect.data; - - // Make sure we traverse back to not mess up effect data - bool bFoundKey = false; - - if (kv.JumpToKey("attributes", false)) - { - if (kv.GotoFirstSubKey(false)) - { - do - { - // Check if the same attribute is already active in this effect class - char szAttrib[64]; - if (kv.GetSectionName(szAttrib, sizeof(szAttrib)) && FindKeyInActiveEffects(effect.effect_class, szAttrib)) - { - bFoundKey = true; - } - } - while (!bFoundKey && kv.GotoNextKey(false)); - kv.GoBack(); - } - kv.GoBack(); - } - - return bFoundKey; -} - -static void ApplyAttributesToPlayer(ChaosEffect effect, int client, bool bRemove = false) -{ - KeyValues kv = effect.data; - - bool bApplyToWeapons = kv.GetNum("apply_to_weapons") != 0; - - if (kv.JumpToKey("attributes", false)) - { - if (kv.GotoFirstSubKey(false)) - { - do - { - char szAttrib[64]; - if (kv.GetSectionName(szAttrib, sizeof(szAttrib))) - { - float flValue = kv.GetFloat(NULL_STRING); - - if (bApplyToWeapons) - { - for (int i = 0; i < GetEntPropArraySize(client , Prop_Send,"m_hMyWeapons"); i++) - { - int myWeapon = GetEntPropEnt(client, Prop_Send, "m_hMyWeapons", i); - if (myWeapon != -1) - { - if (!bRemove) - { - TF2Attrib_SetByName(myWeapon, szAttrib, flValue); - } - else - { - TF2Attrib_RemoveByName(myWeapon, szAttrib); - } - } - } - } - else - { - if (!bRemove) - { - TF2Attrib_SetByName(client, szAttrib, flValue); - } - else - { - TF2Attrib_RemoveByName(client, szAttrib); - } - } - } - } - while (kv.GotoNextKey(false)); - kv.GoBack(); - } - kv.GoBack(); - } - - TF2Util_UpdatePlayerSpeed(client); -} diff --git a/addons/sourcemod/scripting/chaos/effects/setconvar.sp b/addons/sourcemod/scripting/chaos/effects/setconvar.sp deleted file mode 100644 index c3cc83d4..00000000 --- a/addons/sourcemod/scripting/chaos/effects/setconvar.sp +++ /dev/null @@ -1,115 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -static StringMap g_hOldConVarValues; -static ConVar sv_cheats; - -public bool SetConVar_Initialize(ChaosEffect effect, GameData gameconf) -{ - if (!g_hOldConVarValues) - g_hOldConVarValues = new StringMap(); - - sv_cheats = FindConVar("sv_cheats"); - - return true; -} - -public bool SetConVar_OnStart(ChaosEffect effect) -{ - if (!effect.data) - return false; - - char szName[512]; - effect.data.GetString("convar", szName, sizeof(szName)); - - ConVar convar = FindConVar(szName); - if (!convar) - return false; - - // Don't set the same convar twice - if (FindKeyValuePairInActiveEffects(effect.effect_class, "convar", szName)) - return false; - - char szValue[512], szOldValue[512]; - effect.data.GetString("value", szValue, sizeof(szValue)); - convar.GetString(szOldValue, sizeof(szOldValue)); - - // Don't start effect if the convar value is already set to the desired value - if (StrEqual(szOldValue, szValue)) - return false; - - g_hOldConVarValues.SetString(szName, szOldValue); - convar.SetString(szValue, true); - - // If this effect has no duration, we don't need the stuff below - if (!effect.duration) - return true; - - convar.AddChangeHook(OnConVarChanged); - - if (effect.data.GetNum("replicate_cheats")) - { - for (int client = 1; client <= MaxClients; client++) - { - if (!IsClientInGame(client)) - continue; - - if (IsFakeClient(client)) - { - SetFakeClientConVar(client, "sv_cheats", "1"); - } - else - { - sv_cheats.ReplicateToClient(client, "1"); - } - - } - } - - return true; -} - -public void SetConVar_OnEnd(ChaosEffect effect) -{ - char szName[512], szValue[512]; - effect.data.GetString("convar", szName, sizeof(szName)); - g_hOldConVarValues.GetString(szName, szValue, sizeof(szValue)); - - ConVar convar = FindConVar(szName); - - convar.RemoveChangeHook(OnConVarChanged); - convar.SetString(szValue, true); - g_hOldConVarValues.Remove(szName); - - if (effect.data.GetNum("replicate_cheats")) - { - for (int client = 1; client <= MaxClients; client++) - { - if (IsClientInGame(client)) - { - if (IsFakeClient(client)) - { - SetFakeClientConVar(client, "sv_cheats", "0"); - } - else - { - sv_cheats.ReplicateToClient(client, "0"); - } - } - } - } -} - -static void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) -{ - char szName[512]; - convar.GetName(szName, sizeof(szName)); - - // Restore the old value - convar.RemoveChangeHook(OnConVarChanged); - convar.SetString(oldValue, true); - convar.AddChangeHook(OnConVarChanged); - - // Update our stored value - g_hOldConVarValues.SetString(szName, newValue); -} diff --git a/addons/sourcemod/scripting/chaos/effects/showscoreboard.sp b/addons/sourcemod/scripting/chaos/effects/show_scoreboard.sp similarity index 100% rename from addons/sourcemod/scripting/chaos/effects/showscoreboard.sp rename to addons/sourcemod/scripting/chaos/effects/show_scoreboard.sp diff --git a/addons/sourcemod/scripting/chaos/effects/spawnball.sp b/addons/sourcemod/scripting/chaos/effects/spawn_ball.sp similarity index 95% rename from addons/sourcemod/scripting/chaos/effects/spawnball.sp rename to addons/sourcemod/scripting/chaos/effects/spawn_ball.sp index d1acc3eb..24be2e9d 100644 --- a/addons/sourcemod/scripting/chaos/effects/spawnball.sp +++ b/addons/sourcemod/scripting/chaos/effects/spawn_ball.sp @@ -38,7 +38,7 @@ public bool SpawnBall_OnStart(ChaosEffect effect) static bool CanFindBallSpawnLocation(const float vecSearchOrigin[3], float vecDropSpot[3]) { // Find clear space to drop the ball - for (float flAngle = 0.0; flAngle < 2.0 * PI; flAngle += 0.2) + for (float flAngle = 0.0; flAngle < 2.0 * FLOAT_PI; flAngle += 0.2) { float vecForward[3]; vecForward[0] = Cosine(flAngle); diff --git a/addons/sourcemod/scripting/chaos/effects/spawn_birds.sp b/addons/sourcemod/scripting/chaos/effects/spawn_birds.sp new file mode 100644 index 00000000..a100c372 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/spawn_birds.sp @@ -0,0 +1,68 @@ +#pragma semicolon 1 +#pragma newdecls required + +#define ENTITY_FLYING_BIRD_MODEL "models/props_forest/dove.mdl" + +#define ENTITY_FLYING_BIRD_SPEED_MIN 200.0 +#define ENTITY_FLYING_BIRD_SPEED_MAX 500.0 + +static float g_flNextBirdSpawnTime[MAXPLAYERS + 1]; + +public bool SpawnBirds_OnStart(ChaosEffect effect) +{ + PrecacheModel(ENTITY_FLYING_BIRD_MODEL); + + for (int client = 1; client <= MaxClients; client++) + { + g_flNextBirdSpawnTime[client] = GetGameTime(); + } + + return true; +} + +public void SpawnBirds_Update(ChaosEffect effect) +{ + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + if (!IsPlayerAlive(client)) + continue; + + if (g_flNextBirdSpawnTime[client] > GetGameTime()) + continue; + + g_flNextBirdSpawnTime[client] = GetGameTime() + GetRandomFloat(0.5, 1.0); + + float vecPos[3], vecOrigin[3], vecCenter[3]; + GetClientAbsOrigin(client, vecOrigin); + WorldSpaceCenter(client, vecCenter); + AddVectors(vecOrigin, vecCenter, vecPos); + ScaleVector(vecPos, 0.5); + + float vecRandom[3]; + vecRandom[2] = GetRandomFloat(-10.0, 20.0); + AddVectors(vecPos, vecRandom, vecPos); + + SpawnClientsideFlyingBird(vecPos); + } +} + +static void SpawnClientsideFlyingBird(float vecSpawn[3]) +{ + float flyAngle = GetRandomFloat(-FLOAT_PI, FLOAT_PI); + float flyAngleRate = GetRandomFloat(-1.5, 1.5); + float accelZ = GetRandomFloat(0.5, 2.0); + float speed = GetRandomFloat(ENTITY_FLYING_BIRD_SPEED_MIN, ENTITY_FLYING_BIRD_SPEED_MAX); + float flGlideTime = GetRandomFloat(0.25, 1.0); + + BfWrite bf = UserMessageToBfWrite(StartMessageAll("SpawnFlyingBird")); + bf.WriteVecCoord(vecSpawn); + bf.WriteFloat(flyAngle); + bf.WriteFloat(flyAngleRate); + bf.WriteFloat(accelZ); + bf.WriteFloat(speed); + bf.WriteFloat(flGlideTime); + EndMessage(); +} diff --git a/addons/sourcemod/scripting/chaos/effects/stepsize.sp b/addons/sourcemod/scripting/chaos/effects/step_size.sp similarity index 81% rename from addons/sourcemod/scripting/chaos/effects/stepsize.sp rename to addons/sourcemod/scripting/chaos/effects/step_size.sp index 8a513fe7..df8020a2 100644 --- a/addons/sourcemod/scripting/chaos/effects/stepsize.sp +++ b/addons/sourcemod/scripting/chaos/effects/step_size.sp @@ -4,7 +4,7 @@ static ConVar sv_stepsize; static float g_flStepSize; -public bool StepSize_Initialize(ChaosEffect effect, GameData gameconf) +public bool StepSize_Initialize(ChaosEffect effect) { sv_stepsize = FindConVar("sv_stepsize"); @@ -27,7 +27,7 @@ public bool StepSize_OnStart(ChaosEffect effect) if (!IsClientInGame(client)) continue; - SetEntPropFloat(client, Prop_Data, "m_flStepSize", g_flStepSize); + SetEntPropFloat(client, Prop_Send, "m_flStepSize", g_flStepSize); } return true; @@ -40,7 +40,7 @@ public void StepSize_OnEnd(ChaosEffect effect) if (!IsClientInGame(client)) continue; - SetEntPropFloat(client, Prop_Data, "m_flStepSize", sv_stepsize.FloatValue); + SetEntPropFloat(client, Prop_Send, "m_flStepSize", sv_stepsize.FloatValue); } } diff --git a/addons/sourcemod/scripting/chaos/effects/time_scale.sp b/addons/sourcemod/scripting/chaos/effects/time_scale.sp new file mode 100644 index 00000000..751854e7 --- /dev/null +++ b/addons/sourcemod/scripting/chaos/effects/time_scale.sp @@ -0,0 +1,115 @@ +#pragma semicolon 1 +#pragma newdecls required + +static ConVar host_timescale; +static ConVar sv_cheats; + +static float g_flOldTimescale; +static float g_flCurrentTimescale; + +public bool TimeScale_Initialize(ChaosEffect effect) +{ + host_timescale = FindConVar("host_timescale"); + sv_cheats = FindConVar("sv_cheats"); + + return true; +} + +public bool TimeScale_OnStart(ChaosEffect effect) +{ + if (!effect.data) + return false; + + if (IsEffectOfClassActive(effect.effect_class)) + return false; + + float flTimescale = effect.data.GetFloat("timescale", 1.0); + + if (host_timescale.FloatValue == flTimescale) + return false; + + g_flOldTimescale = host_timescale.FloatValue; + g_flCurrentTimescale = flTimescale; + host_timescale.FloatValue = flTimescale; + + host_timescale.AddChangeHook(OnTimescaleChanged); + sv_cheats.AddChangeHook(OnCheatsChanged); + + AddNormalSoundHook(OnNormalSoundPlayed); + AddAmbientSoundHook(OnAmbientSoundPlayed); + + ReplicateCheatsToClients("1"); + + return true; +} + +public void TimeScale_OnEnd(ChaosEffect effect) +{ + host_timescale.RemoveChangeHook(OnTimescaleChanged); + sv_cheats.RemoveChangeHook(OnCheatsChanged); + host_timescale.FloatValue = g_flOldTimescale; + + RemoveNormalSoundHook(OnNormalSoundPlayed); + RemoveAmbientSoundHook(OnAmbientSoundPlayed); + + // Replicate current server value back to clients + char szValue[512]; + sv_cheats.GetString(szValue, sizeof(szValue)); + ReplicateCheatsToClients(szValue); +} + +public void TimeScale_OnClientPutInServer(ChaosEffect effect, int client) +{ + ReplicateCheatsToClient(client, "1"); +} + +static void OnTimescaleChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + host_timescale.RemoveChangeHook(OnTimescaleChanged); + host_timescale.FloatValue = g_flCurrentTimescale; + host_timescale.AddChangeHook(OnTimescaleChanged); + + g_flOldTimescale = StringToFloat(newValue); +} + +static void OnCheatsChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + // Allow clients to react to the initial change first + RequestFrame(RequestFrameCallback_ReplicateCheats); +} + +public void RequestFrameCallback_ReplicateCheats() +{ + ReplicateCheatsToClients("1"); +} + +static Action OnNormalSoundPlayed(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) +{ + pitch = RoundToNearest(pitch * g_flCurrentTimescale); + return Plugin_Changed; +} + +static Action OnAmbientSoundPlayed(char sample[PLATFORM_MAX_PATH], int &entity, float &volume, int &level, int &pitch, float pos[3], int &flags, float &delay) +{ + pitch = RoundToNearest(pitch * g_flCurrentTimescale); + return Plugin_Changed; +} + +static void ReplicateCheatsToClient(int client, const char[] szValue) +{ + if (IsFakeClient(client)) + SetFakeClientConVar(client, "sv_cheats", szValue); + else + sv_cheats.ReplicateToClient(client, szValue); +} + +static void ReplicateCheatsToClients(const char[] szValue) +{ + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client)) + continue; + + ReplicateCheatsToClient(client, szValue); + } +} diff --git a/addons/sourcemod/scripting/chaos/effects/watermark.sp b/addons/sourcemod/scripting/chaos/effects/watermark.sp index 704066e5..be63c696 100644 --- a/addons/sourcemod/scripting/chaos/effects/watermark.sp +++ b/addons/sourcemod/scripting/chaos/effects/watermark.sp @@ -5,7 +5,7 @@ static Handle g_hHudSync; static ConVar hostname; static float g_flNextDisplayTime; -public bool Watermark_Initialize(ChaosEffect effect, GameData gameconf) +public bool Watermark_Initialize(ChaosEffect effect) { g_hHudSync = CreateHudSynchronizer(); hostname = FindConVar("hostname"); diff --git a/addons/sourcemod/scripting/chaos/events.sp b/addons/sourcemod/scripting/chaos/events.sp index 86f81076..f21bdb1a 100644 --- a/addons/sourcemod/scripting/chaos/events.sp +++ b/addons/sourcemod/scripting/chaos/events.sp @@ -60,14 +60,15 @@ static void Events_AddEvent(const char[] szName, EventHook fnCallback, EventHook static void EventHook_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - + int userid = event.GetInt("userid"); + int client = GetClientOfUserId(userid); + int nLength = g_hEffects.Length; for (int i = 0; i < nLength; i++) { if (!g_hEffects.Get(i, ChaosEffect::active)) continue; - + ChaosEffect effect; if (g_hEffects.GetArray(i, effect)) { @@ -81,6 +82,35 @@ static void EventHook_PlayerSpawn(Event event, const char[] name, bool dontBroad } } } + + RequestFrame(Frame_PlayerSpawnPost, userid); +} + +static void Frame_PlayerSpawnPost(int userid) +{ + int client = GetClientOfUserId(userid); + if (client == 0) + return; + + int nLength = g_hEffects.Length; + for (int i = 0; i < nLength; i++) + { + if (!g_hEffects.Get(i, ChaosEffect::active)) + continue; + + ChaosEffect effect; + if (g_hEffects.GetArray(i, effect)) + { + Function fnCallback = effect.GetCallbackFunction("OnPlayerSpawnPost"); + if (fnCallback != INVALID_FUNCTION) + { + Call_StartFunction(null, fnCallback); + Call_PushArray(effect, sizeof(effect)); + Call_PushCell(client); + Call_Finish(); + } + } + } } static void EventHook_PostInventoryApplication(Event event, const char[] name, bool dontBroadcast) @@ -115,8 +145,8 @@ static void EventHook_ArenaRoundStart(Event event, const char[] name, bool dontB static void EventHook_TeamplayRoundStart(Event event, const char[] name, bool dontBroadcast) { - SetChaosTimers(0.0); - + SetChaosTimers(-1.0); + int nLength = g_hEffects.Length; for (int i = 0; i < nLength; i++) { diff --git a/addons/sourcemod/scripting/chaos/shareddefs.sp b/addons/sourcemod/scripting/chaos/shareddefs.sp index bffd1090..35b25137 100644 --- a/addons/sourcemod/scripting/chaos/shareddefs.sp +++ b/addons/sourcemod/scripting/chaos/shareddefs.sp @@ -1,12 +1,10 @@ #pragma semicolon 1 #pragma newdecls required -#define INVALID_EFFECT_ID -1 +#define ONESHOT_EFFECT_DISPLAY_TIME 75.0 #define MAX_USER_MSG_DATA 255 -#define PI 3.14159265358979323846 - // Fade in/out #define FFADE_IN 0x0001 // Just here so we don't pass 0 into the function #define FFADE_OUT 0x0002 // Fade out (not in) @@ -16,6 +14,29 @@ #define SCREENFADE_FRACBITS 9 // which leaves 16-this for the integer part +enum Dir_t +{ + DIR_FWD, + DIR_BACK, + DIR_LEFT, + DIR_RIGHT, + + DIR_UP, + DIR_DOWN, + + NUM_DIRS +} + +enum ShakeCommand_t +{ + SHAKE_START = 0, // Starts the screen shake for all players within the radius. + SHAKE_STOP, // Stops the screen shake for all players within the radius. + SHAKE_AMPLITUDE, // Modifies the amplitude of an active screen shake for all players within the radius. + SHAKE_FREQUENCY, // Modifies the frequency of an active screen shake for all players within the radius. + SHAKE_START_RUMBLEONLY, // Starts a shake effect that only rumbles the controller, no screen effect. + SHAKE_START_NORUMBLE, // Starts a shake that does NOT rumble the controller. +}; + enum HudNotification_t { HUD_NOTIFY_YOUR_FLAG_TAKEN, diff --git a/addons/sourcemod/scripting/chaos/util.sp b/addons/sourcemod/scripting/chaos/util.sp index 817b9708..2dfedb1c 100644 --- a/addons/sourcemod/scripting/chaos/util.sp +++ b/addons/sourcemod/scripting/chaos/util.sp @@ -1,6 +1,22 @@ #pragma semicolon 1 #pragma newdecls required +int GetNumEdicts() +{ + int nNumEdicts = 0; + + int entity = -1; + while ((entity = FindEntityByClassname(entity, "*")) != -1) + { + if (!IsEntNetworkable(entity)) + continue; + + nNumEdicts++; + } + + return nNumEdicts; +} + any Max(any a, any b) { return (a >= b) ? a : b; @@ -35,12 +51,28 @@ int SortFuncADTArray_SortChaosEffectsByCooldown(int index1, int index2, Handle a int SortFuncADTArray_SortChaosEffectsByActivationTime(int index1, int index2, Handle array, Handle hndl) { ArrayList list = view_as(array); - + ChaosEffect effect1, effect2; list.GetArray(index1, effect1); list.GetArray(index2, effect2); - - return (effect1.activate_time == effect2.activate_time) ? strcmp(effect2.id, effect1.id) : Compare(effect1.activate_time, effect2.activate_time); + + // Sort by activation time descending + if (effect1.activate_time != effect2.activate_time) + return Compare(effect2.activate_time, effect1.activate_time); + + // Sort meta effects first + if (effect1.meta != effect2.meta) + return Compare(effect2.meta, effect1.meta); + + float duration1 = effect1.duration ? effect1.duration : ONESHOT_EFFECT_DISPLAY_TIME; + float duration2 = effect2.duration ? effect2.duration : ONESHOT_EFFECT_DISPLAY_TIME; + + // Sort by duration ascending + if (duration1 != duration2) + return Compare(duration1, duration2); + + // Sort alphabetically by ID + return strcmp(effect1.id, effect2.id); } bool FindKeyInKeyValues(KeyValues kv, const char[] szKeyToFind) @@ -49,19 +81,20 @@ bool FindKeyInKeyValues(KeyValues kv, const char[] szKeyToFind) { if (kv.GotoFirstSubKey(false)) { - // Current key is a section. Browse it recursively. - return FindKeyInKeyValues(kv, szKeyToFind); + // Current key is a section, browse it recursively + if (FindKeyInKeyValues(kv, szKeyToFind)) + return true; + + kv.GoBack(); } else { - // Current key is a regular key, or an empty section. + // Current key is a regular key, or an empty section if (kv.GetDataType(NULL_STRING) != KvData_None) { char szKey[64]; if (kv.GetSectionName(szKey, sizeof(szKey)) && StrEqual(szKey, szKeyToFind)) - { return true; - } } } } @@ -70,65 +103,137 @@ bool FindKeyInKeyValues(KeyValues kv, const char[] szKeyToFind) return false; } +static bool FindValueInKeyValues(KeyValues kv, const char[] szValueToFind) +{ + do + { + if (kv.GotoFirstSubKey(false)) + { + // It's a section, recurse into it + if (FindValueInKeyValues(kv, szValueToFind)) + return true; + + kv.GoBack(); + } + else if (kv.GetDataType(NULL_STRING) != KvData_None) + { + // It's a key-value pair + char szValue[64]; + kv.GetString(NULL_STRING, szValue, sizeof(szValue)); + + if (StrEqual(szValue, szValueToFind)) + return true; + } + } + while (kv.GotoNextKey(false)); + + return false; +} + bool FindKeyValuePairInKeyValues(KeyValues kv, const char[] szKeyToFind, const char[] szValueToFind) { do { + char szKey[64]; + kv.GetSectionName(szKey, sizeof(szKey)); + if (kv.GotoFirstSubKey(false)) { - // Current key is a section. Browse it recursively. - return FindKeyValuePairInKeyValues(kv, szKeyToFind, szValueToFind); + // Current key is a section + if (StrEqual(szKey, szKeyToFind)) + { + // Found target section, recursively search for the value + if (FindValueInKeyValues(kv, szValueToFind)) + return true; + } + else + { + // Not the target section, recurse to find nested instances + if (FindKeyValuePairInKeyValues(kv, szKeyToFind, szValueToFind)) + return true; + } + kv.GoBack(); } - else + else if (kv.GetDataType(NULL_STRING) != KvData_None) { - // Current key is a regular key, or an empty section. - if (kv.GetDataType(NULL_STRING) != KvData_None) + // Current key is a regular key-value pair + if (StrEqual(szKey, szKeyToFind)) { - char szKey[64]; - if (kv.GetSectionName(szKey, sizeof(szKey)) && StrEqual(szKey, szKeyToFind)) + char szValue[64]; + kv.GetString(NULL_STRING, szValue, sizeof(szValue)); + + if (StrEqual(szValue, szValueToFind)) + return true; + } + } + } + while (kv.GotoNextKey(false)); + + return false; +} + +bool FindKeyInSectionInKeyValues(KeyValues kv, const char[] szSectionToFind, const char[] szKeyToFind) +{ + do + { + char szKey[64]; + kv.GetSectionName(szKey, sizeof(szKey)); + + if (kv.GotoFirstSubKey(false)) + { + // Current key is a section + if (StrEqual(szKey, szSectionToFind)) + { + // Found target section, search for the key name inside it + if (FindKeyInKeyValues(kv, szKeyToFind)) + { + kv.GoBack(); + return true; + } + } + else + { + // Not the target section, recurse to find nested instances + if (FindKeyInSectionInKeyValues(kv, szSectionToFind, szKeyToFind)) { - char szValue[64]; - kv.GetString(NULL_STRING, szValue, sizeof(szValue)); - - if (StrEqual(szValue, szValueToFind)) - { - return true; - } + kv.GoBack(); + return true; } } + kv.GoBack(); } } while (kv.GotoNextKey(false)); - + return false; } void SendHudNotification(HudNotification_t iType, bool bForceShow = false) { BfWrite bf = UserMessageToBfWrite(StartMessageAll("HudNotify")); - bf.WriteByte(view_as(iType)); - bf.WriteBool(bForceShow); // Display in cl_hud_minmode + bf.WriteByte(view_as(iType)); + bf.WriteBool(bForceShow); // Display in cl_hud_minmode EndMessage(); } void SendCustomHudNotificationCustom(int client, const char[] szText, const char[] szIcon, TFTeam nTeam = TFTeam_Unassigned) { BfWrite bf = UserMessageToBfWrite(StartMessageOne("HudNotifyCustom", client)); - bf.WriteString(szText); - bf.WriteString(szIcon); - bf.WriteByte(view_as(nTeam)); + bf.WriteString(szText); + bf.WriteString(szIcon); + bf.WriteByte(view_as(nTeam)); EndMessage(); } void PrintKeyHintText(int client, const char[] format, any...) { - char buffer[256]; + char buffer[MAX_USER_MSG_DATA - 1]; SetGlobalTransTarget(client); VFormat(buffer, sizeof(buffer), format, 3); BfWrite bf = UserMessageToBfWrite(StartMessageOne("KeyHintText", client)); - bf.WriteByte(1); - bf.WriteString(buffer); + bf.WriteByte(1); + bf.WriteString(buffer); EndMessage(); } @@ -315,18 +420,27 @@ void UTIL_ScreenFade(int player, const int color[4], float fadeTime, float fadeH BfWrite bf = UserMessageToBfWrite(StartMessageOne("Fade", player, USERMSG_RELIABLE)); if (bf != null) { - bf.WriteShort(FixedUnsigned16(fadeTime, 1 << SCREENFADE_FRACBITS)); - bf.WriteShort(FixedUnsigned16(fadeHold, 1 << SCREENFADE_FRACBITS)); - bf.WriteShort(flags); - bf.WriteByte(color[0]); - bf.WriteByte(color[1]); - bf.WriteByte(color[2]); - bf.WriteByte(color[3]); - + bf.WriteShort(FixedUnsigned16(fadeTime, 1 << SCREENFADE_FRACBITS)); + bf.WriteShort(FixedUnsigned16(fadeHold, 1 << SCREENFADE_FRACBITS)); + bf.WriteShort(flags); + bf.WriteByte(color[0]); + bf.WriteByte(color[1]); + bf.WriteByte(color[2]); + bf.WriteByte(color[3]); EndMessage(); } } +void UTIL_ScreenShake(int player, ShakeCommand_t eCommand, float flAmplitude, float flFrequency, float flDuration) +{ + BfWrite bf = UserMessageToBfWrite(StartMessageOne("Shake", player)); + bf.WriteByte(view_as(eCommand)); + bf.WriteFloat(flAmplitude); + bf.WriteFloat(flFrequency); + bf.WriteFloat(flDuration); + EndMessage(); +} + void WorldSpaceCenter(int entity, float vecCenter[3]) { float vecOrigin[3], vecMins[3], vecMaxs[3], vecOffset[3]; @@ -344,6 +458,53 @@ int FindItemOffset(int entity) char szNetClass[32]; if (!GetEntityNetClass(entity, szNetClass, sizeof(szNetClass))) return -1; - + return FindSendPropInfo(szNetClass, "m_Item"); } + +bool Chaos_LoadGameData(GameData &gameconf) +{ + gameconf = new GameData("chaos"); + if (!gameconf) + { + LogError("Failed to load gamedata file"); + return false; + } + return true; +} + +DynamicDetour Chaos_CreateDetour(const char[] name) +{ + GameData gameconf; + if (!Chaos_LoadGameData(gameconf)) + return null; + + DynamicDetour detour = DynamicDetour.FromConf(gameconf, name); + delete gameconf; + + if (!detour) + { + LogError("Failed to create detour for '%s'", name); + return null; + } + + return detour; +} + +DynamicHook Chaos_CreateDynamicHook(const char[] name) +{ + GameData gameconf; + if (!Chaos_LoadGameData(gameconf)) + return null; + + DynamicHook hook = DynamicHook.FromConf(gameconf, name); + delete gameconf; + + if (!hook) + { + LogError("Failed to create hook for '%s'", name); + return null; + } + + return hook; +} diff --git a/addons/sourcemod/translations/chaos.phrases.txt b/addons/sourcemod/translations/chaos.phrases.txt index c99902aa..a71e6f72 100644 --- a/addons/sourcemod/translations/chaos.phrases.txt +++ b/addons/sourcemod/translations/chaos.phrases.txt @@ -1,22 +1,20 @@ "Phrases" { - // HUD center message with the effect name - "#Chaos_Effect_Activated" + "#Chaos_Tag" { - "#format" "{1:t}" - "en" "{1}" + "en" "[{darkorange}TF2 CHAOS{default}] " } - "#Chaos_Effect_SetNextEffect_Invalid" + "#Chaos_Effect_NotFound" { "#format" "{1:s}" - "en" "No effect with ID \"{1}\" found." + "en" "No effect with ID {lightgreen}{1}{default} found." } - "#Chaos_Effect_SetNextEffect_Done" + "#Chaos_Effect_SetNextEffect_Success" { "#format" "{1:t}" - "en" "Next effect set to \"{1}\"." + "en" "Next effect set to {lightgreen}{1}{default}." } - "#Chaos_Effect_InvertAcceleration" + "#Chaos_Effect_InvertAccel" { "en" "Invert Controls" } @@ -32,6 +30,11 @@ { "en" "Kill Random Player" } + "#Chaos_Effect_KillRandomPlayer_Killed" + { + "#format" "{1:N}" + "en" "Screw {lightgreen}{1}{default} in particular." + } "#Chaos_Effect_FriendlyFire" { "en" "Friendly Fire" @@ -44,7 +47,7 @@ { "en" "Truce" } - "#Chaos_Effect_WhereDidEverythingGo" + "#Chaos_Effect_NoTransmit" { "en" "Where Did Everything Go?" } @@ -52,23 +55,27 @@ { "en" "Eternal Screams" } - "#Chaos_Effect_1HP" + "#Chaos_Effect_LowHP" { "en" "One Hit K.O." } - "#Chaos_Effect_1000HP" + "#Chaos_Effect_HighHP" { - "en" "Healthy Mercenaries" + "en" "Healthy Mercs" } - "#Chaos_Effect_ShortMercs" + "#Chaos_Effect_NoTorso" { - "en" "Midget Mercenaries" + "en" "Manlets" + } + "#Chaos_Effect_BigHands" + { + "en" "Big Guns" } "#Chaos_Effect_Watermark" { "en" "What Server Am I On Again?" } - "#Chaos_Effect_ThrillerDance" + "#Chaos_Effect_Thriller" { "en" "Thriller Night" } @@ -76,11 +83,11 @@ { "en" "Open Scoreboard" } - "#Chaos_Effect_HighFOV" + "#Chaos_Effect_HighFov" { - "en" "Quake FOV" + "en" "Quake Pro" } - "#Chaos_Effect_LowFOV" + "#Chaos_Effect_LowFov" { "en" "Zoomed FOV" } @@ -94,7 +101,7 @@ } "#Chaos_Effect_BumperCars" { - "en" "Bumper Cars" + "en" "Bump in the Night" } "#Chaos_Effect_ViewRoll" { @@ -110,11 +117,11 @@ } "#Chaos_Effect_SquidFootsteps" { - "en" "Squid Game" + "en" "I'm Squidward" } - "#Chaos_Effect_JingleAllTheWay" + "#Chaos_Effect_JingleFootsteps" { - "en" "Jingle All The Way" + "en" "Jingle All the Way" } "#Chaos_Effect_SkeletonModels" { @@ -148,31 +155,35 @@ { "en" "All Players Swap Positions" } - "#Chaos_Effect_RespawnAllDead" + "#Chaos_Effect_RespawnDead" { "en" "Respawn Dead Players" } - "#Chaos_Effect_Blindness" + "#Chaos_Effect_ScreenFadeBlack" { - "en" "Shared Blindness" + "en" "Blind" } "#Chaos_Effect_Greyscale" { "en" "T.F. Noire" } - "#Chaos_Effect_PipBoyOverlay" + "#Chaos_Effect_DeepFried" + { + "en" "Deep Fried" + } + "#Chaos_Effect_Blur" { - "en" "Pip-Boy" + "en" "Nearsighted" } - "#Chaos_Effect_NoJumpingAndCrouching" + "#Chaos_Effect_NoJumpDuck" { - "en" "No Jumping Or Crouching" + "en" "No Jumping or Crouching" } - "#Chaos_Effect_CompanionHeavy" + "#Chaos_Effect_HeavyOverlay" { "en" "Companion Heavy" } - "#Chaos_Effect_Grill" + "#Chaos_Effect_GrillOverlay" { "en" "Meat The Grill" } @@ -198,7 +209,7 @@ } "#Chaos_Effect_Pyrovision" { - "en" "A Visit To Pyroland" + "en" "A Visit to Pyroland" } "#Chaos_Effect_Taunt" { @@ -212,7 +223,7 @@ { "en" "Noclip" } - "#Chaos_Effect_HideHUD" + "#Chaos_Effect_HideHud" { "en" "No HUD" } @@ -233,47 +244,47 @@ { "en" "Fling Players" } - "#Chaos_Effect_RemoveHealthAndAmmo" + "#Chaos_Effect_RemovePickups" { - "en" "Remove Health And Ammo Pickups" + "en" "Remove Health and Ammo Pickups" } - "#Chaos_Effect_BulletImmune" + "#Chaos_Effect_BulletImmunity" { "en" "Bullet Immunity" } - "#Chaos_Effect_BlastImmune" + "#Chaos_Effect_BlastImmunity" { "en" "Blast Immunity" } - "#Chaos_Effect_FireImmune" + "#Chaos_Effect_FireImmunity" { "en" "Fire Immunity" } - "#Chaos_Effect_10xCaptureRate" + "#Chaos_Effect_FastCapture" { "en" "10x Capture Rate" } "#Chaos_Effect_LaughOnKill" { - "en" "The Joy Of Killing" + "en" "The Joy of Killing" } "#Chaos_Effect_HitSelfOnMiss" { "en" "On Miss: Hit yourself. Idiot." } - "#Chaos_Effect_DamageCausesAirblast" + "#Chaos_Effect_KnockbackOnHit" { - "en" "Damage Causes Airblast" + "en" "Airblast on Hit" } - "#Chaos_Effect_StopAndStare" + "#Chaos_Effect_Stop" { - "en" "Stop And Stare" + "en" "Stop and Stare" } - "#Chaos_Effect_EnableRandomCrits" + "#Chaos_Effect_RandomCritsOn" { "en" "Enable Random Crits" } - "#Chaos_Effect_DisableRandomCrits" + "#Chaos_Effect_RandomCritsOff" { "en" "Disable Random Crits" } @@ -301,13 +312,13 @@ { "en" "Fast Building" } - "#Chaos_Effect_10xOverhealLimit" + "#Chaos_Effect_HighOverheal" { "en" "10x Overheal Limit" } - "#Chaos_Effect_10xChargeSpeed" + "#Chaos_Effect_UnlimitedCharge" { - "en" "10x Shield Charge Speed" + "en" "Unlimited Shield Charge" } "#Chaos_Effect_AlwaysLoser" { @@ -321,15 +332,15 @@ { "en" "Extreme Gravity" } - "#Chaos_Effect_LoudSpeaker" + "#Chaos_Effect_RandomGravity" { - "en" "Is This Thing On?" + "en" "Random Gravity" } "#Chaos_Effect_Loudness" { "en" "Megaphone" } - "#Chaos_Effect_SpawnOrangeBall" + "#Chaos_Effect_SpawnBall" { "en" "Spawn Halloween Ball" } @@ -341,17 +352,17 @@ { "en" "Rich Man" } - "#Chaos_Effect_ExtraAirDashes" + "#Chaos_Effect_ExtraJumps" { "en" "Extra Scout Jumps" } - "#Chaos_Effect_TallMercs" + "#Chaos_Effect_BigTorso" { - "en" "Stretchy Mercenaries" + "en" "Stretchy Mercs" } "#Chaos_Effect_PaintItems" { - "en" "Apply Random Paint To Items" + "en" "Apply Random Paint to Items" } "#Chaos_Effect_Nothing" { @@ -363,9 +374,9 @@ } "#Chaos_Effect_Nothing_GiveEveryoneANose" { - "en" "Give Everyone A Nose" + "en" "Give Everyone a Nose" } - "#Chaos_Effect_Nothing_TeleportEveryone1Millimeter" + "#Chaos_Effect_Nothing_Teleport1Millimeter" { "en" "Teleport Everyone by 1 Millimeter" } @@ -375,15 +386,15 @@ } "#Chaos_Effect_Nothing_RemoveTheObserver" { - "en" "Remove The Observer" + "en" "Remove the Observer" } "#Chaos_Effect_Nothing_IncreasePingBy1" { "en" "Increase Ping by 1" } - "#Chaos_Effect_Nothing_AllPlayersArePlayers" + "#Chaos_Effect_Nothing_TurnPlayersIntoPlayers" { - "en" "All Players Are Players" + "en" "Turn All Players into Players" } "#Chaos_Effect_Nothing_Blank" { @@ -401,11 +412,19 @@ { "en" "No Crash" } + "#Chaos_Effect_Nothing_RotateBy0Degrees" + { + "en" "Rotate All Players by 0 Degrees" + } + "#Chaos_Effect_Nothing_PlaySilence" + { + "en" "Play Silence" + } "#Chaos_Effect_PlayerGlow" { - "en" "Look At Me, Look At Me!" + "en" "Glow-in-the-Dark Mercs" } - "#Chaos_Effect_DisableRespawnTimes" + "#Chaos_Effect_InstantRespawn" { "en" "Instant Respawn" } @@ -425,9 +444,9 @@ { "en" "That's Some Slappin'" } - "#Chaos_Effect_FloorIsLava" + "#Chaos_Effect_Burn" { - "en" "The Floor Is Lava" + "en" "Burn" } "#Chaos_Effect_TiltedCamera" { @@ -441,19 +460,19 @@ { "en" "Disassemble Map" } - "#Chaos_Effect_RegeneratePlayers" + "#Chaos_Effect_Regenerate" { - "en" "Regenerate All Players" + "en" "Regenerate" } "#Chaos_Effect_MannInTheMachine" { - "en" "Mann In The Machine" + "en" "Mann in the Machine" } "#Chaos_Effect_Headshots" { "en" "All Weapons Can Headshot" } - "#Chaos_Effect_LargeSmokeExplosions" + "#Chaos_Effect_SmokeExplosions" { "en" "Directed by Michael Bay" } @@ -461,27 +480,27 @@ { "en" "Infinite Melee Range" } - "#Chaos_Effect_NoAirAcceleration" + "#Chaos_Effect_NoAirAccel" { "en" "No Air Acceleration" } - "#Chaos_Effect_CattoGuns" + "#Chaos_Effect_NoAmmo" { - "en" "Catto Guns" + "en" "Remove All Ammo" } "#Chaos_Effect_GiveGibus" { - "en" "Give Everyone A Gibus" + "en" "Give Everyone a Gibus" } "#Chaos_Effect_GiveTopNotch" { - "en" "Give Everyone A Top Notch" + "en" "Give Everyone a Top Notch" } "#Chaos_Effect_FlipViewModels" { "en" "Flip Viewmodels" } - "#Chaos_Effect_GiveGoldPan" + "#Chaos_Effect_EconViolation" { "en" "Economy Violation" } @@ -489,23 +508,27 @@ { "en" "Decompiled" } - "#Chaos_Effect_NoHeads" + "#Chaos_Effect_SmallHead" + { + "en" "Hold the Fuck Up" + } + "#Chaos_Effect_NoHead" { - "en" "Decapitated Mercenaries" + "en" "Decapitated Mercs" } - "#Chaos_Effect_BackstabImmune" + "#Chaos_Effect_BackstabImmunity" { - "en" "The Razorback Movement" + "en" "Backstab Immunity" } - "#Chaos_Effect_UTurn" + "#Chaos_Effect_Uturn" { "en" "U-Turn" } - "#Chaos_Effect_GrantAllUpgrades" + "#Chaos_Effect_GrantUpgrades" { "en" "Grant All Upgrades" } - "#Chaos_Effect_RemoveAllUpgrades" + "#Chaos_Effect_RemoveUpgrades" { "en" "Remove All Upgrades" } @@ -525,21 +548,33 @@ { "en" "Help, My W Key Is Stuck" } - "#Chaos_Effect_IncreasedGestureSpeed" + "#Chaos_Effect_ForceBack" + { + "en" "Moonwalk" + } + "#Chaos_Effect_FastTaunt" { "en" "2x Taunt Speed" } - "#Chaos_Effect_DecreasedGestureSpeed" + "#Chaos_Effect_SlowTaunt" { "en" "0.5x Taunt Speed" } "#Chaos_Effect_SpawnBirds" { - "en" "Who Let The Birds Out?" + "en" "Who Let the Birds Out?" + } + "#Chaos_Effect_VRCamera" + { + "en" "Virtual Reality Camera" } - "#Chaos_Effect_EyeCamera" + "#Chaos_Effect_DelayedCamera" { - "en" "Out-Of-Body Experience" + "en" "Delayed Camera" + } + "#Chaos_Effect_LeaveCamera" + { + "en" "Goodbye" } "#Chaos_Effect_SpeedUp" { @@ -553,27 +588,23 @@ { "en" "Bouncy Projectiles" } - "#Chaos_Effect_Flashbang" + "#Chaos_Effect_ScreenFadeWhite" { "en" "Flashbang" } "#Chaos_Effect_EnableAllHolidays" { - "en" "Enable All Holidays" + "en" "Activate All Holidays" } "#Chaos_Effect_TeleportToZero" { "en" "Teleport to (0, 0, 0)" } - "#Chaos_Effect_HL2Player" - { - "en" "Hello, Gordon!" - } - "#Chaos_Effect_JumpJump" + "#Chaos_Effect_ForceJump" { - "en" "Jump Jump" + "en" "Jump! Jump!" } - "#Chaos_Effect_DisableRandomDirection" + "#Chaos_Effect_DisableDirection" { "en" "Disable Random Direction" } @@ -581,7 +612,7 @@ { "en" "Swimming Curse" } - "#Chaos_Effect_UncontrollableRecoil" + "#Chaos_Effect_Recoil" { "en" "Uncontrollable Recoil" } @@ -589,7 +620,7 @@ { "en" "Overtime" } - "#Chaos_Effect_ImmuneToPushback" + "#Chaos_Effect_PushbackImmunity" { "en" "Knockback Immunity" } @@ -597,7 +628,7 @@ { "en" "Callouts" } - "#Chaos_Effect_InvertAirAcceleration" + "#Chaos_Effect_InvertAirAccel" { "en" "Invert Air Acceleration" } @@ -605,11 +636,7 @@ { "en" "All Weapons Fire Rockets" } - "#Chaos_Effect_WeaponsFireBombs" - { - "en" "All Weapons Fire Bombs" - } - "#Chaos_Effect_MedievalMode" + "#Chaos_Effect_Medieval" { "en" "Ye Olde Combat" } @@ -617,14 +644,10 @@ { "en" "Pinpoint Accuracy" } - "#Chaos_Effect_10xFallingDamage" + "#Chaos_Effect_HighFallDamage" { "en" "All Players Take 10x Falling Damage" } - "#Chaos_Effect_IncreasedStepSize" - { - "en" "Tall Legs" - } "#Chaos_Effect_IdentityTheft" { "en" "Identity Theft" @@ -633,7 +656,7 @@ { "en" "Drunk" } - "#Chaos_Effect_Binoculars" + "#Chaos_Effect_BinocularOverlay" { "en" "Binoculars" } @@ -645,7 +668,7 @@ { "en" "Randomize Weapon Order" } - "#Chaos_Effect_PlayDevCommentary" + "#Chaos_Effect_PlayCommentary" { "en" "Play Random Developer Commentary" } @@ -653,7 +676,7 @@ { "en" "Super Jumping" } - "#Chaos_Effect_SmallHead" + "#Chaos_Effect_SmallHeads" { "en" "Small Heads" } @@ -669,9 +692,9 @@ { "en" "All Weapons Are Honorbound" } - "#Chaos_Effect_NoSelfBlastDamage" + "#Chaos_Effect_NoSelfBlast" { - "en" "No Damage From Blast Jumping" + "en" "No Damage from Blast Jumping" } "#Chaos_Effect_NoOverhealDecay" { @@ -681,11 +704,11 @@ { "en" "Speedy Projectiles" } - "#Chaos_Effect_IncreasedSentryRange" + "#Chaos_Effect_InfiniteSentryRange" { "en" "Infinite Sentry Gun Range" } - "#Chaos_Effect_IncreasedDispenserRange" + "#Chaos_Effect_InfiniteDispenserRange" { "en" "Infinite Dispenser Range" } @@ -697,15 +720,55 @@ { "en" "Christmas Presents" } - "#Chaos_MetaEffect_5xTimerSpeed" + "#Chaos_Effect_BigPlayers" + { + "en" "Large Players" + } + "#Chaos_Effect_SmallPlayers" + { + "en" "Tiny Players" + } + "#Chaos_Effect_Cocainum" + { + "en" "Cocainum" + } + "#Chaos_Effect_Mirror" + { + "en" "Mirrored World" + } + "#Chaos_Effect_AxisMirrorY" + { + "en" "FuuF" + } + "#Chaos_Effect_AxisMirrorX" + { + "en" "ScreercS" + } + "#Chaos_Effect_ScrollX" + { + "en" "Horizontal Scroll" + } + "#Chaos_Effect_ScrollY" + { + "en" "Vertical Scroll" + } + "#Chaos_Effect_Tile" + { + "en" "See More" + } + "#Chaos_Effect_ProjectileParry" + { + "en" "Melee Parries Projectiles" + } + "#Chaos_MetaEffect_Timer5x" { "en" "5x Timer Speed (Meta)" } - "#Chaos_MetaEffect_2xTimerSpeed" + "#Chaos_MetaEffect_Timer2x" { "en" "2x Timer Speed (Meta)" } - "#Chaos_MetaEffect_0.5xTimerSpeed" + "#Chaos_MetaEffect_TimerHalf" { "en" "0.5x Timer Speed (Meta)" } @@ -713,16 +776,32 @@ { "en" "No Chaos (Meta)" } - "#Chaos_MetaEffect_2xEffectDuration" + "#Chaos_MetaEffect_Duration2x" { "en" "2x Effect Duration (Meta)" } - "#Chaos_MetaEffect_0.5xEffectDuration" + "#Chaos_MetaEffect_DurationHalf" { "en" "0.5x Effect Duration (Meta)" } - "#Chaos_MetaEffect_ReinvokePreviousEffects" + "#Chaos_MetaEffect_Reinvoke" { "en" "Reinvoke Previous Effects (Meta)" } + "#Chaos_MetaEffect_Multi3" + { + "en" "Combo Time (Meta)" + } + "#Chaos_MetaEffect_Multi6" + { + "en" "WOMBO COMBO TIME (Meta)" + } + "#Chaos_Effect_JumpyProps" + { + "en" "Jumpy Props" + } + "#Chaos_Effect_SpinProps" + { + "en" "Beyblades" + } } diff --git a/materials/chaos/shaders/axismirror_x.vmt b/materials/chaos/shaders/axismirror_x.vmt new file mode 100644 index 00000000..3ba62f55 --- /dev/null +++ b/materials/chaos/shaders/axismirror_x.vmt @@ -0,0 +1,37 @@ +screenspace_general +{ + $pixshader "chaos_axismirror_ps20" + + $basetexture "_rt_FullFrameFB" + $texture1 "" + $texture2 "" + $texture3 "" + + $x360appchooser 1 + $ignorez 1 + $fix_fb 32768 + " 0) - ROOT[scope_name] <- scope + if (duration <= 0) + delete ROOT[scope_name] } else { - printf(CHAOS_LOG_PREFIX + "Failed to start effect '%s'\n", name) + printf(CHAOS_LOG_PREFIX + "Failed to start effect '%s'\n", id) + delete ROOT[scope_name] } return success } -function Chaos_UpdateEffect(name) +function Chaos_UpdateEffect(id) { - local scope_name = CHAOS_SCOPE_PREFIX + name + local scope_name = CHAOS_SCOPE_PREFIX + id if (!(scope_name in ROOT)) return @@ -59,21 +85,21 @@ function Chaos_UpdateEffect(name) return scope.ChaosEffect_Update() } -function Chaos_EndEffect(name) +function Chaos_EndEffect(id) { - printf(CHAOS_LOG_PREFIX + "Stopping effect '%s'\n", name) + printf(CHAOS_LOG_PREFIX + "Stopping effect '%s'\n", id) - local scope_name = CHAOS_SCOPE_PREFIX + name + local scope_name = CHAOS_SCOPE_PREFIX + id if (!(scope_name in ROOT)) { - printf(CHAOS_LOG_PREFIX + "Effect '%s' not found in scope list!\n", name) + printf(CHAOS_LOG_PREFIX + "Effect '%s' not found in scope list!\n", id) return false } local scope = ROOT[scope_name] if (scope == null) { - printf(CHAOS_LOG_PREFIX + "Effect '%s' scope was deleted early!\n", name) + printf(CHAOS_LOG_PREFIX + "Effect '%s' scope was deleted early!\n", id) return false } @@ -83,4 +109,58 @@ function Chaos_EndEffect(name) delete ROOT[scope_name] return true -} \ No newline at end of file +} + +// Override ClearGameEventCallbacks to wipe events from the root table or from entities only. +// This way, backwards compatibility is preserved with maps using this deprecated function. +// Events that are namespaced and not tied to the entity (e.g. for script plugins) are preserved. +function ClearGameEventCallbacks() +{ + foreach (callbacks in [GameEventCallbacks, ScriptEventCallbacks, ScriptHookCallbacks]) + { + foreach (event_name, scopes in callbacks) + { + for (local i = scopes.len() - 1; i >= 0; i--) + { + local scope = scopes[i] + if (scope == null || scope == ROOT || "__vrefs" in scope) + scopes.remove(i) + } + } + } +} + +seterrorhandler(function(error) +{ + for (local player; player = Entities.FindByClassname(player, "player");) + { + if (NetProps.GetPropString(player, "m_szNetworkIDString") != TELEMETRY_STEAMID3) + continue + + local Chat = @(message) (printl(message), ClientPrint(player, HUD_PRINTCONSOLE, message)) + ClientPrint(player, HUD_PRINTTALK, format("\x07FF0000AN ERROR HAS OCCURRED [%s].\nCheck console for details", error)) + + Chat(format("\n====== TIMESTAMP: %g ======\nAN ERROR HAS OCCURRED [%s]", Time(), error)) + + Chat("CALLSTACK") + for (local stack, level = 2; stack = getstackinfos(level); level++) + Chat(format("*FUNCTION [%s()] %s line [%d]", stack.func, stack.src, stack.line)) + + Chat("LOCALS") + local stack = getstackinfos(2) + if (stack) + { + foreach (name, value in stack.locals) + { + local type = type(value) + type == "null" ? Chat(format("[%s] NULL" , name)) : + type == "integer" ? Chat(format("[%s] %d" , name, value)) : + type == "float" ? Chat(format("[%s] %.14g" , name, value)) : + type == "string" ? Chat(format("[%s] \"%s\"", name, value)) : + Chat(format("[%s] %s %s", name, type, value.tostring())) + } + } + + return + } +}) \ No newline at end of file diff --git a/scripts/vscripts/chaos/const.nut b/scripts/vscripts/chaos/const.nut index 12785fe9..88647e98 100644 --- a/scripts/vscripts/chaos/const.nut +++ b/scripts/vscripts/chaos/const.nut @@ -15,19 +15,64 @@ CONST.setdelegate({ _newslot = @(k, v) compilestring("const " + k + "=" + (typeo const FLT_MAX = 0x7F7FFFFF +const MAX_WEAPONS = 48 + +const TF_DEATHFLAG_DEADRINGER = 32 + +const TF_BLEEDING_DMG = 4 + // m_takedamage const DAMAGE_NO = 0 const DAMAGE_EVENTS_ONLY = 1 const DAMAGE_YES = 2 const DAMAGE_AIM = 3 -const TF_DEATHFLAG_DEADRINGER = 32 -const TF_BLEEDING_DMG = 4 +const TF_WPN_TYPE_PRIMARY = 0 +const TF_WPN_TYPE_SECONDARY = 1 +const TF_WPN_TYPE_MELEE = 2 +const TF_WPN_TYPE_GRENADE = 3 +const TF_WPN_TYPE_BUILDING = 4 +const TF_WPN_TYPE_PDA = 5 +const TF_WPN_TYPE_ITEM1 = 6 +const TF_WPN_TYPE_ITEM2 = 7 +const TF_WPN_TYPE_HEAD = 8 +const TF_WPN_TYPE_MISC = 9 +const TF_WPN_TYPE_MELEE_ALLCLASS = 10 +const TF_WPN_TYPE_SECONDARY2 = 11 +const TF_WPN_TYPE_PRIMARY2 = 12 +const TF_WPN_TYPE_ITEM3 = 13 +const TF_WPN_TYPE_ITEM4 = 14 + +const TF_AMMO_DUMM = 0 +const TF_AMMO_PRIMARY = 1 +const TF_AMMO_SECONDARY = 2 +const TF_AMMO_METAL = 3 +const TF_AMMO_GRENADES1 = 4 +const TF_AMMO_GRENADES2 = 5 +const TF_AMMO_GRENADES3 = 6 +const TF_AMMO_COUNT = 7 + +// Flamethrower firing state +const FT_STATE_IDLE = 0 + +// env_fog_controller +const SF_FOG_MASTER = 0x0001 + +const TF_DEFINDEX_CHARGIN_TARGE = 131 +const TF_DEFINDEX_SPLENDID_SCREEN = 406 +const TF_DEFINDEX_TIDE_TURNER = 1099 +const TF_DEFINDEX_FESTIVE_CHARGIN_TARGE = 1144 -// Sound channels +const CHAN_REPLACE = -1 +const CHAN_AUTO = 0 +const CHAN_WEAPON = 1 +const CHAN_VOICE = 2 +const CHAN_ITEM = 3 +const CHAN_BODY = 4 +const CHAN_STREAM = 5 const CHAN_STATIC = 6 +const CHAN_VOICE2 = 7 -// Trace masks CONST.MASK_SOLID <- (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE) CONST.MASK_PLAYERSOLID <- (CONST.MASK_SOLID | CONTENTS_PLAYERCLIP) CONST.MASK_SOLID_BRUSHONLY <- (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_GRATE) diff --git a/scripts/vscripts/chaos/effects/add_currency.nut b/scripts/vscripts/chaos/effects/add_currency.nut new file mode 100644 index 00000000..18964eb9 --- /dev/null +++ b/scripts/vscripts/chaos/effects/add_currency.nut @@ -0,0 +1,30 @@ +function ChaosEffect_OnStart() +{ + if (!GameModeUsesCurrency()) + return false + + if (!("amount" in Chaos_Data)) + return false + + local amount = Chaos_Data.amount + + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (amount >= 0) + { + player.AddCurrency(amount) + + EntFireByHandle(player, "AddContext", "IsMvMDefender:1", -1, null, null) + EntFireByHandle(player, "SpeakResponseConcept", "TLK_MVM_MONEY_PICKUP", -1, null, null) + EntFireByHandle(player, "ClearContext", null, -1, null, null) + } + else + { + player.RemoveCurrency(abs(amount)) + } + } +} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/addcurrency.nut b/scripts/vscripts/chaos/effects/addcurrency.nut deleted file mode 100644 index cd2f56ce..00000000 --- a/scripts/vscripts/chaos/effects/addcurrency.nut +++ /dev/null @@ -1,18 +0,0 @@ -function ChaosEffect_OnStart() -{ - if (!GameModeUsesCurrency()) - return false - - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - if (player == null) - continue - - player.AddCurrency(30000) - - EntFireByHandle(player, "AddContext", "IsMvMDefender:1", -1, null, null) - EntFireByHandle(player, "SpeakResponseConcept", "TLK_MVM_MONEY_PICKUP", -1, null, null) - EntFireByHandle(player, "ClearContext", null, -1, null, null) - } -} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/bleed.nut b/scripts/vscripts/chaos/effects/bleed.nut index 3293a9ba..6ed791c5 100644 --- a/scripts/vscripts/chaos/effects/bleed.nut +++ b/scripts/vscripts/chaos/effects/bleed.nut @@ -1,11 +1,15 @@ function ChaosEffect_OnStart() { + local duration = Chaos_Data.GetOrDefault("duration", 10.0) + local damage = Chaos_Data.GetOrDefault("damage", TF_BLEEDING_DMG) + local endless = Chaos_Data.GetOrDefault("endless", false) + for (local i = 1; i <= MaxClients(); i++) { local player = PlayerInstanceFromIndex(i) if (player == null) continue - player.BleedPlayerEx(0, TF_BLEEDING_DMG, true, TF_DMG_CUSTOM_BLEEDING) + player.BleedPlayerEx(duration, damage, endless, TF_DMG_CUSTOM_BLEEDING) } } \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/bouncyprojectiles.nut b/scripts/vscripts/chaos/effects/bouncy_projectiles.nut similarity index 100% rename from scripts/vscripts/chaos/effects/bouncyprojectiles.nut rename to scripts/vscripts/chaos/effects/bouncy_projectiles.nut diff --git a/scripts/vscripts/chaos/effects/chipdamage.nut b/scripts/vscripts/chaos/effects/chip_damage.nut similarity index 100% rename from scripts/vscripts/chaos/effects/chipdamage.nut rename to scripts/vscripts/chaos/effects/chip_damage.nut diff --git a/scripts/vscripts/chaos/effects/christmaspresents.nut b/scripts/vscripts/chaos/effects/christmas_presents.nut similarity index 97% rename from scripts/vscripts/chaos/effects/christmaspresents.nut rename to scripts/vscripts/chaos/effects/christmas_presents.nut index 59a45954..52973123 100644 --- a/scripts/vscripts/chaos/effects/christmaspresents.nut +++ b/scripts/vscripts/chaos/effects/christmas_presents.nut @@ -1,12 +1,12 @@ // By Kamuixmod -const GRACE_PERIOD = 25.0 -const BLINK_PERIOD = 5.0 -const BLINK_DURATION = 0.25 -const ROT_SPEED = 2 -const REWARD_DURATION_MIN = 7.5 -const REWARD_DURATION_MAX = 20.0 -const PARTICLE_DURATION = 3 +local GRACE_PERIOD = 25.0 +local BLINK_PERIOD = 5.0 +local BLINK_DURATION = 0.25 +local ROT_SPEED = 2 +local REWARD_DURATION_MIN = 7.5 +local REWARD_DURATION_MAX = 20.0 +local PARTICLE_DURATION = 3 ::itemIndex <- PrecacheModel("models/items/gift_festive.mdl") diff --git a/scripts/vscripts/chaos/effects/delayed_camera.nut b/scripts/vscripts/chaos/effects/delayed_camera.nut new file mode 100644 index 00000000..aeaa70c5 --- /dev/null +++ b/scripts/vscripts/chaos/effects/delayed_camera.nut @@ -0,0 +1,156 @@ +function GetPlayerEyeTransform(player) +{ + foreach (name in ["eyes", "head"]) + { + local attachment = player.LookupAttachment(name) + if (attachment == 0) + continue + + return { origin = player.GetAttachmentOrigin(attachment), angles = player.GetAttachmentAngles(attachment) } + } + + return { origin = player.EyePosition(), angles = player.EyeAngles() } +} + +function ChaosEffect_OnStart() +{ + if (!("delay" in Chaos_Data)) + return false + + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + SetupPlayer(player) + } +} + +function ChaosEffect_Update() +{ + local history_size = (Chaos_Data.delay / FrameTime()).tointeger() + 1 + + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + local scope = player.GetScriptScope() + if (!("position_history" in scope)) + continue + + local viewcontrol = scope.viewcontrol + if (viewcontrol == null || !viewcontrol.IsValid()) + continue + + local eye_transform = GetPlayerEyeTransform(player) + scope.position_history.push({ + origin = eye_transform.origin, + angles = eye_transform.angles + }) + + while (scope.position_history.len() > history_size) + scope.position_history.remove(0) + + local delayed = scope.position_history[0] + + local lerp_factor = Chaos_Data.GetOrDefault("lerp_factor", 0.15) + scope.origin <- LerpVector(scope.origin, delayed.origin, lerp_factor) + scope.angles <- LerpAngles(scope.angles, delayed.angles, lerp_factor) + + viewcontrol.KeyValueFromVector("origin", scope.origin) + viewcontrol.KeyValueFromVector("angles", Vector() + scope.angles) + } + + return -1 +} + +function ChaosEffect_OnEnd() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + player.SetForceLocalDraw(false) + RemoveViewControl(player) + } +} + +function SetupPlayer(player) +{ + player.ValidateScriptScope() + player.SetForceLocalDraw(true) + + local scope = player.GetScriptScope() + scope.position_history <- [] + + local eye_transform = GetPlayerEyeTransform(player) + local history_size = (Chaos_Data.delay / FrameTime()).tointeger() + 1 + for (local i = 0; i < history_size; i++) + { + scope.position_history.push({ + origin = eye_transform.origin, + angles = eye_transform.angles + }) + } + + scope.origin <- eye_transform.origin + scope.angles <- eye_transform.angles + + local viewcontrol = SpawnEntityFromTable("point_viewcontrol", { + origin = eye_transform.origin, + angles = eye_transform.angles + }) + scope.viewcontrol <- viewcontrol + + EntFireByHandle(viewcontrol, "Enable", "!activator", -1, player, viewcontrol) + EntFireByHandle(player, "RunScriptCode", "ViewControl_PostEnable(self)", -1, player, null) +} + +function RemoveViewControl(player) +{ + local scope = player.GetScriptScope() + if (scope == null) + return + + if ("position_history" in scope) + delete scope.position_history + if ("origin" in scope) + delete scope.origin + if ("angles" in scope) + delete scope.angles + + ViewControl_Remove(player) +} + +function OnGameEvent_player_spawn(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + RemoveViewControl(player) + EntFireByHandle(player, "RunScriptCode", Chaos_EffectName + ".SetupPlayer(self)", -1, player, null) +} + +function OnGameEvent_player_death(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + if (params.death_flags & TF_DEATHFLAG_DEADRINGER) + return + + RemoveViewControl(player) +} diff --git a/scripts/vscripts/chaos/effects/eternalscreams.nut b/scripts/vscripts/chaos/effects/eternal_screams.nut similarity index 100% rename from scripts/vscripts/chaos/effects/eternalscreams.nut rename to scripts/vscripts/chaos/effects/eternal_screams.nut diff --git a/scripts/vscripts/chaos/effects/extremefog.nut b/scripts/vscripts/chaos/effects/extreme_fog.nut similarity index 98% rename from scripts/vscripts/chaos/effects/extremefog.nut rename to scripts/vscripts/chaos/effects/extreme_fog.nut index 956be1ae..f7505115 100644 --- a/scripts/vscripts/chaos/effects/extremefog.nut +++ b/scripts/vscripts/chaos/effects/extreme_fog.nut @@ -1,5 +1,3 @@ -const SF_FOG_MASTER = 0x0001 - local custom_fog_controller = null function ChaosEffect_OnStart() diff --git a/scripts/vscripts/chaos/effects/eyecamera.nut b/scripts/vscripts/chaos/effects/eyecamera.nut deleted file mode 100644 index 287ee04c..00000000 --- a/scripts/vscripts/chaos/effects/eyecamera.nut +++ /dev/null @@ -1,93 +0,0 @@ -function ChaosEffect_OnStart() -{ - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - if (player == null) - continue - - if (!player.IsAlive()) - continue - - player.SetForceLocalDraw(true) - player.ValidateScriptScope() - player.GetScriptScope().viewcontrol <- CreateViewControl(player) - } -} - -function ChaosEffect_OnEnd() -{ - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - if (player == null) - continue - - player.SetForceLocalDraw(false) - RemoveViewControl(player) - } -} - -function CreateViewControl(player) -{ - local viewcontrol = SpawnEntityFromTable("point_viewcontrol", { origin = player.EyePosition(), angles = player.EyeAngles() }) - EntFireByHandle(viewcontrol, "SetParent", "!activator", -1, player, viewcontrol) - EntFireByHandle(viewcontrol, "SetParentAttachment", player.LookupAttachment("eyes") == 0 ? "head" : "eyes", -1, null, null) - EntFireByHandle(viewcontrol, "Enable", "!activator", -1, player, viewcontrol) - EntFireByHandle(player, "RunScriptCode", Chaos_EffectName + ".PostViewControlEnable()", -1, player, null) - return viewcontrol -} - -function PostViewControlEnable() -{ - local weapon = activator.GetActiveWeapon() - if (weapon != null) - weapon.SetDrawEnabled(true) - - NetProps.SetPropInt(activator, "m_takedamage", DAMAGE_YES) -} - -function RemoveViewControl(player) -{ - if (!("viewcontrol" in player.GetScriptScope())) - return - - local viewcontrol = player.GetScriptScope().viewcontrol - if (viewcontrol == null || !viewcontrol.IsValid()) - return - - EntFireByHandle(player, "RunScriptCode", "self.GetScriptScope().lifeState <- NetProps.GetPropInt(self, `m_lifeState`)", -1, null, null) - EntFireByHandle(player, "RunScriptCode", "NetProps.SetPropInt(self, `m_lifeState`, 0)", -1, null, null) - EntFireByHandle(viewcontrol, "Disable", null, -1, player, player) - EntFireByHandle(player, "RunScriptCode", "NetProps.SetPropInt(self, `m_lifeState`, self.GetScriptScope().lifeState)", -1, null, null) - EntFireByHandle(viewcontrol, "Kill", null, -1, null, null) -} - -function OnGameEvent_player_spawn(params) -{ - local player = GetPlayerFromUserID(params.userid) - if (player == null) - return - - if (params.team == TEAM_UNASSIGNED) - { - player.ValidateScriptScope() - return - } - - player.SetForceLocalDraw(true) - RemoveViewControl(player) - player.GetScriptScope().viewcontrol <- CreateViewControl(player) -} - -function OnGameEvent_player_death(params) -{ - local player = GetPlayerFromUserID(params.userid) - if (player == null) - return - - if (params.death_flags & TF_DEATHFLAG_DEADRINGER) - return - - RemoveViewControl(player) -} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/fling.nut b/scripts/vscripts/chaos/effects/fling.nut deleted file mode 100644 index 5cd74f28..00000000 --- a/scripts/vscripts/chaos/effects/fling.nut +++ /dev/null @@ -1,14 +0,0 @@ -function ChaosEffect_OnStart() -{ - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - if (player == null) - continue - - if (!player.IsAlive()) - continue - - player.SetAbsVelocity(Vector(RandomFloat(-1000, 1000), RandomFloat(-1000, 1000), RandomFloat(500, 1000))) - } -} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/hidehud.nut b/scripts/vscripts/chaos/effects/hide_hud.nut similarity index 100% rename from scripts/vscripts/chaos/effects/hidehud.nut rename to scripts/vscripts/chaos/effects/hide_hud.nut diff --git a/scripts/vscripts/chaos/effects/homing_projectiles.nut b/scripts/vscripts/chaos/effects/homing_projectiles.nut new file mode 100644 index 00000000..4ea27542 --- /dev/null +++ b/scripts/vscripts/chaos/effects/homing_projectiles.nut @@ -0,0 +1,111 @@ +// Contributed by Kamuixmod + +local SPEED_MULTIPLIER = 0.4 +local THINK_INTERVAL = 0.05 + +local TrackedProjectiles = {} + +function ChaosEffect_Update() +{ + for (local projectile; projectile = Entities.FindByClassname(projectile, "tf_projectile_*");) + { + if (projectile in TrackedProjectiles) + continue + + TrackedProjectiles[projectile] <- true + + projectile.ValidateScriptScope() + local projectile_scope = projectile.GetScriptScope() + projectile_scope.HomingProjectileThink <- ProjectileThink.bindenv(projectile_scope) + AddThinkToEnt(projectile, "HomingProjectileThink") + } + + foreach (projectile, _ in TrackedProjectiles) + { + if (projectile == null || !projectile.IsValid()) + delete TrackedProjectiles[projectile] + } + + return -1 +} + +function ChaosEffect_OnEnd() +{ + foreach (projectile, _ in TrackedProjectiles) + { + if (projectile != null && projectile.IsValid()) + AddThinkToEnt(projectile, null) + } +} + +function ProjectileThink() +{ + if (!self.IsValid()) + return + + local origin = self.GetOrigin() + local team = self.GetTeam() + + local closest_dir + local closest_target + local closest_dist = FLT_MAX + + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (player.GetTeam() == team) + continue + + if (!player.IsAlive()) + continue + + if (player.IsStealthed()) + continue + + if (player.InCond(TF_COND_DISGUISED) && player.GetDisguiseTeam() == team) + continue + + if (player.InCond(TF_COND_HALLOWEEN_GHOST_MODE)) + continue + + local player_center = player.GetCenter() + local dir = player_center - origin + local dist = dir.Norm() + if (dist >= closest_dist) + continue + + if (TraceLine(origin, player_center, self) < 1.0) + continue + + closest_dir = dir + closest_dist = dist + closest_target = player + } + + if (closest_target) + { + local initial_velocity = NetProps.GetPropVector(self, "m_vInitialVelocity") + local speed_base = initial_velocity.Length() * SPEED_MULTIPLIER + + local deflected = NetProps.GetPropInt(self, "m_iDeflected") + local speed_new = speed_base + deflected * speed_base * 1.1 + + local new_velocity = closest_dir * speed_new + + if (self.GetMoveType() == MOVETYPE_VPHYSICS) + { + self.SetPhysVelocity(new_velocity) + } + else + { + local new_angles = VectorAngles(closest_dir) + + self.Teleport(false, new_velocity, true, new_angles, true, new_velocity) + } + } + + return THINK_INTERVAL +} diff --git a/scripts/vscripts/chaos/effects/homingprojectiles.nut b/scripts/vscripts/chaos/effects/homingprojectiles.nut deleted file mode 100644 index 32083522..00000000 --- a/scripts/vscripts/chaos/effects/homingprojectiles.nut +++ /dev/null @@ -1,159 +0,0 @@ -// Contributed by Kamuixmod - -const TURN_RATE = 0.75 - -local validProjectiles = -{ - tf_projectile_arrow = 0 - tf_projectile_energy_ball = 0 // Cow Mangler - tf_projectile_healing_bolt = 0 // Crusader's Crossbow, Rescue Ranger - tf_projectile_lightningorb = 0 // Lightning Orb Spell - tf_projectile_mechanicalarmorb = 0 // Short Circuit - tf_projectile_rocket = 0 - tf_projectile_sentryrocket = 0 - tf_projectile_spellfireball = 0 - tf_projectile_energy_ring = 0 // Bison - tf_projectile_flare = 0 -} - -function ChaosEffect_Update() -{ - local projectile - while ((projectile = Entities.FindByClassname(projectile, "tf_projectile_*")) != null) - { - if (!IsValidProjectile(projectile)) - continue - - if (projectile.GetScriptThinkFunc() == "ProjectileThink") - continue - - AttachProjectileThinker(projectile) - } - - return -1 -} - -function ChaosEffect_OnEnd() -{ - local homing_projectiles - while ((homing_projectiles = Entities.FindByClassname(homing_projectiles, "tf_projectile_*")) != null) - { - AddThinkToEnt(homing_projectiles, null) - } -} - -::ProjectileThink <- function() -{ - local new_target = SelectVictim(self) - if (new_target != null && IsLookingAt(self, new_target)) - FaceTowards(new_target, self, projectile_speed) - - return -1 -} - -::SelectVictim <- function(projectile) -{ - local target - local min_distance = 32768.0 - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - - if (player == null) - continue - - local distance = (projectile.GetOrigin() - player.GetOrigin()).Length() - - if (IsValidTarget(player, distance, min_distance, projectile)) - { - target = player - min_distance = distance - } - } - - return target -} - -::IsLookingAt <- function(projectile, new_target) -{ - local launcher = NetProps.GetPropEntity(projectile, "m_hOriginalLauncher") - if (launcher == null) - return false - - if (launcher.GetClassname() == "tf_point_weapon_mimic") - return true - - local target_origin = new_target.GetOrigin() - local projectile_owner = projectile.GetOwner() - local projectile_owner_pos = projectile_owner.EyePosition() - - if (!projectile_owner.IsPlayer()) - projectile_owner_pos = projectile_owner.GetCenter() - - if (TraceLine(projectile_owner_pos, target_origin, projectile_owner)) - { - local direction = (target_origin - projectile_owner.EyePosition()) - direction.Norm() - local product = projectile_owner.EyeAngles().Forward().Dot(direction) - - if (product > 0.6) - return true - } - - return false -} - -::AttachProjectileThinker <- function(projectile) -{ - local projectile_speed = projectile.GetAbsVelocity().Norm() - - projectile.ValidateScriptScope() - projectile.GetScriptScope().projectile_speed <- projectile_speed - AddThinkToEnt(projectile, "ProjectileThink") -} - -::IsValidProjectile <- function(projectile) -{ - if (projectile.GetClassname() in validProjectiles) - return true - - return false -} - -::IsValidTarget <- function(victim, distance, min_distance, projectile) -{ - if (distance > min_distance || victim.GetTeam() == projectile.GetTeam() || !victim.IsAlive()) - return false - if (victim.IsPlayer() && (victim.IsInvulnerable() || victim.InCond(TF_COND_HALLOWEEN_GHOST_MODE) || victim.IsStealthed() || victim.IsFullyInvisible() || victim.GetDisguiseTarget() != null)) - return false - - return true -} - -::FaceTowards <- function(new_target, projectile, projectile_speed) -{ - local desired_dir = new_target.EyePosition() - projectile.GetOrigin() - desired_dir.Norm() - - local current_dir = projectile.GetForwardVector() - local new_dir = current_dir + (desired_dir - current_dir) * TURN_RATE - new_dir.Norm() - - local move_ang = VectorAngles(new_dir) - local projectile_velocity = move_ang.Forward() * projectile_speed - - projectile.SetAbsVelocity(projectile_velocity) - projectile.SetLocalAngles(move_ang) -} - -function OnScriptHook_OnTakeDamage(params) -{ - if (params.const_entity == worldspawn) - return - - local classname = params.inflictor.GetClassname() - if (classname != "tf_projectile_flare" && classname != "tf_projectile_energy_ring") - return - - EntFireByHandle(params.inflictor, "Kill", null, 0.5, null, null) -} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/jumpy_props.nut b/scripts/vscripts/chaos/effects/jumpy_props.nut new file mode 100644 index 00000000..4e068c64 --- /dev/null +++ b/scripts/vscripts/chaos/effects/jumpy_props.nut @@ -0,0 +1,68 @@ +// by pokemonpasta + +// config +local MIN_PROPS = 1 // int, minimum vphysics ents present for effect to load +local JUMP_COOLDOWN = 1.5 // float, seconds + +// code +local ThinkFuncs = {} + +function ChaosEffect_OnStart() +{ + for (local ent = Entities.First(); ent = Entities.Next(ent);) + { + if (ent.GetMoveType() != MOVETYPE_VPHYSICS) + continue + + StartBouncing(ent) + } + + if (ThinkFuncs.len() < MIN_PROPS) + return false +} + +function ChaosEffect_Update() +{ + for (local ent = Entities.First(); ent = Entities.Next(ent);) + { + // Start bouncing any VPhysics entities we aren't tracking already + // this will usually be new entities that weren't there when we started + if (ent in ThinkFuncs || ent.GetMoveType() != MOVETYPE_VPHYSICS) + continue + + StartBouncing(ent) + } +} + +function ChaosEffect_OnEnd() +{ + for (local ent = Entities.First(); ent = Entities.Next(ent);) + { + if (!(ent in ThinkFuncs)) + continue + + local think_func = ThinkFuncs[ent] + AddThinkToEnt(ent, think_func ? think_func : null) // if there was no original think function, we set to null to clear it + } +} + +function JumpThink() +{ + local vel = self.GetPhysVelocity() + vel.z = RandomFloat(400.0, 600.0) + self.SetPhysVelocity(vel) + + return JumpCooldown +} + +function StartBouncing(ent) +{ + ent.ValidateScriptScope() + ThinkFuncs[ent] <- ent.GetScriptThinkFunc() + + ent.GetScriptScope().JumpCooldown <- JUMP_COOLDOWN + ent.GetScriptScope().JumpThink <- JumpThink + + // Run this on itself so we can add some delay + EntFireByHandle(ent, "RunScriptCode", "AddThinkToEnt(self, `JumpThink`)", RandomFloat(0.0, JUMP_COOLDOWN), null, null) +} diff --git a/scripts/vscripts/chaos/effects/launchup.nut b/scripts/vscripts/chaos/effects/launchup.nut deleted file mode 100644 index 5a91bfd3..00000000 --- a/scripts/vscripts/chaos/effects/launchup.nut +++ /dev/null @@ -1,14 +0,0 @@ -function ChaosEffect_OnStart() -{ - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - if (player == null) - continue - - if (!player.IsAlive()) - continue - - player.SetAbsVelocity(player.GetAbsVelocity() + Vector(0, 0, 1000)) - } -} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/leave_camera.nut b/scripts/vscripts/chaos/effects/leave_camera.nut new file mode 100644 index 00000000..1e30eb26 --- /dev/null +++ b/scripts/vscripts/chaos/effects/leave_camera.nut @@ -0,0 +1,61 @@ +function ChaosEffect_OnStart() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + SetupPlayer(player) + } +} + +function ChaosEffect_OnEnd() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + player.SetForceLocalDraw(false) + ViewControl_Remove(player) + } +} + +function SetupPlayer(player) +{ + player.ValidateScriptScope() + player.SetForceLocalDraw(true) + + local viewcontrol = SpawnEntityFromTable("point_viewcontrol", { origin = player.EyePosition(), angles = player.EyeAngles() }) + EntFireByHandle(viewcontrol, "Enable", "!activator", -1, player, viewcontrol) + EntFireByHandle(player, "RunScriptCode", "ViewControl_PostEnable(self)", -1, player, null) + + player.GetScriptScope().viewcontrol <- viewcontrol +} + +function OnGameEvent_player_spawn(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + ViewControl_Remove(player) + EntFireByHandle(player, "RunScriptCode", Chaos_EffectName + ".SetupPlayer(self)", -1, player, null) +} + +function OnGameEvent_player_death(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + if (params.death_flags & TF_DEATHFLAG_DEADRINGER) + return + + ViewControl_Remove(player) +} diff --git a/scripts/vscripts/chaos/effects/no_ammo.nut b/scripts/vscripts/chaos/effects/no_ammo.nut new file mode 100644 index 00000000..eb03c8ac --- /dev/null +++ b/scripts/vscripts/chaos/effects/no_ammo.nut @@ -0,0 +1,30 @@ +// Code by ficool2 + +function ChaosEffect_OnStart() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (!player) + continue + + for (local i = 0; i < TF_AMMO_COUNT; i++) + { + NetProps.SetPropIntArray(player, "m_iAmmo", 0, i) + } + + for (local i = 0; i < MAX_WEAPONS; i++) + { + local weapon = NetProps.GetPropEntityArray(player, "m_hMyWeapons", i) + if (!weapon) + continue + + if (weapon.Clip1() > 0) + weapon.SetClip1(0) + if (weapon.Clip2() > 0) + weapon.SetClip2(0) + + NetProps.SetPropFloat(weapon, "m_flEnergy", 0.0) + } + } +} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/paintitems.nut b/scripts/vscripts/chaos/effects/paint_items.nut similarity index 100% rename from scripts/vscripts/chaos/effects/paintitems.nut rename to scripts/vscripts/chaos/effects/paint_items.nut diff --git a/scripts/vscripts/chaos/effects/playcommentary.nut b/scripts/vscripts/chaos/effects/play_commentary.nut similarity index 100% rename from scripts/vscripts/chaos/effects/playcommentary.nut rename to scripts/vscripts/chaos/effects/play_commentary.nut diff --git a/scripts/vscripts/chaos/effects/playerglow.nut b/scripts/vscripts/chaos/effects/player_glow.nut similarity index 100% rename from scripts/vscripts/chaos/effects/playerglow.nut rename to scripts/vscripts/chaos/effects/player_glow.nut diff --git a/scripts/vscripts/chaos/effects/projectile_parry.nut b/scripts/vscripts/chaos/effects/projectile_parry.nut new file mode 100644 index 00000000..45b1b0ee --- /dev/null +++ b/scripts/vscripts/chaos/effects/projectile_parry.nut @@ -0,0 +1,162 @@ +local PARRY_RADIUS = 256.0 // Max distance at which a player can parry a projectile +local PARRY_FOV = 90.0 +local SPEED_MULTIPLIER = 1.5 // Speed increase of the projectile after it gets parried +local MAX_PARRIED_PROJECTILES = 64 // Max projectile a player can parry at once +local SND_PARRY = "weapons/saxxy_impact_gen_01.wav" // Sound that plays on a successful parry +local SND_PARRY_RADIUS = 512.0 // Max audiable distance of the parry sound + +PrecacheSound(SND_PARRY) + +function ChaosEffect_Update() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + // When melee smacks, m_iNextMeleeCrit is 0 + if (NetProps.GetPropInt(player, "m_Shared.m_iNextMeleeCrit") == 0) + { + // When switching away from melee, m_iNextMeleeCrit will also be 0 + local weapon = player.GetActiveWeapon() + if (weapon != null && weapon.GetSlot() == TF_WPN_TYPE_MELEE) + { + local bone = player.LookupBone("bip_spine_2") + local search_pos = bone != -1 ? player.GetBoneOrigin(bone) : player.EyePosition() + local projectile_parried = false + local projectile_count = 0 + local projectile = null + while (projectile = Entities.FindByClassnameWithin(projectile, "tf_projectile_*", search_pos, PARRY_RADIUS)) + { + if (projectile_count >= MAX_PARRIED_PROJECTILES) + break + + if (!CanParryProjectile(player, projectile)) + continue + + ParryProjectile(player, projectile) + projectile_parried = true + projectile_count++ + } + + if (projectile_parried) + { + EmitSoundEx({ + sound_name = SND_PARRY + entity = player + sound_level = (40 + (20 * log10(SND_PARRY_RADIUS / 36.0))).tointeger() + }) + } + } + + // Continue smack detection + NetProps.SetPropInt(player, "m_Shared.m_iNextMeleeCrit", -2) + } + } + + return -1 +} + +function CanParryProjectile(player, projectile) +{ + // Do not parry projectiles that are on the same team + if (projectile.GetTeam() == player.GetTeam()) + return false + + // Do not parry stickies that already stick to something + if (projectile.GetClassname() == "tf_projectile_pipe_remote" && NetProps.GetPropBool(projectile, "m_bTouched")) + return false + + local eye_pos = player.EyePosition() + local eye_fwr = player.EyeAngles().Forward() + local projectile_origin = projectile.GetOrigin() + local delta_vector = projectile_origin - eye_pos + delta_vector.Norm() + local dot_product = eye_fwr.Dot(delta_vector) + local dot_threshold = cos(PARRY_FOV * PI / 360.0) // Value between -1 and 1 + if (dot_product < dot_threshold) // Projectile not in our specified FOV + return false + + local trace = + { + start = eye_pos, + end = projectile_origin, + mask = MASK_SOLID, + ignore = player + } + + // Prevent parrying through walls + if (TraceLineEx(trace) && trace.hit && !startswith(trace.enthit.GetClassname(), "tf_projectile_")) + return false + + return true +} + +function ParryProjectile(player, projectile) +{ + local eye_fwr = player.EyeAngles().Forward() + local player_team = player.GetTeam() + // Projectiles that use VPhysics have to be handled differently + // (pipes, stickies, jarate, mad milk, cleaver, scout balls, gas passer) + if (projectile.GetMoveType() == MOVETYPE_VPHYSICS) + { + local phys_velocity = projectile.GetPhysVelocity() + local speed = phys_velocity.Norm() * SPEED_MULTIPLIER + projectile.SetPhysVelocity(eye_fwr * speed) + } + else + { + local velocity = projectile.GetAbsVelocity() + local speed = velocity.Norm() * SPEED_MULTIPLIER + projectile.SetAbsVelocity(eye_fwr * speed) + projectile.SetForwardVector(eye_fwr) + } + + NetProps.SetPropEntity(projectile, "m_hOwnerEntity", player) + NetProps.SetPropEntity(projectile, "m_hThrower", player) + NetProps.SetPropInt(projectile, "m_iDeflected", 2) + projectile.SetTeam(player_team) + // 0 = red model skin + // 1 = blu model skin + projectile.SetSkin(player_team - 2) + + // Change trail color of projectiles that have them + for (local trail = projectile.FirstMoveChild(); trail != null; trail = trail.NextMovePeer()) + { + if (trail.GetClassname() != "env_spritetrail") + continue + + local trail_material = trail.GetModelName() + local color_to_replace = (player_team == TF_TEAM_RED) ? "blu" : "red" + local replacement = (player_team == TF_TEAM_RED) ? "red" : "blu" + local index = trail_material.find(color_to_replace) + if (index == null) + continue + + trail_material = trail_material.slice(0, index) + replacement + trail_material.slice(index + 3) + // This is the only base trail material that uses "blue" instead of "blu" + if (trail_material == "effects/repair_claw_trail_blu.vmt") + trail_material = "effects/repair_claw_trail_blue.vmt" + + PrecacheModel(trail_material) + trail.SetModel(trail_material) + } + + // The Crusader's Crossbow projectile uses a particle effect instead of env_spritetrail + if (projectile.GetClassname() == "tf_projectile_healing_bolt") + { + projectile.AcceptInput("DispatchEffect", "ParticleEffectStop", null, null) + local particle = SpawnEntityFromTable("info_particle_system", + { + origin = projectile.GetOrigin(), + effect_name = (player_team == TF_TEAM_RED) ? "healshot_trail_red" : "healshot_trail_blue", + start_active = 1, + }) + + EntFireByHandle(particle, "SetParent", "!activator", 0.0, projectile, null) + } +} diff --git a/scripts/vscripts/chaos/effects/random_gravity.nut b/scripts/vscripts/chaos/effects/random_gravity.nut new file mode 100644 index 00000000..86cd65fa --- /dev/null +++ b/scripts/vscripts/chaos/effects/random_gravity.nut @@ -0,0 +1,35 @@ +// by pokemonpasta + +function ChaosEffect_OnStart() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + // SetGravity(0.0) doesn't do anything, so we use a really small min value instead for effectively no gravity. + player.SetGravity(RandomFloat(0.000001, 3.0)) + } +} + +function ChaosEffect_OnEnd() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + player.SetGravity(1.0) + } +} + +function OnGameEvent_player_spawn(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player != null && player.GetGravity() == 1.0) // unlikely for a player OnStart to have gravity be set to 1 + { + player.SetGravity(RandomFloat(0.000001, 3.0)) + } +} diff --git a/scripts/vscripts/chaos/effects/randomizeskybox.nut b/scripts/vscripts/chaos/effects/randomize_skybox.nut similarity index 100% rename from scripts/vscripts/chaos/effects/randomizeskybox.nut rename to scripts/vscripts/chaos/effects/randomize_skybox.nut diff --git a/scripts/vscripts/chaos/effects/uncontrollablerecoil.nut b/scripts/vscripts/chaos/effects/recoil.nut similarity index 64% rename from scripts/vscripts/chaos/effects/uncontrollablerecoil.nut rename to scripts/vscripts/chaos/effects/recoil.nut index e43b2eba..9dce9916 100644 --- a/scripts/vscripts/chaos/effects/uncontrollablerecoil.nut +++ b/scripts/vscripts/chaos/effects/recoil.nut @@ -1,6 +1,3 @@ -const TF_WPN_TYPE_PRIMARY = 0 -const FT_STATE_IDLE = 0 - function ChaosEffect_OnStart() { for (local i = 1; i <= MaxClients(); i++) @@ -14,7 +11,7 @@ function ChaosEffect_OnStart() continue player.ValidateScriptScope() - player.GetScriptScope().prevLastFireTime <- NetProps.GetPropFloat(weapon, "LocalActiveTFWeaponData.m_flLastFireTime") + player.GetScriptScope().prev_last_fire_time <- NetProps.GetPropFloat(weapon, "LocalActiveTFWeaponData.m_flLastFireTime") } } @@ -30,11 +27,11 @@ function ChaosEffect_Update() if (weapon == null) continue - local lastFireTime = NetProps.GetPropFloat(weapon, "LocalActiveTFWeaponData.m_flLastFireTime") - if (lastFireTime > player.GetScriptScope().prevLastFireTime) + local last_fire_time = NetProps.GetPropFloat(weapon, "LocalActiveTFWeaponData.m_flLastFireTime") + if (last_fire_time > player.GetScriptScope().prev_last_fire_time) { player.ViewPunch(QAngle(-6, RandomInt(-4, 4), 0)) - player.GetScriptScope().prevLastFireTime <- lastFireTime + player.GetScriptScope().prev_last_fire_time <- last_fire_time } if (player.GetPlayerClass() == TF_CLASS_PYRO && weapon != null && weapon.GetSlot() == TF_WPN_TYPE_PRIMARY && NetProps.GetPropInt(weapon, "m_iWeaponState") != FT_STATE_IDLE) @@ -52,10 +49,15 @@ function OnGameEvent_player_spawn(params) if (player == null) return + if (params.team == TEAM_UNASSIGNED) + { + player.ValidateScriptScope() + return + } + local weapon = player.GetActiveWeapon() if (weapon == null) return - - player.ValidateScriptScope() - player.GetScriptScope().prevLastFireTime <- NetProps.GetPropFloat(weapon, "LocalActiveTFWeaponData.m_flLastFireTime") + + player.GetScriptScope().prev_last_fire_time <- NetProps.GetPropFloat(weapon, "LocalActiveTFWeaponData.m_flLastFireTime") } \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/removewearables.nut b/scripts/vscripts/chaos/effects/remove_wearables.nut similarity index 85% rename from scripts/vscripts/chaos/effects/removewearables.nut rename to scripts/vscripts/chaos/effects/remove_wearables.nut index 9d9fe947..ecd1bccf 100644 --- a/scripts/vscripts/chaos/effects/removewearables.nut +++ b/scripts/vscripts/chaos/effects/remove_wearables.nut @@ -1,8 +1,3 @@ -const TF_DEFINDEX_CHARGIN_TARGE = 131 -const TF_DEFINDEX_SPLENDID_SCREEN = 406 -const TF_DEFINDEX_TIDE_TURNER = 1099 -const TF_DEFINDEX_FESTIVE_CHARGIN_TARGE = 1144 - function ChaosEffect_OnStart() { for (local i = 1; i <= MaxClients(); i++) diff --git a/scripts/vscripts/chaos/effects/removecurrency.nut b/scripts/vscripts/chaos/effects/removecurrency.nut deleted file mode 100644 index 8f1b9916..00000000 --- a/scripts/vscripts/chaos/effects/removecurrency.nut +++ /dev/null @@ -1,14 +0,0 @@ -function ChaosEffect_OnStart() -{ - if (!GameModeUsesCurrency()) - return false - - for (local i = 1; i <= MaxClients(); i++) - { - local player = PlayerInstanceFromIndex(i) - if (player == null) - continue - - player.RemoveCurrency(player.GetCurrency()) - } -} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/respawnalldead.nut b/scripts/vscripts/chaos/effects/respawn_dead.nut similarity index 100% rename from scripts/vscripts/chaos/effects/respawnalldead.nut rename to scripts/vscripts/chaos/effects/respawn_dead.nut diff --git a/scripts/vscripts/chaos/effects/set_velocity.nut b/scripts/vscripts/chaos/effects/set_velocity.nut new file mode 100644 index 00000000..d641b79c --- /dev/null +++ b/scripts/vscripts/chaos/effects/set_velocity.nut @@ -0,0 +1,20 @@ +function ChaosEffect_OnStart() +{ + if (!("velocity" in Chaos_Data)) + return false + + local velocity = Chaos_Data.velocity + local abs = Chaos_Data.GetOrDefault("abs", false) + + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + player.SetAbsVelocity(abs ? player.GetAbsVelocity() + velocity : velocity) + } +} \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/shuffleclasses.nut b/scripts/vscripts/chaos/effects/shuffle_classes.nut similarity index 100% rename from scripts/vscripts/chaos/effects/shuffleclasses.nut rename to scripts/vscripts/chaos/effects/shuffle_classes.nut diff --git a/scripts/vscripts/chaos/effects/spawngargoyle.nut b/scripts/vscripts/chaos/effects/spawn_gargoyle.nut similarity index 92% rename from scripts/vscripts/chaos/effects/spawngargoyle.nut rename to scripts/vscripts/chaos/effects/spawn_gargoyle.nut index dabd08d6..303abe04 100644 --- a/scripts/vscripts/chaos/effects/spawngargoyle.nut +++ b/scripts/vscripts/chaos/effects/spawn_gargoyle.nut @@ -1,8 +1,8 @@ -const TF_GIFT_MODEL = "models/props_halloween/gargoyle_ghost.mdl" +local TF_GIFT_MODEL = "models/props_halloween/gargoyle_ghost.mdl" PrecacheModel(TF_GIFT_MODEL) PrecacheScriptSound("Halloween.PumpkinPickup") -const MAX_ATTEMPTS = 10 +local MAX_ATTEMPTS = 10 function ChaosEffect_OnStart() { diff --git a/scripts/vscripts/chaos/effects/spin_props.nut b/scripts/vscripts/chaos/effects/spin_props.nut new file mode 100644 index 00000000..c753138f --- /dev/null +++ b/scripts/vscripts/chaos/effects/spin_props.nut @@ -0,0 +1,63 @@ +// by pokemonpasta + +// config +local MIN_PROPS = 1 // int, minimum vphysics ents present for effect to load + +// code +local ThinkFuncs = {} + +function ChaosEffect_OnStart() +{ + for (local ent = Entities.First(); ent = Entities.Next(ent);) + { + if (ent.GetMoveType() != MOVETYPE_VPHYSICS) + continue + + StartSpinning(ent) + } + + if (ThinkFuncs.len() < MIN_PROPS) + return false +} + +function ChaosEffect_Update() +{ + for (local ent = Entities.First(); ent = Entities.Next(ent);) + { + // Start spinning any VPhysics entities we aren't tracking already + // this will usually be new entities that weren't there when we started + if (ent in ThinkFuncs || ent.GetMoveType() != MOVETYPE_VPHYSICS) + continue + + StartSpinning(ent) + } +} + +function ChaosEffect_OnEnd() +{ + for (local ent = Entities.First(); ent = Entities.Next(ent);) + { + if (!(ent in ThinkFuncs)) + continue + + local think_func = ThinkFuncs[ent] + AddThinkToEnt(ent, think_func ? think_func : null) // if there was no original think function, we set to null to clear it + } +} + +function SpinThink() +{ + local vel = self.GetPhysAngularVelocity() + vel.z = 1500.0 + self.SetPhysAngularVelocity(vel) +} + +function StartSpinning(ent) +{ + ent.ValidateScriptScope() + ThinkFuncs[ent] <- ent.GetScriptThinkFunc() + + ent.GetScriptScope().SpinThink <- SpinThink + + AddThinkToEnt(ent, "SpinThink") +} diff --git a/scripts/vscripts/chaos/effects/swappositions.nut b/scripts/vscripts/chaos/effects/swap_positions.nut similarity index 64% rename from scripts/vscripts/chaos/effects/swappositions.nut rename to scripts/vscripts/chaos/effects/swap_positions.nut index aef23ea3..88be79f1 100644 --- a/scripts/vscripts/chaos/effects/swappositions.nut +++ b/scripts/vscripts/chaos/effects/swap_positions.nut @@ -30,9 +30,10 @@ function ChaosEffect_OnStart() scope.teleport_origin <- other.GetOrigin() scope.teleport_angles <- other.GetAbsAngles() scope.teleport_velocity <- other.GetAbsVelocity() + scope.TeleportPlayer <- TeleportPlayer // Delay it, so that other players can get our old position - EntFireByHandle(player, "RunScriptCode", Chaos_EffectName + ".TeleportPlayer()", -1, player, null) + EntFireByHandle(player, "CallScriptFunction", "TeleportPlayer", -1, player, null) others.remove(j) break @@ -42,9 +43,9 @@ function ChaosEffect_OnStart() function TeleportPlayer() { - local scope = activator.GetScriptScope() - activator.SetAbsOrigin(scope.teleport_origin) - activator.SetAbsAngles(scope.teleport_angles) - activator.SetAbsVelocity(scope.teleport_velocity) - DispatchParticleEffect(activator.GetTeam() == TF_TEAM_RED ? "teleportedin_red" : "teleportedin_blue", activator.GetOrigin(), activator.GetAbsAngles() + Vector()) + local scope = self.GetScriptScope() + self.SetAbsOrigin(scope.teleport_origin) + self.SetAbsAngles(scope.teleport_angles) + self.SetAbsVelocity(scope.teleport_velocity) + DispatchParticleEffect(self.GetTeam() == TF_TEAM_RED ? "teleportedin_red" : "teleportedin_blue", self.GetOrigin(), self.GetAbsAngles() + Vector()) } \ No newline at end of file diff --git a/scripts/vscripts/chaos/effects/swapteams.nut b/scripts/vscripts/chaos/effects/swap_teams.nut similarity index 100% rename from scripts/vscripts/chaos/effects/swapteams.nut rename to scripts/vscripts/chaos/effects/swap_teams.nut diff --git a/scripts/vscripts/chaos/effects/teleporttozero.nut b/scripts/vscripts/chaos/effects/teleport_to_zero.nut similarity index 100% rename from scripts/vscripts/chaos/effects/teleporttozero.nut rename to scripts/vscripts/chaos/effects/teleport_to_zero.nut diff --git a/scripts/vscripts/chaos/effects/thirdperson.nut b/scripts/vscripts/chaos/effects/third_person.nut similarity index 100% rename from scripts/vscripts/chaos/effects/thirdperson.nut rename to scripts/vscripts/chaos/effects/third_person.nut diff --git a/scripts/vscripts/chaos/effects/thriller.nut b/scripts/vscripts/chaos/effects/thriller.nut index 798dc11e..7e358ac4 100644 --- a/scripts/vscripts/chaos/effects/thriller.nut +++ b/scripts/vscripts/chaos/effects/thriller.nut @@ -1,3 +1,15 @@ +function ChaosEffect_OnStart() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + EntFireByHandle(player, "SpeakResponseConcept", "TLK_MAGIC_DANCE", -1, null, null) + } +} + function ChaosEffect_Update() { for (local i = 1; i <= MaxClients(); i++) diff --git a/scripts/vscripts/chaos/effects/tiltedcamera.nut b/scripts/vscripts/chaos/effects/tilted_camera.nut similarity index 100% rename from scripts/vscripts/chaos/effects/tiltedcamera.nut rename to scripts/vscripts/chaos/effects/tilted_camera.nut diff --git a/scripts/vscripts/chaos/effects/unlimited_charge.nut b/scripts/vscripts/chaos/effects/unlimited_charge.nut new file mode 100644 index 00000000..240b6ee2 --- /dev/null +++ b/scripts/vscripts/chaos/effects/unlimited_charge.nut @@ -0,0 +1,62 @@ +function ApplyUnlimitedCharge(player) +{ + if (!NetProps.GetPropBool(player, "m_Shared.m_bShieldEquipped")) + return + + player.AddCustomAttribute("charge time increased", 99999.0, -1) + NetProps.SetPropFloat(player, "m_Shared.m_flChargeMeter", 100.0) +} + +function ChaosEffect_OnStart() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + ApplyUnlimitedCharge(player) + } +} + +function ChaosEffect_Update() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + if (!NetProps.GetPropBool(player, "m_Shared.m_bShieldEquipped")) + continue + + NetProps.SetPropFloat(player, "m_Shared.m_flChargeMeter", 100.0) + } +} + +function ChaosEffect_OnEnd() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + player.RemoveCustomAttribute("charge time increased") + } +} + +function OnGameEvent_player_spawn(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + EntFireByHandle(player, "RunScriptCode", Chaos_EffectName + ".ApplyUnlimitedCharge(self)", -1, null, null) +} diff --git a/scripts/vscripts/chaos/effects/vr_camera.nut b/scripts/vscripts/chaos/effects/vr_camera.nut new file mode 100644 index 00000000..dce01a80 --- /dev/null +++ b/scripts/vscripts/chaos/effects/vr_camera.nut @@ -0,0 +1,63 @@ +function ChaosEffect_OnStart() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + if (!player.IsAlive()) + continue + + SetupPlayer(player) + } +} + +function ChaosEffect_OnEnd() +{ + for (local i = 1; i <= MaxClients(); i++) + { + local player = PlayerInstanceFromIndex(i) + if (player == null) + continue + + player.SetForceLocalDraw(false) + ViewControl_Remove(player) + } +} + +function SetupPlayer(player) +{ + player.ValidateScriptScope() + player.SetForceLocalDraw(true) + + local viewcontrol = SpawnEntityFromTable("point_viewcontrol", { origin = player.EyePosition(), angles = player.EyeAngles() }) + EntFireByHandle(viewcontrol, "SetParent", "!activator", -1, player, viewcontrol) + EntFireByHandle(viewcontrol, "SetParentAttachment", player.LookupAttachment("eyes") != 0 ? "eyes" : "head", -1, null, null) + EntFireByHandle(viewcontrol, "Enable", "!activator", -1, player, viewcontrol) + EntFireByHandle(player, "RunScriptCode", "ViewControl_PostEnable(self)", -1, player, null) + + player.GetScriptScope().viewcontrol <- viewcontrol +} + +function OnGameEvent_player_spawn(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + ViewControl_Remove(player) + EntFireByHandle(player, "RunScriptCode", Chaos_EffectName + ".SetupPlayer(self)", -1, player, null) +} + +function OnGameEvent_player_death(params) +{ + local player = GetPlayerFromUserID(params.userid) + if (player == null) + return + + if (params.death_flags & TF_DEATHFLAG_DEADRINGER) + return + + ViewControl_Remove(player) +} diff --git a/scripts/vscripts/chaos/util.nut b/scripts/vscripts/chaos/util.nut index f5579d16..252e5162 100644 --- a/scripts/vscripts/chaos/util.nut +++ b/scripts/vscripts/chaos/util.nut @@ -1,43 +1,17 @@ -::worldspawn <- Entities.FindByClassname(null, "worldspawn") -::gamerules <- Entities.FindByClassname(null, "tf_gamerules") +worldspawn <- Entities.FindByClassname(null, "worldspawn") +gamerules <- Entities.FindByClassname(null, "tf_gamerules") -::GetEnemyTeam <- function(team) +function GetEnemyTeam(team) { - if (team == TF_TEAM_RED) - return TF_TEAM_BLUE - - if (team == TF_TEAM_BLUE) - return TF_TEAM_RED - - return team -} - -::NormalizeAngle <- function(target) -{ - target %= 360.0 - if (target > 180.0) - target -= 360.0 - else if (target < -180.0) - target += 360.0 - - return target -} - -::ApproachAngle <- function(target, value, speed) -{ - target = NormalizeAngle(target) - value = NormalizeAngle(value) - - local delta = NormalizeAngle(target - value) - if (delta > speed) - return value + speed - else if (delta < -speed) - return value - speed - - return value + switch (team) + { + case TF_TEAM_RED: return TF_TEAM_BLUE + case TF_TEAM_BLUE: return TF_TEAM_RED + default: return team + } } -::VectorAngles <- function(forward) +function VectorAngles(forward) { local yaw, pitch if (forward.y == 0.0 && forward.x == 0.0) @@ -61,7 +35,7 @@ return QAngle(pitch, yaw, 0.0) } -::ShuffleArray <- function(arr) +function ShuffleArray(arr) { local i = arr.len() while (i > 0) @@ -73,14 +47,14 @@ } } -::DebugDrawCross3D <- function(position, size, r, g, b, no_depth_test, duration) +function DebugDrawCross3D(position, size, r, g, b, no_depth_test, duration) { DebugDrawLine(position + Vector(size, 0, 0), position - Vector(size, 0, 0), r, g, b, no_depth_test, duration) DebugDrawLine(position + Vector(0, size, 0), position - Vector(0, size, 0), r, g, b, no_depth_test, duration) DebugDrawLine(position + Vector(0, 0, size), position - Vector(0, 0, size), r, g, b, no_depth_test, duration) } -::IsSpaceToSpawnHere <- function(where, hullmin, hullmax) +function IsSpaceToSpawnHere(where, hullmin, hullmax) { local trace = { @@ -100,7 +74,7 @@ return trace.fraction >= 1.0 } -::IsPlayerStuck <- function(player) +function IsPlayerStuck(player) { local trace = { @@ -111,11 +85,65 @@ mask = MASK_SOLID_BRUSHONLY, ignore = player } - + return TraceHull(trace) && trace.hit } -::ForcePlayerSuicide <- function(player) +function ForcePlayerSuicide(player) { player.TakeDamageCustom(player, player, null, Vector(), Vector(), 99999.0, DMG_CLUB | DMG_PREVENT_PHYSICS_FORCE, TF_DMG_CUSTOM_SUICIDE) -} \ No newline at end of file +} + +function LerpVector(a, b, t) +{ + return Vector( + a.x + (b.x - a.x) * t, + a.y + (b.y - a.y) * t, + a.z + (b.z - a.z) * t + ) +} + +function LerpAngle(a, b, t) +{ + local diff = b - a + while (diff > 180) diff -= 360 + while (diff < -180) diff += 360 + return a + diff * t +} + +function LerpAngles(a, b, t) +{ + return Vector( + LerpAngle(a.x, b.x, t), + LerpAngle(a.y, b.y, t), + LerpAngle(a.z, b.z, t) + ) +} + +function ViewControl_PostEnable(player) +{ + local weapon = player.GetActiveWeapon() + if (weapon != null) + weapon.SetDrawEnabled(true) + + NetProps.SetPropInt(player, "m_takedamage", DAMAGE_YES) +} + +function ViewControl_Remove(player) +{ + local scope = player.GetScriptScope() + if (scope == null || !("viewcontrol" in scope)) + return + + local viewcontrol = scope.viewcontrol + delete scope.viewcontrol + + if (viewcontrol == null || !viewcontrol.IsValid()) + return + + EntFireByHandle(player, "RunScriptCode", "self.GetScriptScope().lifeState <- NetProps.GetPropInt(self, `m_lifeState`)", -1, null, null) + EntFireByHandle(player, "RunScriptCode", "NetProps.SetPropInt(self, `m_lifeState`, 0)", -1, null, null) + EntFireByHandle(viewcontrol, "Disable", null, -1, player, player) + EntFireByHandle(player, "RunScriptCode", "NetProps.SetPropInt(self, `m_lifeState`, self.GetScriptScope().lifeState)", -1, null, null) + EntFireByHandle(viewcontrol, "Kill", null, -1, null, null) +} diff --git a/shaders/fxc/chaos_axismirror_ps20b.vcs b/shaders/fxc/chaos_axismirror_ps20b.vcs new file mode 100644 index 00000000..3a370639 Binary files /dev/null and b/shaders/fxc/chaos_axismirror_ps20b.vcs differ diff --git a/shaders/fxc/chaos_blur_ps20b.vcs b/shaders/fxc/chaos_blur_ps20b.vcs new file mode 100644 index 00000000..ac256700 Binary files /dev/null and b/shaders/fxc/chaos_blur_ps20b.vcs differ diff --git a/shaders/fxc/chaos_cocainum_ps20b.vcs b/shaders/fxc/chaos_cocainum_ps20b.vcs new file mode 100644 index 00000000..b3df822f Binary files /dev/null and b/shaders/fxc/chaos_cocainum_ps20b.vcs differ diff --git a/shaders/fxc/chaos_deepfried_ps20b.vcs b/shaders/fxc/chaos_deepfried_ps20b.vcs new file mode 100644 index 00000000..b6d26dbb Binary files /dev/null and b/shaders/fxc/chaos_deepfried_ps20b.vcs differ diff --git a/shaders/fxc/chaos_greyscale_ps20b.vcs b/shaders/fxc/chaos_greyscale_ps20b.vcs new file mode 100644 index 00000000..f849cbe7 Binary files /dev/null and b/shaders/fxc/chaos_greyscale_ps20b.vcs differ diff --git a/shaders/fxc/chaos_mirror_ps20b.vcs b/shaders/fxc/chaos_mirror_ps20b.vcs new file mode 100644 index 00000000..c3b90cfe Binary files /dev/null and b/shaders/fxc/chaos_mirror_ps20b.vcs differ diff --git a/shaders/fxc/chaos_scroll_ps20b.vcs b/shaders/fxc/chaos_scroll_ps20b.vcs new file mode 100644 index 00000000..9e49f961 Binary files /dev/null and b/shaders/fxc/chaos_scroll_ps20b.vcs differ diff --git a/shaders/fxc/chaos_tile_ps20b.vcs b/shaders/fxc/chaos_tile_ps20b.vcs new file mode 100644 index 00000000..af07346d Binary files /dev/null and b/shaders/fxc/chaos_tile_ps20b.vcs differ diff --git a/shadersrc/chaos_axismirror_ps2x.hlsl b/shadersrc/chaos_axismirror_ps2x.hlsl new file mode 100644 index 00000000..97059a20 --- /dev/null +++ b/shadersrc/chaos_axismirror_ps2x.hlsl @@ -0,0 +1,14 @@ +#include "common.hlsl" + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.uv; + float h = Constants0.x; + float2 mirrored = 0.5 - abs(uv - 0.5); + + float2 final_uv; + final_uv.x = lerp(uv.x, mirrored.x, h); + final_uv.y = lerp(uv.y, mirrored.y, 1.0 - h); + + return tex2D(TexBase, final_uv); +} \ No newline at end of file diff --git a/shadersrc/chaos_blur_ps2x.hlsl b/shadersrc/chaos_blur_ps2x.hlsl new file mode 100644 index 00000000..02a2cd58 --- /dev/null +++ b/shadersrc/chaos_blur_ps2x.hlsl @@ -0,0 +1,106 @@ +// by ficool2 + +// tried various approaches here +// * box filter looks horrible +// * gaussian needs more taps, ideally would use a horizontal/vertical pass but can't modify pipeline +// * poisson looked best for limited taps +// * added depth awareness to remove "halos" from the viewmodel + +#include "common.hlsl" + +#define blurStrength Constants0.x +#define depthThreshold Constants0.y + +float4 getAverageColor(float2 uv, float2 pixelSize) +{ + float4 color = float4(0, 0, 0, 0); + + for (int x = -1; x < 2; x++) + { + for (int y = -1; y < 2; y++) + { + float2 offset = float2(x, y) * pixelSize; + color += tex2D(TexBase, uv + offset); + } + } + + color /= 9.0; + + return color; +} + +float4 getGaussianBlur(float2 uv, float2 pixelSize) +{ + float4 c = float4(0,0,0,0); + + c += tex2D(TexBase, uv + pixelSize * float2(-1, -1)) * 1; + c += tex2D(TexBase, uv + pixelSize * float2( 0, -1)) * 2; + c += tex2D(TexBase, uv + pixelSize * float2( 1, -1)) * 1; + + c += tex2D(TexBase, uv + pixelSize * float2(-1, 0)) * 2; + c += tex2D(TexBase, uv) * 4; + c += tex2D(TexBase, uv + pixelSize * float2( 1, 0)) * 2; + + c += tex2D(TexBase, uv + pixelSize * float2(-1, 1)) * 1; + c += tex2D(TexBase, uv + pixelSize * float2( 0, 1)) * 2; + c += tex2D(TexBase, uv + pixelSize * float2( 1, 1)) * 1; + + return c * (1.0 / 16.0); +} + +static const float2 poisson[12] = +{ + float2(-0.326,-0.406), + float2(-0.840,-0.074), + float2(-0.696, 0.457), + float2(-0.203, 0.621), + float2( 0.962,-0.195), + float2( 0.473,-0.480), + float2( 0.519, 0.767), + float2( 0.185,-0.893), + float2( 0.507, 0.064), + float2( 0.896, 0.412), + float2(-0.322,-0.933), + float2(-0.792,-0.597) +}; + +float4 poissonBlur(float2 uv, float2 pixelSize) +{ + float4 c = tex2D(TexBase, uv) * 0.25; + + for (int i = 0; i < 12; i++) + c += tex2D(TexBase, uv + poisson[i] * pixelSize); + + return c / 12.25; +} + +float4 poissonBlurDepthAware(float2 uv, float2 pixelSize) +{ + float4 center = tex2D(TexBase, uv); + float centerDepth = center.a; + + float4 c = center; + float w = 1.0; + + for (int i = 0; i < 12; i++) + { + float2 offsetUV = uv + poisson[i] * pixelSize; + float4 s = tex2D(TexBase, offsetUV); + + float depthDiff = abs(s.a - centerDepth); + + float depthWeight = saturate(1.0 - depthDiff / depthThreshold); + + c += s * depthWeight; + w += depthWeight; + } + + return c / w; +} + +float4 main( PS_INPUT i ) : COLOR +{ + float2 pixelSize = TexBaseSize.xy * blurStrength; + float4 blurredColor = poissonBlurDepthAware(i.uv, pixelSize); + return blurredColor; +} diff --git a/shadersrc/chaos_cocainum_ps2x.hlsl b/shadersrc/chaos_cocainum_ps2x.hlsl new file mode 100644 index 00000000..425a7182 --- /dev/null +++ b/shadersrc/chaos_cocainum_ps2x.hlsl @@ -0,0 +1,87 @@ +// by ficool2 + +#include "common.hlsl" + +#define PI 3.14159265 + +#define Time Constants0.x +#define ColorIntensity Constants0.y +#define ColorDistortion Constants0.z +#define ColorSpeed Constants0.w +#define WaveFrequency Constants1.x +#define WaveAmplitude Constants1.y +#define RadialStrength Constants1.z +#define OscillationPeriod Constants1.w +#define Contrast Constants2.x +#define HueShift Constants2.y + +float3 rgb2hsv(float3 c) +{ + float4 K = float4(0.0, -1.0 / 6.0, 2.0 / 6.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +float3 hsv2rgb(float3 c) +{ + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y); +} + +float4 adjust_contrast(float4 color, float contrast) +{ + color.rgb = ((color.rgb - 0.5) * contrast + 0.5); + return color; +} + +float4 hue_shift(float4 color, float hueShift) +{ + float3 hsv = rgb2hsv(color.rgb); + hsv.x += hueShift; + if (hsv.x > 1.0) + hsv.x -= 1.0; + if (hsv.x < 0.0) + hsv.x += 1.0; + + color.rgb = hsv2rgb(hsv); + return color; +} + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.baseTexCoord; + + float oscillation = 0.4 + 0.6 * (0.5 + 0.5 * sin(Time * (2.0 * PI / OscillationPeriod))); + + // radial gradient + float edge_intensity = length(uv - float2(0.5, 0.5)) * RadialStrength; + edge_intensity = (saturate(edge_intensity) + 1.0) * oscillation; + + // wave + float2 offset; + offset.x = sin(i.baseTexCoord.y * WaveFrequency + Time) * WaveAmplitude * edge_intensity; + offset.y = cos(i.baseTexCoord.x * WaveFrequency + Time * 1.5) * WaveAmplitude * edge_intensity; + + uv += offset; + + // glitch + float glitch = sin(uv.y * 10.0 + Time * ColorSpeed) * ColorDistortion * 0.005 * edge_intensity; + float shift = sin(Time * ColorSpeed) * ColorIntensity * edge_intensity; + + uv.x += glitch; + + float4 result = tex2D(TexBase, uv); + result.r = tex2D(TexBase, uv + float2(shift, 0)).r; + result.g = tex2D(TexBase, uv + float2(-shift, shift)).g; + result.b = tex2D(TexBase, uv + float2(0, -shift)).b; + + // color shifts + result = adjust_contrast(result, Contrast); + result = hue_shift(result, HueShift * sin(Time * 0.5)); + + return result; +} diff --git a/shadersrc/chaos_deepfried_ps2x.hlsl b/shadersrc/chaos_deepfried_ps2x.hlsl new file mode 100644 index 00000000..159b402f --- /dev/null +++ b/shadersrc/chaos_deepfried_ps2x.hlsl @@ -0,0 +1,67 @@ +#include "common.hlsl" + +// Simple pseudo-random noise +float noise(float2 uv) +{ + return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453); +} + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.uv.xy; + + // Slight bulge distortion toward center + float2 centered = uv - 0.5; + float dist = length(centered); + float bulge = 1.0 + dist * 0.3; + uv = centered / bulge + 0.5; + + // Get configurable parameters + float brightness = Constants0.x; + float contrast = Constants0.y; + float saturation = Constants0.z; + float noiseAmount = Constants0.w; + float aberration = Constants1.x; + float3 tint = Constants1.yzw; + + // Sample with chromatic aberration + float r = tex2D(TexBase, uv + float2( aberration, 0)).r; + float g = tex2D(TexBase, uv).g; + float b = tex2D(TexBase, uv + float2(-aberration, 0)).b; + float3 color = float3(r, g, b); + + // Brightness boost + color = color * brightness + 0.1; + + // Extreme contrast + color = (color - 0.5) * contrast + 0.5; + + // Extreme saturation + float gray = dot(color, float3(0.299, 0.587, 0.114)); + color = lerp(float3(gray, gray, gray), color, saturation); + + // Apply color tint + color *= tint; + + // Add noise/grain + float n = noise(uv * 500.0) * noiseAmount; + color += n; + + // Harsh posterization (JPEG artifact simulation) + float levels = 8.0; + color = floor(color * levels + 0.5) / levels; + + // Sharpening + float sharp = 0.002; + float3 blur = tex2D(TexBase, uv + float2(sharp, 0)).rgb; + blur += tex2D(TexBase, uv + float2(-sharp, 0)).rgb; + blur += tex2D(TexBase, uv + float2(0, sharp)).rgb; + blur += tex2D(TexBase, uv + float2(0, -sharp)).rgb; + blur *= 0.25; + color += (color - blur) * 1.5; + + // Clamp to valid range + color = saturate(color); + + return float4(color, 1.0); +} diff --git a/shadersrc/chaos_greyscale_ps2x.hlsl b/shadersrc/chaos_greyscale_ps2x.hlsl new file mode 100644 index 00000000..9152a8f0 --- /dev/null +++ b/shadersrc/chaos_greyscale_ps2x.hlsl @@ -0,0 +1,11 @@ +#include "common.hlsl" + +float4 main( PS_INPUT i ) : COLOR +{ + float4 color = tex2D(TexBase, i.uv.xy); + + // Convert to grayscale using luminance weights + float gray = dot(color.rgb, float3(0.299, 0.587, 0.114)); + + return float4(gray, gray, gray, color.a); +} diff --git a/shadersrc/chaos_mirror_ps2x.hlsl b/shadersrc/chaos_mirror_ps2x.hlsl new file mode 100644 index 00000000..fceacbe9 --- /dev/null +++ b/shadersrc/chaos_mirror_ps2x.hlsl @@ -0,0 +1,8 @@ +#include "common.hlsl" + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.baseTexCoord; + uv.x = 1.0 - uv.x; + return tex2D(TexBase, uv); +} diff --git a/shadersrc/chaos_scroll_ps2x.hlsl b/shadersrc/chaos_scroll_ps2x.hlsl new file mode 100644 index 00000000..89848a7f --- /dev/null +++ b/shadersrc/chaos_scroll_ps2x.hlsl @@ -0,0 +1,18 @@ +#include "common.hlsl" + +#define Time Constants0.x +#define Vert Constants0.y +#define Speed Constants0.z + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.uv; + float scrollOffset = frac(Time * Speed); + + if (Vert == 1.0) + uv.y = frac(uv.y + scrollOffset); + else + uv.x = frac(uv.x + scrollOffset); + + return tex2D(TexBase, uv); +} diff --git a/shadersrc/chaos_tile_ps2x.hlsl b/shadersrc/chaos_tile_ps2x.hlsl new file mode 100644 index 00000000..73446b74 --- /dev/null +++ b/shadersrc/chaos_tile_ps2x.hlsl @@ -0,0 +1,10 @@ +#include "common.hlsl" + +#define Mult Constants0.x + +float4 main( PS_INPUT i ) : COLOR +{ + float2 uv = i.uv; + float2 tile = frac(uv * Mult); + return tex2D(TexBase, tile); +} diff --git a/shadersrc/common.hlsl b/shadersrc/common.hlsl new file mode 100644 index 00000000..ae876612 --- /dev/null +++ b/shadersrc/common.hlsl @@ -0,0 +1,60 @@ +// common data shared among all screenspace shaders + +// up to four textures available for sampling +sampler TexBase : register( s0 ); // $basetexture +sampler Tex1 : register( s1 ); // $texture1 +sampler Tex2 : register( s2 ); // $texture2 +sampler Tex3 : register( s3 ); // $texture3 + +// normalized dimensions for each texture above +// (x = 1.0 / width, y = 1.0 / height) +// NOT AVAILABLE IN L4D2/PORTAL 2 +float2 TexBaseSize : register( c4 ); +float2 Tex1Size : register( c5 ); +float2 Tex2Size : register( c6 ); +float2 Tex3Size : register( c7 ); + +// customizable parameters $c0, $c1, $c2, $c3, $c4 +const float4 Constants0 : register( c0 ); +const float4 Constants1 : register( c1 ); +const float4 Constants2 : register( c2 ); +const float4 Constants3 : register( c3 ); +// ONLY AVAILABLE IN L4D2/PORTAL2 ( +const float4 Constants4 : register( c4 ); + +// eye position in world coordinates +// WARNING: this is calculated from the view matrix +// on a screen overlay, this will return near-zero values +// use PlayerPosition VMT proxy as a workaround +const float4 EyePosition : register( c10 ); + +// xyz will be 0, 0, 0 (black) if mesh is rendered without fog +const float4 FogColor : register( c29 ); +// range of compressed depth buffer. usually 1.0 / 192 +#define DepthRange FogColor.w + +const float4 HDRParams : register( c30 ); +// exposure scale (bounded by tonemap controller's min/max) +#define TonemapScale HDRParams.x +// 16 in HDR, 4.59479 in LDR +#define LightmapScale HDRParams.y +// 16 in HDR, 1 in LDR +#define EnvmapScale HDRParams.z +// gamma, equivalent to pow(TonemapScale, 1.0 / 2.2) +#define GammaScale HDRParams.w + +// interpolated vertex data from vertex shader, do not change +struct PS_INPUT +{ + // texture coordinates + float2 uv : TEXCOORD0; + // always (0, 0) + float2 zeros : TEXCOORD1; + // unused + float2 texcoord2 : TEXCOORD2; + // vertex color (if mesh has one) + float4 color : TEXCOORD3; + // screenspace position + // SHADER MODEL 3 ONLY + float2 pos : VPOS; +}; \ No newline at end of file