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)!**
+
-
+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