]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Juhu/battle-royale Juhu/battle-royale
authorJuhu <5894800-Juhu_@users.noreply.gitlab.com>
Wed, 21 Jun 2023 23:55:28 +0000 (01:55 +0200)
committerJuhu <5894800-Juhu_@users.noreply.gitlab.com>
Wed, 21 Jun 2023 23:55:28 +0000 (01:55 +0200)
41 files changed:
1  2 
.gitlab-ci.yml
_hud_descriptions.cfg
balance-mario.cfg
balance-nexuiz25.cfg
balance-overkill.cfg
balance-samual.cfg
balance-xdf.cfg
balance-xonotic.cfg
balance-xpm.cfg
gamemodes-client.cfg
gamemodes-server.cfg
hud_luma.cfg
hud_luminos.cfg
hud_luminos_minimal.cfg
hud_luminos_minimal_xhair.cfg
hud_luminos_old.cfg
hud_nexuiz.cfg
notifications.cfg
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/hud/panel/radar.qc
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/common/ent_cs.qc
qcsrc/common/gamemodes/gamemode/_mod.inc
qcsrc/common/gamemodes/gamemode/_mod.qh
qcsrc/common/gamemodes/gamemode/br/sv_br.qc
qcsrc/common/mapinfo.qc
qcsrc/common/mapobjects/triggers.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/waypoints/all.inc
qcsrc/common/notifications/all.inc
qcsrc/common/notifications/all.qh
qcsrc/common/scores.qh
qcsrc/common/stats.qh
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/vehicle/bumblebee.qc
qcsrc/lib/csqcmodel/cl_player.qc
qcsrc/menu/xonotic/util.qc
qcsrc/server/chat.qc
qcsrc/server/client.qc
qcsrc/server/damage.qc
qcsrc/server/world.qc

diff --cc .gitlab-ci.yml
index 26bdd574e918531d4e605326bd952dc59eab30a5,5637179f1953643a4cbb29999eaa708d710038b1..57319b01eac555dfee20a3cabe3746b43122f25f
- workflow:\r
-   rules:\r
-     - if: $CI_COMMIT_MESSAGE =~ /Transifex autosync/\r
-       when: never\r
-     - when: always\r
\r
- before_script:\r
-   - ln -s $PWD data/xonotic-data.pk3dir\r
\r
-   - export MAKEFLAGS=-j$(nproc); echo MAKEFLAGS=$MAKEFLAGS\r
- #   FIXME: -march=native -mtune=native _changes the hash_, why?!?\r
- # - export CC="gcc -pipe -march=native -mtune=native"\r
-   - export CC="gcc -pipe"\r
\r
-   - git clone --depth=1 --branch=main https://gitlab.com/xonotic/gmqcc.git gmqcc\r
-   - make -C gmqcc || exit 1\r
-   - export QCC="$PWD/gmqcc/gmqcc"\r
\r
-   # Makefile: don't complain about lack of tags (fetching them is slow)\r
-   - export QCCFLAGS_WATERMARK=gitlab_pipeline\r
-   # Makefile: don't compress anything or complain about lack of zip program\r
-   - export ZIP=/bin/true\r
\r
- test_compilation_units:\r
-   rules:\r
-     - changes:\r
-       - qcsrc/**/*\r
-   stage: test\r
-   script:\r
-     - make test\r
\r
- test_sv_game:\r
-   stage: test\r
-   script:\r
-     - git clone --depth=1 --branch=div0-stable https://gitlab.com/xonotic/darkplaces.git darkplaces\r
-     - make -C darkplaces sv-release || exit 1\r
-     - export ENGINE="$PWD/darkplaces/darkplaces-dedicated -xonotic -noconfig -nohome"\r
-     - make qc || exit 1\r
\r
-     - mkdir -p data/maps\r
-     - wget -O data/maps/gitlab-ci.bsp https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/_init/_init.bsp\r
\r
-     - while read LINE; do\r
-         echo $LINE;\r
-         [ "$LINE" = "All tests OK" ] && PASS=1;\r
-       done < <(${ENGINE} +developer 1 +map gitlab-ci +sv_cmd runtest +wait +quit)\r
-     - test "$PASS" = "1" || { echo 'sv_cmd runtest failed!'; exit 1; }\r
\r
-     - ${ENGINE} +map gitlab-ci +sv_cmd dumpnotifs +wait +quit\r
-     - diff notifications.cfg data/data/notifications_dump.cfg ||\r
-         { echo 'Please update notifications.cfg using `dumpnotifs`!'; exit 1; }\r
\r
-     - wget -O data/stormkeep.pk3 http://beta.xonotic.org/autobuild-bsp/latest/stormkeep.pk3\r
-     - wget -O data/maps/stormkeep.mapinfo https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.mapinfo\r
-     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints\r
-     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache\r
\r
-     - EXPECT=79ebc686ac31153de17e6b8b268c3ac2\r
-     - HASH=$(${ENGINE} +timestamps 1 +exec serverbench.cfg\r
-       | tee /dev/stderr\r
-       | sed -e 's,^\[[^]]*\] ,,'\r
-       | grep '^:'\r
-       | grep -v '^:gamestart:'\r
-       | grep -v '^:anticheat:'\r
-       | md5sum | awk '{ print $1 }')\r
-     - echo 'expected:' $EXPECT\r
-     - echo '  actual:' $HASH\r
-     - test "$HASH" == "$EXPECT"\r
-     - exit $?\r
\r
\r
- # NOTE: The generated docs are incomplete - they don't contain code behind SVQC CSQC MENUQC GAMEQC ifdefs.\r
- # With them added to PREDEFINED, it would take over half an hour to generate the docs and even then\r
- # they might not be complete. Doxygen doesn't handle #elif and might not understand some QC definitions.\r
- #doxygen:  # rename to 'pages' when gitlab.com allows pages to exceed 100MiB\r
- #  before_script:\r
- #    - ln -s $PWD data/xonotic-data.pk3dir # is this needed?\r
- #    - apt-get update\r
- #    - apt-get -y install doxygen graphviz\r
- #  stage: deploy\r
- #  script:\r
- #    - cd qcsrc && doxygen\r
- #    - mv html ../public\r
- #    - mkdir -p ~/.ssh\r
- #    - for i in {0..0}; do eval $(printf "echo \$id_rsa_%02d\n" $i) >> ~/.ssh/id_rsa_base64; done\r
- #    - base64 --decode ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa\r
- #    - chmod 600 ~/.ssh/id_rsa\r
- #    - echo -e "Host *\n\tStrictHostKeyChecking no\n\tLogLevel ERROR\n" >> ~/.ssh/config\r
- #    - git config --global user.name "Gitlab CI"\r
- #    - git config --global user.email "<>"\r
- #    - git clone --single-branch --depth 1 ${DEPLOY_HOST}:${DEPLOY_REPO} ~/deploy_\r
- #    - mkdir ~/deploy && mv ~/deploy_/.git ~/deploy && rm -r ~/deploy_\r
- #    - cp -r ../public/* ~/deploy\r
- #    - cd ~/deploy && git add -A . && git commit -m "Deploy ${CI_BUILD_REF}" && git push origin gh-pages\r
- #  artifacts:\r
- #    paths:\r
- #      - public\r
- #  only:\r
- #    - master\r
+ workflow:
+   rules:
+     - if: $CI_COMMIT_MESSAGE =~ /Transifex autosync/
+       when: never
+     - when: always
+ before_script:
+   - ln -s $PWD data/xonotic-data.pk3dir
+   - export MAKEFLAGS=-j$(nproc); echo MAKEFLAGS=$MAKEFLAGS
+ #   FIXME: -march=native -mtune=native _changes the hash_, why?!?
+ # - export CC="gcc -pipe -march=native -mtune=native"
+   - export CC="gcc -pipe"
+   - >
+     if wget -nv https://beta.xonotic.org/pipeline-bin/gmqcc ; then
+       export QCC="$PWD/gmqcc"
+       chmod +x "$QCC"
+     else
+       git clone --depth=1 --branch=main https://gitlab.com/xonotic/gmqcc.git gmqcc
+       make -C gmqcc || exit 1
+       export QCC="$PWD/gmqcc/gmqcc"
+     fi
+   # Makefile: don't complain about lack of tags (fetching them is slow)
+   - export QCCFLAGS_WATERMARK=gitlab_pipeline
+   # Makefile: don't compress anything or complain about lack of zip program
+   - export ZIP=/bin/true
+ test_compilation_units:
+   rules:
+     - changes:
+       - qcsrc/**/*
+   stage: test
+   script:
+     - make test
+ test_sv_game:
+   stage: test
+   script:
+     - >
+       if wget -nv https://beta.xonotic.org/pipeline-bin/xonotic-linux64-dedicated ; then
+         export ENGINE="$PWD/xonotic-linux64-dedicated"
+         chmod +x "$ENGINE"
+       else
+         git clone --depth=1 --branch=div0-stable https://gitlab.com/xonotic/darkplaces.git darkplaces
+         make -C darkplaces sv-release || exit 1
+         export ENGINE="$PWD/darkplaces/darkplaces-dedicated -xonotic"
+       fi
+     - export ENGINE="$ENGINE -noconfig -nohome"
+     - make qc || exit 1
+     - mkdir -p data/maps
+     - wget -nv -O data/maps/_init.bsp https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/_init/_init.bsp
+     - while read LINE; do
+         echo $LINE;
+         [ "$LINE" = "All tests OK" ] && PASS=1;
+       done < <(${ENGINE} +developer 1 +map _init +sv_cmd runtest +wait +quit)
+     - test "$PASS" = "1" || { echo 'sv_cmd runtest failed!'; exit 1; }
+     - ${ENGINE} +map _init +sv_cmd dumpnotifs +wait +quit
+     - diff notifications.cfg data/data/notifications_dump.cfg ||
+         { echo 'Please update notifications.cfg using `dumpnotifs`!'; exit 1; }
+ #    - wget -nv -O data/stormkeep.pk3 http://beta.xonotic.org/autobuild-bsp/latest/stormkeep.pk3
+ # ^^ INCORRECT: /latest/stormkeep.pk3 is the most recently built, not necessarily the one built from master!
+ # we can't get the one from master directly as there's no /stable/stormkeep.pk3 or /master/stormkeep.pk3
+ # and we can't run misc/tools/xonotic-map-compiler-autobuild as it uses commit hashes from xonotic-maps.pk3dir to generate filenames
+ # but the autobuild server can run it and provide us the resulting pk3:
+     - wget -nv -O data/stormkeep.pk3 https://beta.xonotic.org/pipeline-bin/stormkeep.pk3
+ # see also: misc/infrastructure/xonotic-release-build.cron
+     - wget -nv -O data/maps/stormkeep.mapinfo https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.mapinfo
+     - wget -nv -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
+     - wget -nv -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
 -    - EXPECT=57325fe74835910e451ba42d31de8f34
++    - EXPECT=03c239e3257ebfd9d34bd11b8af9cbe0
+     - HASH=$(${ENGINE} +exec serverbench.cfg
+       | tee /dev/stderr
+       | grep '^:'
+       | grep -v '^:gamestart:'
+       | grep -v '^:anticheat:'
+       | md5sum | awk '{ print $1 }')
+     - echo 'expected:' $EXPECT
+     - echo '  actual:' $HASH
+     - test "$HASH" == "$EXPECT"
+     - exit $?
+ # NOTE: The generated docs are incomplete - they don't contain code behind SVQC CSQC MENUQC GAMEQC ifdefs.
+ # With them added to PREDEFINED, it would take over half an hour to generate the docs and even then
+ # they might not be complete. Doxygen doesn't handle #elif and might not understand some QC definitions.
+ #doxygen:  # rename to 'pages' when gitlab.com allows pages to exceed 100MiB
+ #  before_script:
+ #    - ln -s $PWD data/xonotic-data.pk3dir # is this needed?
+ #    - apt-get update
+ #    - apt-get -y install doxygen graphviz
+ #  stage: deploy
+ #  script:
+ #    - cd qcsrc && doxygen
+ #    - mv html ../public
+ #    - mkdir -p ~/.ssh
+ #    - for i in {0..0}; do eval $(printf "echo \$id_rsa_%02d\n" $i) >> ~/.ssh/id_rsa_base64; done
+ #    - base64 --decode ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
+ #    - chmod 600 ~/.ssh/id_rsa
+ #    - echo -e "Host *\n\tStrictHostKeyChecking no\n\tLogLevel ERROR\n" >> ~/.ssh/config
+ #    - git config --global user.name "Gitlab CI"
+ #    - git config --global user.email "<>"
+ #    - git clone --single-branch --depth 1 ${DEPLOY_HOST}:${DEPLOY_REPO} ~/deploy_
+ #    - mkdir ~/deploy && mv ~/deploy_/.git ~/deploy && rm -r ~/deploy_
+ #    - cp -r ../public/* ~/deploy
+ #    - cd ~/deploy && git add -A . && git commit -m "Deploy ${CI_BUILD_REF}" && git push origin gh-pages
+ #  artifacts:
+ #    paths:
+ #      - public
+ #  only:
+ #    - master
Simple merge
index 325db8e58f2027a5c1f3276dfc6fd22a0c2220cc,8b9f37ac10d7782c60f1248aef0e82feb4cd2c5f..3c1c0a9e43ac435ca26864b995705589b3f2bf3d
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 200
+ set g_mayhem_start_armor 200
+ set g_mayhem_start_ammo_shells 60
+ set g_mayhem_start_ammo_nails 320
+ set g_mayhem_start_ammo_rockets 160
+ set g_mayhem_start_ammo_cells 180
+ set g_mayhem_start_ammo_plasma 180
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 200
+ set g_tmayhem_start_armor 200
+ set g_tmayhem_start_ammo_shells 60
+ set g_tmayhem_start_ammo_nails 320
+ set g_tmayhem_start_ammo_rockets 160
+ set g_tmayhem_start_ammo_cells 180
+ set g_tmayhem_start_ammo_plasma 180
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 100
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
index d34e8e30996a331096030925a81b1596350ba9bb,0b39de222f3a3305f0836801995204cdcbfe45db..33a175b00349b9f0bef5fe5121a777ab0dc67d8d
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 5
  set g_lms_start_ammo_cells 50
  set g_lms_start_ammo_plasma 50
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 250
+ set g_mayhem_start_armor 100
+ set g_mayhem_start_ammo_shells 50
+ set g_mayhem_start_ammo_nails 150
+ set g_mayhem_start_ammo_rockets 50
+ set g_mayhem_start_ammo_cells 50
+ set g_mayhem_start_ammo_plasma 50
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 250
+ set g_tmayhem_start_armor 100
+ set g_tmayhem_start_ammo_shells 50
+ set g_tmayhem_start_ammo_nails 150
+ set g_tmayhem_start_ammo_rockets 50
+ set g_tmayhem_start_ammo_cells 50
+ set g_tmayhem_start_ammo_plasma 50
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 150
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 15
index 752a8982500d103bacf486394fb119fe5bc4f9f1,3944d7d3b18ab87d6cc280e38df1c23360bf912f..b8dfbe178208f83b978d7c1e3221d1b1e31d5ddd
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 200
+ set g_mayhem_start_armor 100
+ set g_mayhem_start_ammo_shells 60
+ set g_mayhem_start_ammo_nails 320
+ set g_mayhem_start_ammo_rockets 160
+ set g_mayhem_start_ammo_cells 180
+ set g_mayhem_start_ammo_plasma 180
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 200
+ set g_tmayhem_start_armor 100
+ set g_tmayhem_start_ammo_shells 60
+ set g_tmayhem_start_ammo_nails 320
+ set g_tmayhem_start_ammo_rockets 160
+ set g_tmayhem_start_ammo_cells 180
+ set g_tmayhem_start_ammo_plasma 180
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 100
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
index 44b110586d674d28dc7b1631c8537bea4a3f2ae3,5686c58d6e24fa2eb211af18644d7ecd536986b6..bf5d1ff0d95e751d6c2e5aa0f5863ae7aadabf96
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 200
+ set g_mayhem_start_armor 200
+ set g_mayhem_start_ammo_shells 60
+ set g_mayhem_start_ammo_nails 320
+ set g_mayhem_start_ammo_rockets 160
+ set g_mayhem_start_ammo_cells 180
+ set g_mayhem_start_ammo_plasma 180
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 200
+ set g_tmayhem_start_armor 200
+ set g_tmayhem_start_ammo_shells 60
+ set g_tmayhem_start_ammo_nails 320
+ set g_tmayhem_start_ammo_rockets 160
+ set g_tmayhem_start_ammo_cells 180
+ set g_tmayhem_start_ammo_plasma 180
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 100
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
diff --cc balance-xdf.cfg
index 895cca9b33f9c92b48c7a8acc51095c886ed195c,d8d4df60d53e2a6c916d61a7785bb07df3efd8b8..08584210953fe81d1263ea65133fe0ddb3cca8ec
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 200
+ set g_mayhem_start_armor 200
+ set g_mayhem_start_ammo_shells 60
+ set g_mayhem_start_ammo_nails 320
+ set g_mayhem_start_ammo_rockets 160
+ set g_mayhem_start_ammo_cells 180
+ set g_mayhem_start_ammo_plasma 180
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 200
+ set g_tmayhem_start_armor 200
+ set g_tmayhem_start_ammo_shells 60
+ set g_tmayhem_start_ammo_nails 320
+ set g_tmayhem_start_ammo_rockets 160
+ set g_tmayhem_start_ammo_cells 180
+ set g_tmayhem_start_ammo_plasma 180
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 100
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
index ba3bf0b5a743cff2fb6a80cca17762bdee29d615,2cf8c9eb4269b106877384494b4a6392c3200fc4..a7b631d61a693237945b70e1b41299854119eb1f
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 200
+ set g_mayhem_start_armor 200
+ set g_mayhem_start_ammo_shells 60
+ set g_mayhem_start_ammo_nails 320
+ set g_mayhem_start_ammo_rockets 160
+ set g_mayhem_start_ammo_cells 180
+ set g_mayhem_start_ammo_plasma 180
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 200
+ set g_tmayhem_start_armor 200
+ set g_tmayhem_start_ammo_shells 60
+ set g_tmayhem_start_ammo_nails 320
+ set g_tmayhem_start_ammo_rockets 160
+ set g_tmayhem_start_ammo_cells 180
+ set g_tmayhem_start_ammo_plasma 180
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 100
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
diff --cc balance-xpm.cfg
index ba72b43db857bb98d7c08753763dcfda3d84d0c2,87b781a555216639bb618652be91097abfe09e62..d56582ec5fc8616a9800f2b919d10dadea445ffa
@@@ -49,14 -49,22 +49,30 @@@ set g_lms_start_ammo_rockets 16
  set g_lms_start_ammo_cells 180
  set g_lms_start_ammo_plasma 180
  set g_lms_start_ammo_fuel 0
+ set g_mayhem_start_health 200
+ set g_mayhem_start_armor 200
+ set g_mayhem_start_ammo_shells 60
+ set g_mayhem_start_ammo_nails 320
+ set g_mayhem_start_ammo_rockets 160
+ set g_mayhem_start_ammo_cells 180
+ set g_mayhem_start_ammo_plasma 180
+ set g_mayhem_start_ammo_fuel 0
+ set g_tmayhem_start_health 200
+ set g_tmayhem_start_armor 200
+ set g_tmayhem_start_ammo_shells 60
+ set g_tmayhem_start_ammo_nails 320
+ set g_tmayhem_start_ammo_rockets 160
+ set g_tmayhem_start_ammo_cells 180
+ set g_tmayhem_start_ammo_plasma 180
+ set g_tmayhem_start_ammo_fuel 0
 +set g_br_start_health 100
 +set g_br_start_armor 0
 +set g_br_start_ammo_shells 0
 +set g_br_start_ammo_nails 0
 +set g_br_start_ammo_rockets 0
 +set g_br_start_ammo_cells 0
 +set g_br_start_ammo_plasma 0
 +set g_br_start_ammo_fuel 0
  set g_balance_nix_roundtime 25
  set g_balance_nix_incrtime 1.6
  set g_balance_nix_ammo_shells 60
index ad35029ec3f01292d801df317b635edc6419eb72,c63bba5a76efb86a6ca15ae8ba4405dd4598828d..252d97a9c1284ee3b2f85a7eff38e305b01d4254
@@@ -32,7 -32,10 +32,11 @@@ alias cl_hook_gamestart_k
  alias cl_hook_gamestart_ft
  alias cl_hook_gamestart_inv
  alias cl_hook_gamestart_duel
+ alias cl_hook_gamestart_mayhem
+ alias cl_hook_gamestart_tmayhem
+ alias cl_hook_gamestart_tka
+ alias cl_hook_gamestart_surv
 +alias cl_hook_gamestart_br
  alias cl_hook_gameend
  alias cl_hook_shutdown
  alias cl_hook_activeweapon
index eb4d0a869271364e72e344778b0bc193ea32c065,01d297e084a27827809150fc6aa2cfd705b9bc7b..4e5481fc20557cedbae35659e0d160b0e0181e40
@@@ -29,7 -29,10 +29,11 @@@ alias sv_hook_gamestart_k
  alias sv_hook_gamestart_ft
  alias sv_hook_gamestart_inv
  alias sv_hook_gamestart_duel
+ alias sv_hook_gamestart_mayhem
+ alias sv_hook_gamestart_tmayhem
+ alias sv_hook_gamestart_tka
+ alias sv_hook_gamestart_surv
 +alias sv_hook_gamestart_br
  // there is currently no hook for when the match is restarted
  // see sv_hook_readyrestart for previous uses of this hook
  //alias sv_hook_gamerestart
@@@ -59,7 -62,10 +63,11 @@@ alias sv_vote_gametype_hook_on
  alias sv_vote_gametype_hook_rc
  alias sv_vote_gametype_hook_tdm
  alias sv_vote_gametype_hook_duel
+ alias sv_vote_gametype_hook_mayhem
+ alias sv_vote_gametype_hook_tmayhem
+ alias sv_vote_gametype_hook_tka
+ alias sv_vote_gametype_hook_surv
 +alias sv_vote_gametype_hook_br
  
  // Example preset to allow 1v1ctf to be used for the gametype voting screen.
  // Aliases can have max 31 chars so the gametype can have max 9 chars.
@@@ -210,13 -216,34 +218,41 @@@ set g_duel_respawn_delay_large_count 
  set g_duel_respawn_delay_max 0
  set g_duel_respawn_waves 0
  set g_duel_weapon_stay 0
+ set g_mayhem_respawn_delay_small 0
+ set g_mayhem_respawn_delay_small_count 0
+ set g_mayhem_respawn_delay_large 0
+ set g_mayhem_respawn_delay_large_count 0
+ set g_mayhem_respawn_delay_max 0
+ set g_mayhem_respawn_waves 0
+ set g_mayhem_weapon_stay 0
+ set g_tmayhem_respawn_delay_small 0
+ set g_tmayhem_respawn_delay_small_count 0
+ set g_tmayhem_respawn_delay_large 0
+ set g_tmayhem_respawn_delay_large_count 0
+ set g_tmayhem_respawn_delay_max 0
+ set g_tmayhem_respawn_waves 0
+ set g_tmayhem_weapon_stay 0
+ set g_tka_respawn_delay_small 0
+ set g_tka_respawn_delay_small_count 0
+ set g_tka_respawn_delay_large 0
+ set g_tka_respawn_delay_large_count 0
+ set g_tka_respawn_delay_max 0
+ set g_tka_respawn_waves 0
+ set g_tka_weapon_stay 0
+ set g_surv_respawn_delay_small 0
+ set g_surv_respawn_delay_small_count 0
+ set g_surv_respawn_delay_large 0
+ set g_surv_respawn_delay_large_count 0
+ set g_surv_respawn_delay_max 0
+ set g_surv_respawn_waves 0
+ set g_surv_weapon_stay 0
 +set g_br_respawn_delay_small 0
 +set g_br_respawn_delay_small_count 0
 +set g_br_respawn_delay_large 0
 +set g_br_respawn_delay_large_count 0
 +set g_br_respawn_delay_max 0
 +set g_br_respawn_waves 0
 +set g_br_weapon_stay 0
  
  
  // =========
@@@ -570,46 -613,87 +622,131 @@@ set g_duel 0 "Duel: frag the opponent m
  set g_duel_with_powerups 0 "Enable powerups to spawn in the duel gamemode"
  set g_duel_not_dm_maps 0 "when this is set, DM maps will NOT be listed in duel"
  
- // ======
+ // ==============================
+ //  free for all and team mayhem
+ // ==============================
+ set g_mayhem 0 "Mayhem: Compete for the most damage dealt and kills in this chaotic mayhem!"
+ set g_tmayhem 0 "Team Mayhem: Compete with your team for the most damage dealt and kills in this chaotic mayhem!"
+ set g_mayhem_scoring_upscaler 20 "upscale one frag's worth to be this amount of in score"
+ set g_tmayhem_scoring_upscaler 20 "upscale one frag's worth to be this amount of in score"
+ set g_mayhem_scoring_kill_weight 0.25 "how much is a kill worth in frags"
+ set g_tmayhem_scoring_kill_weight 0.25 "how much is a kill worth in frags"
+ set g_mayhem_scoring_damage_weight 0.75 "how much is damage equal to player's spawning health+armor worth in frags"
+ set g_tmayhem_scoring_damage_weight 0.75 "how much is damage equal to player's spawning health+armor worth in frags"
+ set g_mayhem_scoring_disable_selfdamage2score 0 "disable reducing score with self damage at the cost of full penalty for suicides regardless of how much health was lost suiciding"
+ set g_tmayhem_scoring_disable_selfdamage2score 0 "disable reducing score with self damage at the cost of full penalty for suicides regardless of how much health was lost suiciding"
+ set g_mayhem_point_limit -1 "Mayhem score limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_mayhem_point_leadlimit -1 "Mayhem score lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_tmayhem_point_limit -1 "Team Mayhem score limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_tmayhem_point_leadlimit -1 "Team Mayhem score lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_mayhem_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
+ set g_tmayhem_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena"
+ set g_mayhem_powerups 1 "Allow powerups in mayhem. Only checked if g_powerups is -1 therefore this will be overridden by g_powerups 1 or 0"
+ set g_tmayhem_powerups 1 "Allow powerups in team mayhem. Only checked if g_powerups is -1 therefore this will be overridden by g_powerups 1 or 0"
+ set g_mayhem_pickup_items 0 "spawn pickup items in mayhem"
+ set g_tmayhem_pickup_items 0 "spawn pickup items in team mayhem"
+ set g_mayhem_pickup_items_remove_weapons_and_ammo 1 "when pickup items are enabled in mayhem still remove weapons and ammo pickups"
+ set g_tmayhem_pickup_items_remove_weapons_and_ammo 1 "when pickup items are enabled in team mayhem still remove weapons and ammo pickups"
+ set g_mayhem_selfdamage 0 "0 = disable selfdamage in mayhem, 1 = enable selfdamage in mayhem"
+ set g_tmayhem_selfdamage 0 "0 = disable selfdamage in tmayhem, 1 = enable selfdamage in tmayhem"
+ set g_mayhem_regenerate 0 "health and/or armor regeneration, according to g_balance_health_regen and g_balance_armor_regen"
+ set g_tmayhem_regenerate 0 "health and/or armor regeneration, according to g_balance_health_regen and g_balance_armor_regen"
+ set g_mayhem_rot 0 "health and/or armor rotting, according to g_balance_health_rot and g_balance_armor_rot"
+ set g_tmayhem_rot 0 "health and/or armor rotting, according to g_balance_health_rot and g_balance_armor_rot"
+ set g_tmayhem_teams 2 "how many teams are in team mayhem (set by mapinfo)"
+ set g_tmayhem_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ set g_tmayhem_teams_override 0 "how many teams are in team mayhem"
+ // ===============
+ //  team keepaway
+ // ===============
+ set g_tka 0 "another game mode which focuses around a ball"
+ set g_tka_on_ka_maps 1 "when this is set, all KA maps automatically support TKA"
+ set g_tka_on_tdm_maps 0 "when this is set, all TDM maps automatically support TKA"
+ set g_tka_teams 2 "how many teams are in team keepaway (set by mapinfo)"
+ set g_tka_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ set g_tka_teams_override 0    "how many teams are in team keepaway"
+ set g_tka_point_limit -1 "TKA point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_tka_point_leadlimit -1 "TKA point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_tka_score_team 1 "allow points to be awarded to teammates for any kill when the ball is in your team's possession"
+ set g_tka_score_bckill 1 "points for killing the ball barrier (Ball Carrier Kill)"
+ set g_tka_score_killac 1 "points for kills while holding the ball (Kill As Carrier)"
+ set g_tka_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score"
+ set g_tka_score_timepoints 0 "points to add to score per timeinterval, 0 for no points"
+ set g_tka_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+ set g_tka_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)"
+ set g_tka_ballcarrier_damage  1       "damage multiplier while holding the ball"
+ set g_tka_ballcarrier_force   1       "force multiplier while holding the ball"
+ set g_tka_ballcarrier_selfdamage      1       "self damage multiplier while holding the ball"
+ set g_tka_ballcarrier_selfforce       1       "self force multiplier while holding the ball"
+ set g_tka_noncarrier_warn     1       "warn players when they kill without holding the ball"
+ set g_tka_noncarrier_damage   1       "damage done to other players if both you and they don't have the ball"
+ set g_tka_noncarrier_force    1       "force done to other players if both you and they don't have the ball"
+ set g_tka_noncarrier_selfdamage       1       "self damage if you don't have the ball"
+ set g_tka_noncarrier_selfforce        1       "self force if you don't have the ball"
+ set g_tkaball_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+ set g_tkaball_trail_color     254     "particle trail color from player/ball"
+ set g_tkaball_damageforcescale        2 "Scale of force which is applied to the ball by weapons/explosions/etc"
+ set g_tkaball_respawntime     10      "if no one picks up the ball, how long to wait until the ball respawns"
+ // ==========
+ //  survival
+ // ==========
+ set g_survival 0 "Survival: identify and eliminate all the hunters before all your allies are gone"
+ set g_survival_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in survival"
+ set g_survival_hunter_count 0.25 "number of players who will become hunters, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players"
+ set g_survival_punish_teamkill 1 "kill the player when they kill an ally"
+ set g_survival_reward_survival 1 "give a point to all surviving players if the round timelimit is reached, in addition to the points given for kills"
+ set g_survival_warmup 10 "how long the players will have time to run around the map before the round starts"
+ set g_survival_round_timelimit 120 "round time limit in seconds"
++
++// ===============
 +//  battle royale
- // ======
++// ===============
 +set g_br 0 "Battle Royale: survive on a shrinking battlefield"
 +set g_br_not_assault_maps 0 "when this is set, CTF maps will NOT be listed in BR"
 +set g_br_not_ctf_maps 0 "when this is set, CTF maps will NOT be listed in BR"
 +set g_br_not_dm_maps 0 "when this is set, DM maps will NOT be listed in BR"
 +set g_br_minplayers 2 "minimum players to start the BR match"
 +set g_br_squad_size 3 "maximum squad size, use 0 or a negative number to determine squad size based on the amount of players up to the specified number or unlimited if 0"
 +set g_br_squad_colors 1 "assign each squad a random color scheme and force players to use it"
 +set g_br_squad_waypoint_distance 1500 "minimum distance required to show an ally waypoint"
 +set g_br_startweapons 0 "when disabled, players land from the dropship without weapons"
 +set g_br_bleed 0.02 "amount of health rot while injured"
 +set g_br_bleedlinear 1 "linear amount of health rot while injured"
 +set g_br_bleeding_health 0.5 "start health mutliplier when injured"
 +set g_br_bleeding_armor 50 "start armor when injured"
 +set g_br_revive_speed 0.4 "Speed for reviving an injured squadmate"
 +set g_br_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
 +set g_br_revive_extra_size 100 "Distance in qu that you can stand from an injured squadmate to keep reviving them"
 +set g_br_revive_health 0.25 "start health multiplier when revived"
 +set g_br_dropship_color "0.5 0 0.5" "dropship color"
 +set g_br_dropship_scale 3 "dropship scale"
 +set g_br_dropship_speed -1 "dropship speed, -1 to decide based on map size"
 +set g_br_drop_damage 0.5 "multiplier of damage taken while dropping"
 +set g_br_drop_speed_max 2.5 "max air speed multiplier while dropping"
 +set g_br_drop_speed_min 1.25 "min air speed multiplier while dropping"
 +set g_br_drop_speed_vertical_min 0.1 "minimum vertical speed portion while dropping"
 +set g_br_drop_accel_dive 50 "dive acceleration while dropping"
 +set g_br_drop_accel_turn 600 "turn acceleration while dropping"
 +set g_br_drop_distance_force 500 "minimum distance to the end of the dropship path before players are force dropped"
 +set g_br_ring_alpha 0.5 "ring transparency"
 +set g_br_ring_color "1 0 0" "ring color"
 +set g_br_ring_radius -1 "ring radius (use -1 to automatically determine the radius based on the map size)"
 +set g_br_ring_duration 150 "total time the ring spends closing"
 +set g_br_ring_timing "0.6 0.8 0.9" "timing values relative to g_br_ring_duration at which the ring waits and transitions into the next stage"
 +set g_br_ring_strength "2.5 5 10 20 50" "damage values for each stage including beginning and end"
 +set g_br_ring_wait 30 "wait time before each ring stage"
 +set g_br_ring_center_factor 0.25 "factor by which the ring can deviate from the center of the map, 0 means always completely centered, 1 means completely random within world bounds"
 +set g_br_ring_fadedistance 0.5 "value multiplied by the current ring radius defines the distance at which the ring slowly becomes visible until it reaches g_br_ring_alpha when standing right in front of it"
 +set g_br_ring_fadedistance_min 2000 "minimum value generated by g_br_ring_fadedistance"
 +set g_br_ring_exitvehicle 0 "players can't use vehicles outside of the ring"
 +set g_br_supply_interval 30 "seconds between each supply drop or 0 to disable"
 +set g_br_vehicle_interval 0 "seconds between each vehicle drop or 0 to disable"
diff --cc hud_luma.cfg
Simple merge
diff --cc hud_luminos.cfg
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc hud_nexuiz.cfg
Simple merge
index 8093752a4362d6d89c5ec19199c50ee8ffb62c3b,22a4e70a31403caeaef852e66b96d1c74ae9f011..b8b40896b226129b00d6de598d9123b2fdb66ed6
@@@ -93,15 -93,18 +93,21 @@@ seta notification_ANNCE_VOTE_ACCEPT "2
  seta notification_ANNCE_VOTE_CALL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
  seta notification_ANNCE_VOTE_FAIL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
  
- // MSG_INFO notifications (count = 339):
+ // MSG_INFO notifications:
 +seta notification_INFO_BR_DOWN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 +seta notification_INFO_BR_DOWN_SELF "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 +seta notification_INFO_BR_REVIVED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CA_JOIN_LATE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CA_LEAVE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_CHAT_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CHAT_NOSPECTATORS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_CHAT_PRIVATE_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_CHAT_SPECTATOR_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_CHAT_TEAM_DISABLED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_COINTOSS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CONNECTING "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_COUNTDOWN_RESTART "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+ seta notification_INFO_COUNTDOWN_STOP "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CTF_CAPTURE_BROKEN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CTF_CAPTURE_NEUTRAL "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
  seta notification_INFO_CTF_CAPTURE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
Simple merge
Simple merge
index 4880f77e40fc1b8cbf890743ce09fb8321f98605,35227ffab524c63bb077dfb624b4005f19ed4032..afcc258f89b1fe8bf045018ba29d9b0fe0261113
@@@ -398,24 -740,23 +741,25 @@@ void Cmd_Scoreboard_Help(
  // otherwise the previous exclusive rule warns anyway
  // e.g. -teams,rc,cts,lms/kills ?+rc/kills
  #define SCOREBOARD_DEFAULT_COLUMNS \
 -"ping pl fps name |" \
 +"ping pl fps +br/squad name |" \
- " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
- " -teams,lms,br/deaths +ft,tdm/deaths" \
+ " -teams,rc,cts,surv,inv,lms/kills +ft,tdm,tmayhem/kills ?+rc,inv/kills" \
 -" -teams,surv,lms/deaths +ft,tdm,tmayhem/deaths" \
++" -teams,surv,lms,br/deaths +ft,tdm,tmayhem/deaths" \
  " +tdm/sum" \
- " -teams,lms,rc,cts,inv,ka,br/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
- " -cts,dm,tdm,ka,ft,br/frags" /* tdm already has this in "score" */ \
- " +tdm,ft,dom,ons,as/teamkills"\
- " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
 -" -teams,lms,rc,cts,surv,inv,ka/suicides +ft,tdm,tmayhem/suicides ?+rc,inv/suicides" \
 -" -cts,dm,tdm,surv,ka,ft,mayhem,tmayhem/frags" /* tdm already has this in "score" */ \
++" -teams,lms,rc,cts,surv,inv,ka,br/suicides +ft,tdm,tmayhem/suicides ?+rc,inv/suicides" \
++" -cts,dm,tdm,surv,ka,ft,mayhem,tmayhem,br/frags" /* tdm already has this in "score" */ \
+ " +tdm,ft,dom,ons,as,tmayhem/teamkills"\
+ " -rc,cts,surv,nb/dmg -rc,cts,surv,nb/dmgtaken" \
+ " +surv/survivals +surv/hunts" \
  " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
  " +lms/lives +lms/rank" \
  " +kh/kckills +kh/losses +kh/caps" \
  " ?+rc/laps ?+rc/time +rc,cts/fastest" \
  " +as/objectives +nb/faults +nb/goals" \
- " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
+ " +ka,tka/pickups +ka,tka/bckills +ka,tka/bctime +ft/revivals" \
  " +dom/ticks +dom/takes" \
 -" -lms,rc,cts,inv,nb/score"
 +" -lms,rc,cts,inv,nb,br/score" \
 +" +br/revivals" \
 +" +br/rank"
  
  void Cmd_Scoreboard_SetFields(int argc)
  {
@@@ -636,9 -977,15 +980,15 @@@ string Scoreboard_GetName(entity pl
        {
                sbt_field_icon0 = "gfx/scoreboard/player_ready";
        }
 -      else if(!teamplay)
 +      else if(!teamplay && !(ISGAMETYPE(BR) && STAT(SQUADCOLORS)))
        {
-               int f = entcs_GetClientColors(pl.sv_entnum);
+               int f;
+               // NOTE: always adding 1024 allows saving the colormap 0 as a value != 0
+               if (playerslots[pl.sv_entnum].colormap >= 1024)
+                       f = playerslots[pl.sv_entnum].colormap - 1024; // override server-side player colors
+               else
+                       f = entcs_GetClientColors(pl.sv_entnum);
                {
                        sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
                        sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
@@@ -750,25 -1139,14 +1142,30 @@@ string Scoreboard_GetField(entity pl, P
                        return ftos(fps);
                }
  
+               case SP_ROUNDS_PL:
+                       return ftos(pl.(scores(field)));
                case SP_DMG: case SP_DMGTAKEN:
+                       if (rounds_played)
+                               return sprintf("%.2f k", pl.(scores(field)) / (1000 * rounds_played));
                        return sprintf("%.1f k", pl.(scores(field)) / 1000);
  
 +              case SP_BR_SQUAD:
 +                      tmp = pl.(scores(field));
 +                      if(tmp == 0)
 +                              return string_null;
 +
 +                      if(STAT(SQUADCOLORS))
 +                      {
 +                              int f = entcs_GetClientColors(pl.sv_entnum);
 +                              sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
 +                              sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
 +                              sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
 +                              sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
 +                              sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
 +                      }
 +                      return ftos(tmp);
 +
                default: case SP_SCORE:
                        tmp = pl.(scores(field));
                        f = scores_flags(field);
Simple merge
index de2f82e4c7edd2991070b3c4c87ce9fa7ec3e679,0000000000000000000000000000000000000000..0a0479beec6d8f868e0ab499f36186cf3d19a07e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1457 -1,0 +1,1457 @@@
-     reset_map(true, false);
 +// battle royale
 +// author: Juhu
 +
 +#include "sv_br.qh"
 +#include <server/elimination.qh>
 +#include <common/resources/sv_resources.qh>
 +#include <common/mutators/base.qh>
 +
 +#define BR_KILLS_INSTANTLY(pl, dt) \
 +    (!IN_SQUAD((pl)) || (br_SquadFindLastAlive((pl).br_squad, true) == (pl)) || ((dt) == DEATH_HURTTRIGGER.m_id) \
 +    || ((dt) == DEATH_KILL.m_id) || ((dt) == DEATH_TEAMCHANGE.m_id) || ((dt) == DEATH_AUTOTEAMCHANGE.m_id) \
 +    || DEATH_ISWEAPON(dt, WEP_VAPORIZER))
 +
 +float br_CalculatePlayerDropAngle(entity this);
 +bool br_PositionDropMember(entity this, entity leader, int position, float disconnect_range);
 +void br_LastPlayerForSquad_Notify(entity squad);
 +void br_RemovePlayer(entity player);
 +void br_Revive(entity player);
 +void br_Start();
 +bool br_CheckPlayers();
 +int br_WinningCondition();
 +
 +entity dropship;
 +
 +bool squads_colored = false;
 +
 +const float br_drop_time_secs = 1;
 +const float drop_speed_vertical_max = 0.9;
 +const float drop_distance_disconnect = 32;
 +const float drop_speed_crash = 0.9;
 +float br_event_supply_time;
 +float br_event_vehicle_time;
 +bool br_started = false;
 +.bool br_ring_warned;
 +.float br_drop_time;
 +.float br_force_drop_distance;
 +.int br_drop_launch;
 +.int br_drop_detached;
 +.float br_drop_position;
 +.bool br_drop_instructions;
 +.float br_ring_damage_time;
 +
 +.entity br_bleeding_inflictor;
 +.entity br_bleeding_attacker;
 +.int br_bleeding_deathtype;
 +..entity br_bleeding_weaponentity;
 +
 +// weapon set restoring for revive/drop
 +.WepSet br_wepset_old;
 +.Weapon br_weapon_prev[MAX_WEAPONSLOTS];
 +.float br_lastweapon_prev[MAX_WEAPONSLOTS];
 +
 +// alpha restoring for drop
 +.float br_alpha_old;
 +
 +float autocvar_g_br_revive_health = 0.25;
 +float autocvar_g_br_bleeding_health = 0.5;
 +float autocvar_g_br_bleeding_armor = 50;
 +float autocvar_g_br_drop_damage = 0.5;
 +float autocvar_g_br_drop_speed_max = 2.5;
 +float autocvar_g_br_drop_speed_min = 1.25;
 +float autocvar_g_br_drop_speed_vertical_min = 0.1;
 +bool autocvar_g_br_squad_colors = true;
 +float autocvar_g_br_drop_accel_dive = 50;
 +float autocvar_g_br_drop_accel_turn = 600;
 +bool autocvar_g_br_startweapons = false;
 +float autocvar_g_br_squad_waypoint_distance = 1500;
 +
 +MUTATOR_HOOKFUNCTION(br, reset_map_global)
 +{
 +    dropship_path_length = 0; // this should kill the dropship
 +    dropship_path_direction = '0 0 0';
 +
 +    if(ring)
 +        delete(ring);
 +    ring = dropship = NULL;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, reset_map_players)
 +{
 +    FOREACH_CLIENT(true, {
 +        GameRules_scoring_add(it, BR_RANK, -GameRules_scoring_add(it, BR_RANK, 0));
 +        GameRules_scoring_add(it, BR_SQUAD, -GameRules_scoring_add(it, BR_SQUAD, 0));
 +        GameRules_scoring_add(it, BR_REVIVALS, -GameRules_scoring_add(it, BR_REVIVALS, 0));
 +
 +        STAT(DROP, it) = DROP_LANDED;
 +        STAT(BLEEDING, it) = false;
 +
 +        br_RemovePlayer(it);
 +
 +        it.br_wepset_old = start_weapons;
 +        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +        {
 +            it.br_weapon_prev[slot] = WEP_Null;
 +            it.br_lastweapon_prev[slot] = 0;
 +        }
 +    });
 +
 +    br_SquadUpdateInfo();
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, CheckRules_World)
 +{
 +    if(!br_started && !warmup_stage && (time > game_starttime) && br_CheckPlayers())
 +        br_Start();
 +
 +    M_ARGV(0, float) = br_WinningCondition();
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, GiveFragsForKill, CBC_ORDER_FIRST)
 +{
 +    M_ARGV(2, float) = 0; // no frags counted in Battle Royale
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ForbidPlayerScore_Clear)
 +{
 +    // don't clear player score
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, GetPlayerStatus)
 +{
 +    entity player = M_ARGV(0, entity);
 +    return IN_SQUAD(player);
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ClientConnect)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    STAT(SQUADCOLORS, player) = squads_colored;
 +
 +    if(ring)
 +        ring_timelimit(ring);
 +
 +    br_SquadUpdateInfo();
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ClientDisconnect)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    br_RemovePlayer(player);
 +    br_SquadUpdateInfo();
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PutClientInServer)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    if (!br_started)
 +    {
 +        if(!warmup_stage && (time > game_starttime) && br_CheckPlayers())
 +            STAT(DROP, player) = DROP_TRANSPORT; // inhibits the spawn effect when the match is about to start
 +        return false;
 +    }
 +
 +    if (IN_SQUAD(player))
 +        Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_BR_JOIN_DEAD);
 +    else
 +        Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_BR_JOIN_LATE);
 +
 +    TRANSMUTE(Observer, player);
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, MakePlayerObserver)
 +{
 +    entity player = M_ARGV(0, entity);
 +    bool is_forced = M_ARGV(1, bool);
 +
 +    if(is_forced && IN_SQUAD(player))
 +    {
 +        br_SquadMember_Remove(player);
 +        br_SquadUpdateInfo();
 +    }
 +
 +    if(IN_SQUAD(player))
 +    {
 +        player.frags = FRAGS_PLAYER_OUT_OF_GAME;
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SpectateCopy)
 +{
 +    entity spectatee = M_ARGV(0, entity);
 +    entity client = M_ARGV(1, entity);
 +
 +    STAT(DROP, client) = STAT(DROP, spectatee);
 +    STAT(BLEEDING, client) = STAT(BLEEDING, spectatee);
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SpectateSet)
 +{
 +    entity client = M_ARGV(0, entity);
 +    entity target = M_ARGV(1, entity);
 +
 +    return (IN_SQUAD(client) && !client.br_squad.br_squad_dead && DIFF_SQUAD(client, target));
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SpectateNext)
 +{
 +    entity client = M_ARGV(0, entity);
 +
 +    if(!IS_REAL_CLIENT(client) || !IN_SQUAD(client) || client.br_squad.br_squad_dead)
 +        return false;
 +
 +    entity new_target;
 +
 +    if(client.enemy && client.enemy.br_squad_next)
 +        new_target = client.enemy.br_squad_next;
 +    else
 +        new_target = client.br_squad.br_squad_first;
 +
 +    while((new_target == client) || IS_DEAD(new_target) || !IS_PLAYER(new_target))
 +    {
 +        new_target = new_target.br_squad_next;
 +        if(!new_target)
 +            new_target = client.br_squad.br_squad_first;
 +    }
 +    M_ARGV(1, entity) = new_target;
 +
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SpectatePrev)
 +{
 +    entity client = M_ARGV(0, entity);
 +
 +    if(!IS_REAL_CLIENT(client) || !IN_SQUAD(client) || client.br_squad.br_squad_dead)
 +        return MUT_SPECPREV_CONTINUE;
 +
 +    entity new_target;
 +
 +    if(client.enemy && client.enemy.br_squad_prev)
 +        new_target = client.enemy.br_squad_prev;
 +    else
 +        new_target = client.br_squad.br_squad_last;
 +
 +    while((new_target == client) || IS_DEAD(new_target) || !IS_PLAYER(new_target))
 +    {
 +        new_target = new_target.br_squad_prev;
 +        if(!new_target)
 +            new_target = client.br_squad.br_squad_last;
 +    }
 +    M_ARGV(1, entity) = new_target;
 +
 +    return MUT_SPECPREV_FOUND;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ForbidSpawn)
 +{
 +    return br_started;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, WantWeapon)
 +{
 +    if(autocvar_g_br_startweapons)
 +        return false;
 +
 +    M_ARGV(1, float) = 0;
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SetStartItems)
 +{
 +    start_items &= ~(IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS);
 +
 +    start_health       = warmup_start_health       = cvar("g_br_start_health");
 +    start_armorvalue   = warmup_start_armorvalue   = cvar("g_br_start_armor");
 +    start_ammo_shells  = warmup_start_ammo_shells  = cvar("g_br_start_ammo_shells");
 +    start_ammo_nails   = warmup_start_ammo_nails   = cvar("g_br_start_ammo_nails");
 +    start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_br_start_ammo_rockets");
 +    start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_br_start_ammo_cells");
 +    start_ammo_plasma  = warmup_start_ammo_plasma  = cvar("g_br_start_ammo_plasma");
 +    start_ammo_fuel    = warmup_start_ammo_fuel    = cvar("g_br_start_ammo_fuel");
 +}
 +
 +// adjusted freezetag reviving code
 +#ifdef IN_REVIVING_RANGE
 +    #undef IN_REVIVING_RANGE
 +#endif
 +
 +#define IN_REVIVING_RANGE(player, it, revive_extra_size) \
 +    (it != player && IS_PLAYER(it) && !IS_DEAD(it) && SAME_SQUAD(it, player) \
 +    && boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax))
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerPreThink, CBC_ORDER_FIRST)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    if (game_stopped || !frametime || !IS_PLAYER(player))
 +        return true;
 +
 +    if(ring)
 +    {
 +        const float ring_damage_interval = 0.75;
 +        vector current_origin;
 +        if(!player.vehicle)
 +            current_origin = player.origin + player.view_ofs;
 +        else
 +            current_origin = player.vehicle.origin;
 +        if(vlen(current_origin - ring.origin) > ring_calculate_current_radius(ring))
 +        {
 +            if(!player.br_ring_warned)
 +            {
 +                player.br_ring_warned = true;
 +                player.br_ring_damage_time = time + ring_damage_interval;
 +                Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_BR_RING_WARN);
 +            }
 +
 +            // ring damage
 +            if (player.br_ring_damage_time < time)
 +            {
 +                if(player.vehicle) // if the player is controlling a vehicle
 +                {
 +                    if(autocvar_g_br_ring_exitvehicle)
 +                        vehicles_exit(player.vehicle, VHEF_RELEASE); // begone!
 +                    else
 +                        vehicles_damage(player.vehicle, ring, ring, 10 * ring.strength * ring_damage_interval, DEATH_RING.m_id, DMG_NOWEP, player.vehicle.origin, '0 0 0');
 +                }
 +                Damage(player, ring, ring, ring.strength * ring_damage_interval, DEATH_RING.m_id, DMG_NOWEP, player.origin, '0 0 0');
 +                player.br_ring_damage_time = time + ring_damage_interval;
 +            }
 +        }
 +        else
 +        {
 +            player.br_ring_warned = false;
 +        }
 +    }
 +
 +    if((STAT(DROP, player) == DROP_FALLING) && (player.br_drop_detached != 2) && IN_SQUAD(player) && (player != player.br_squad.br_squad_drop_leader)){
 +        // atck2 has to be released then pressed to detach
 +        if(!(STAT(PRESSED_KEYS, player) & KEY_ATCK2)){
 +            if(player.br_drop_detached == 0){
 +                player.br_drop_detached = 1;
 +            }
 +        }
 +        else{
 +            if(player.br_drop_detached == 1){
 +                player.br_drop_detached = 2;
 +                Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_BR_DROP);
 +            }
 +        }
 +    }
 +
 +    if(STAT(DROP, player) == DROP_TRANSPORT){
 +        if(time > (player.br_squad.br_drop_time + br_drop_time_secs))
 +        {
 +            if(!player.br_drop_instructions)
 +            {
 +                player.br_drop_instructions = true;
 +                Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_BR_DROPSHIP);
 +            }
 +
 +            // jump has to be released then pressed to launch
 +            if(!(STAT(PRESSED_KEYS, player) & KEY_JUMP)){
 +                if(player.br_drop_launch == 0){
 +                    player.br_drop_launch = 1;
 +                }
 +            }
 +            else{
 +                if(player.br_drop_launch == 1){
 +                    player.br_drop_launch = 2;
 +                }
 +            }
 +        }
 +
 +        if(!(IS_REAL_CLIENT(player) && (player.br_drop_launch == 2)) && (dropship_path_length > player.br_squad.br_force_drop_distance)){
 +            player.velocity = dropship_path_direction * dropship_speed;
 +        }
 +        else{
 +            if(!(IN_SQUAD(player) && player.br_squad.br_squad_drop_leader))
 +            {
 +                player.alpha = player.br_alpha_old;
 +                player.takedamage = DAMAGE_AIM;
 +                player.solid = SOLID_SLIDEBOX;
 +                if(!autocvar__notarget)
 +                    player.flags &= ~FL_NOTARGET;
 +                Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_BR_DROP);
 +                STAT(DROP, player) = DROP_FALLING;
 +                player.br_drop_detached = 0;
 +                float mindropspeed = PHYS_MAXAIRSPEED(player) * max(autocvar_g_br_drop_speed_min, 0); // no maxspeed_mod available here
 +                float maxdropspeed_ratio = drop_speed_vertical_max; // moving straight down is glitchy
 +                float mindropspeed_ratio = bound(0, autocvar_g_br_drop_speed_vertical_min, drop_speed_vertical_max);
 +                float pitch_view = max(player.v_angle.x, 0);
 +
 +                // pitch_view angle needs to be between 0 and 90 degrees
 +                if(pitch_view > 90)
 +                    pitch_view = 180 - pitch_view;
 +
 +                player.velocity.x = cos(player.angles.y * DEG2RAD);
 +                player.velocity.y = sin(player.angles.y * DEG2RAD);
 +                player.velocity.z = -tan(bound(asin(mindropspeed_ratio), pitch_view * DEG2RAD, asin(maxdropspeed_ratio)));
 +
 +                player.velocity = normalize(player.velocity) * mindropspeed;
 +
 +                player.angles.x = br_CalculatePlayerDropAngle(player) - 90;
 +                player.angles.y = vectoangles(vec2(player.velocity)).y + 180;
 +                player.angles.z = 180;
 +
 +                if(IN_SQUAD(player) && ((IS_REAL_CLIENT(player) && (player.br_drop_launch == 2)) || br_SquadIsBotsOnly(player.br_squad)))
 +                {
 +                    player.br_squad.br_squad_drop_leader = player;
 +
 +                    bool other_side = false;
 +                    int drop_position = 1;
 +
 +                    if(random() < 0.5)
 +                        drop_position *= -1;
 +
 +                    FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && (it != player) && SAME_SQUAD(it, player) && (STAT(DROP, it) == DROP_TRANSPORT), {
 +                        it.alpha = it.br_alpha_old;
 +                        it.takedamage = DAMAGE_AIM;
 +                        it.solid = SOLID_SLIDEBOX;
 +                        if(!autocvar__notarget)
 +                            it.flags &= ~FL_NOTARGET;
 +                        Kill_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CPID_BR_DROP);
 +                        STAT(DROP, it) = DROP_FALLING;
 +                        it.br_drop_detached = 0;
 +                        Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_BR_DROP_DETACH);
 +
 +                        it.br_drop_position = drop_position;
 +                        if(other_side)
 +                            drop_position += copysign(1, drop_position);
 +                        drop_position *= -1;
 +                        other_side = !other_side;
 +
 +                        br_PositionDropMember(it, player, it.br_drop_position, -1);
 +
 +                        it.velocity = player.velocity;
 +                        it.angles = player.angles;
 +                    });
 +                }
 +            }
 +        }
 +    }
 +
 +    // adjusted freezetag reviving code
 +    entity revivers_last = NULL;
 +    entity revivers_first = NULL;
 +
 +    bool player_is_reviving = false;
 +    bool player_is_being_revived = false;
 +    vector revive_extra_size = '1 1 1' * max(autocvar_g_br_revive_extra_size, 0);
 +    FOREACH_CLIENT(IS_PLAYER(it), {
 +        // check if player is reviving anyone
 +        if (STAT(BLEEDING, it))
 +        {
 +            if (STAT(BLEEDING, player))
 +                continue;
 +            if (!IN_REVIVING_RANGE(player, it, revive_extra_size))
 +                continue;
 +            player_is_reviving = true;
 +            break;
 +        }
 +
 +        if (!STAT(BLEEDING, player))
 +            continue; // both player and it are NOT bleeding
 +        if (!IN_REVIVING_RANGE(player, it, revive_extra_size))
 +            continue;
 +
 +        // found a squadmate that is reviving player
 +        if (revivers_last)
 +            revivers_last.chain = it;
 +        revivers_last = it;
 +        if (!revivers_first)
 +            revivers_first = it;
 +        player_is_being_revived = true;
 +    });
 +    if (revivers_last)
 +        revivers_last.chain = NULL;
 +
 +    if (!player_is_being_revived) // no squadmate nearby
 +    {
 +        float clearspeed = max(autocvar_g_br_revive_clearspeed, 0);
 +        if (STAT(BLEEDING, player))
 +            STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) - frametime * clearspeed, 1);
 +        else if (!player_is_reviving)
 +            STAT(REVIVE_PROGRESS, player) = 0; // reviving nobody
 +    }
 +    else // OK, there is at least one squadmate reviving us
 +    {
 +        float spd = max(autocvar_g_br_revive_speed, 0);
 +        STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * spd, 1);
 +
 +        if(STAT(REVIVE_PROGRESS, player) >= 1)
 +        {
 +            br_Revive(player);
 +
 +            // EVERY squad mate nearby gets a point (even if multiple!)
 +            for(entity it = revivers_first; it; it = it.chain)
 +            {
 +                GameRules_scoring_add(it, BR_REVIVALS, +1);
 +            }
 +
 +            Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_BR_REVIVED, revivers_first.netname);
 +            Send_Notification(NOTIF_ONE, revivers_first, MSG_CENTER, CENTER_BR_REVIVE, player.netname);
 +            Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_BR_REVIVED, player.netname, revivers_first.netname);
 +            if(autocvar_sv_eventlog)
 +            {
 +                string revivers = "";
 +                for(entity it = revivers_first; it; it = it.chain)
 +                    revivers = strcat(revivers, ftos(it.playerid), ",");
 +                revivers = substring(revivers, 0, strlen(revivers) - 1);
 +                GameLogEcho(strcat(":br:revival:", ftos(player.playerid), ":", revivers));
 +            }
 +        }
 +
 +        for(entity it = revivers_first; it; it = it.chain)
 +            STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
 +    }
 +
 +    if (STAT(BLEEDING, player))
 +    {
 +        entity player_wp = player.waypointsprite_attached;
 +        if (player_is_being_revived)
 +        {
 +            WaypointSprite_UpdateSprites(player_wp, WP_BRReviving, WP_Null, WP_Null);
 +            WaypointSprite_UpdateTeamRadar(player_wp, RADARICON_WAYPOINT, WP_BR_REVIVING_COLOR);
 +        }
 +        else
 +        {
 +            WaypointSprite_UpdateSprites(player_wp, WP_BRBleeding, WP_Null, WP_Null);
 +            WaypointSprite_UpdateTeamRadar(player_wp, RADARICON_WAYPOINT, WP_BR_BLEEDING_COLOR);
 +        }
 +
 +        WaypointSprite_UpdateMaxHealth(player_wp, 1);
 +        WaypointSprite_UpdateHealth(player_wp, STAT(REVIVE_PROGRESS, player));
 +    }
 +
 +    return true;
 +}
 +
 +#undef IN_REVIVING_RANGE
 +
 +MUTATOR_HOOKFUNCTION(br, PM_Physics)
 +{
 +    entity player = M_ARGV(0, entity);
 +    float maxspeed_mod = M_ARGV(1, float);
 +    float dt = M_ARGV(2, float); // tick rate
 +
 +    if(!IS_PLAYER(player))
 +        return false;
 +
 +    if(STAT(DROP, player) == DROP_TRANSPORT)
 +        return true;
 +
 +    // set the drop stat to landed on the next frame if it was set on landing
 +    if(STAT(DROP, player) == DROP_LANDING)
 +        STAT(DROP, player) = DROP_LANDED;
 +
 +    // TODO: improve dropping physics
 +    if(STAT(DROP, player) == DROP_FALLING){
 +        float maxairspeed = PHYS_MAXAIRSPEED(player) * max(maxspeed_mod, 1);
 +        float mindropspeed = maxairspeed * max(autocvar_g_br_drop_speed_min, 0);
 +        float dropspeed = vlen(vec2(player.velocity) + eZ * min(player.velocity.z, 0));
 +        if(player.velocity.z > 0)
 +            dropspeed -= vlen(player.velocity) - dropspeed;
 +        if(!IS_ONGROUND(player) && (player.waterlevel < WATERLEVEL_SWIMMING) && (dropspeed >= (mindropspeed * drop_speed_crash)) && ((tracebox(player.origin, player.mins, player.maxs, player.origin - '0 0 1', MOVE_NOMONSTERS, player), trace_fraction) >= 1)) // IS_ONGROUND doesn't work if jump is held (jump is theoretically blocked until landed)
 +        {
 +            ITEMS_STAT(player) |= IT_USING_JETPACK;
 +            bool has_drop_leader = IN_SQUAD(player) && (player.br_drop_detached != 2) && (player.br_squad.br_squad_drop_leader && (STAT(DROP, player.br_squad.br_squad_drop_leader) == DROP_FALLING));
 +            bool player_is_drop_leader = has_drop_leader && (player == player.br_squad.br_squad_drop_leader);
 +            if(player_is_drop_leader || !has_drop_leader)
 +            {
 +                float maxdropspeed = maxairspeed * max(autocvar_g_br_drop_speed_max, 0);
 +                float maxdropspeed_ratio = drop_speed_vertical_max; // moving straight down is glitchy
 +                float mindropspeed_ratio = bound(0, autocvar_g_br_drop_speed_vertical_min, drop_speed_vertical_max);
 +                float accel_dive = max(autocvar_g_br_drop_accel_dive, 0);
 +                float accel_turn = max(autocvar_g_br_drop_accel_turn, 0);
 +                float dropspeed_xy = vlen(vec2(player.velocity));
 +                float pitch_current = br_CalculatePlayerDropAngle(player);
 +                float pitch_view = max(player.v_angle.x, 0);
 +
 +                // pitch_view angle needs to be between 0 and 90 degrees
 +                if(pitch_view > 90)
 +                    pitch_view = 180 - pitch_view;
 +
 +                float pitch_diff = pitch_current - pitch_view;
 +                float pitch_ratio_wish = 0;
 +
 +                // calculate how much the player wants to change pitch (ratio is at least 0.1)
 +                // ratio is between -1 (looking straight down) and +1 (looking straight ahead or up)
 +                if((pitch_diff < 0) && (pitch_current < 90))
 +                    pitch_ratio_wish = bound(-1, sin(pitch_diff / (90 - pitch_current) * M_PI_2), -0.1);
 +                else if((pitch_diff > 0) && (pitch_current > 0))
 +                    pitch_ratio_wish = bound(0.1, sin(pitch_diff / pitch_current * M_PI_2), 1);
 +
 +                makevectors(player.v_angle);
 +                // horizontal wishvel as usual
 +                vector wishvel = v_forward * CS(player).movement.x + v_right * CS(player).movement.y;
 +
 +                // except make turning backwards easier by limiting the maximum turning angle to 90 degrees
 +                vector wish_angles = vectoangles(vec2(wishvel));
 +                vector vel_angles = vectoangles(vec2(player.velocity));
 +
 +                float diff_angle = wish_angles.y - vel_angles.y;
 +                if(diff_angle > 180)
 +                    diff_angle -= 360;
 +                if(diff_angle < -180)
 +                    diff_angle += 360;
 +
 +                wish_angles.y = (vel_angles.y + bound(-90, diff_angle, 90) + 360) % 360;
 +                makevectors(wish_angles);
 +
 +                wishvel = normalize(v_forward) * min(1, vlen(wishvel) / maxairspeed);
 +                // vertical wishvel using forward movement and the previously calculated ratio
 +                wishvel.z = pitch_ratio_wish * bound(0, CS(player).movement.x / maxairspeed, 1);
 +                // apply turn acceleration to wishvel
 +                wishvel *= accel_turn;
 +                player.velocity += wishvel * dt;
 +                player.velocity = normalize(eZ * player.velocity.z + normalize(vec2(player.velocity)) * dropspeed_xy);
 +
 +                // if there is no horizontal movement point the horizontal vector towards the view direction
 +                if(vlen(vec2(player.velocity)) == 0)
 +                    player.velocity += (eX * cos(player.angles.y * DEG2RAD) + eY * sin(player.angles.y * DEG2RAD)) * sqrt(1 - pow(maxdropspeed_ratio, 2));
 +
 +                // modify mindropspeed_ratio and maxdropspeed_ratio so that the player does not rotate beyond the view angle
 +                float pitch_ratio_view = sin(pitch_view * DEG2RAD);
 +                if(pitch_ratio_wish > 0)
 +                    mindropspeed_ratio = bound(mindropspeed_ratio, pitch_ratio_view, maxdropspeed_ratio);
 +                else if(pitch_ratio_wish < 0)
 +                    maxdropspeed_ratio = bound(mindropspeed_ratio, pitch_ratio_view, maxdropspeed_ratio);
 +
 +                // constrain to vertical min/maxdropspeed
 +                if(player.velocity.z > -mindropspeed_ratio)
 +                    player.velocity.z = -mindropspeed_ratio;
 +                if(player.velocity.z < -maxdropspeed_ratio)
 +                    player.velocity.z = -maxdropspeed_ratio;
 +
 +                // adjust horizontal speed so that vertical speed + horizontal speed = maxdropspeed
 +                float dropangle = br_CalculatePlayerDropAngle(player);
 +                const float accelangle = 20;
 +                dropspeed = bound(mindropspeed, dropspeed + accel_dive * (dropangle - accelangle) / accelangle * dt, maxdropspeed);
 +                player.velocity = normalize(player.velocity) * dropspeed;
 +
 +                player.angles.x = dropangle - 90;
 +                player.angles.y = vectoangles(vec2(player.velocity)).y + 180;
 +                player.angles.z = 180;
 +
 +                if(player_is_drop_leader)
 +                {
 +                    FOREACH_CLIENT(IS_PLAYER(it) && (it != player) && SAME_SQUAD(it, player) && (it.br_drop_detached != 2) && (STAT(DROP, it) == DROP_FALLING), {
 +                        if(!br_PositionDropMember(it, player, it.br_drop_position, drop_distance_disconnect))
 +                        {
 +                            it.br_drop_detached = 2;
 +                            Kill_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CPID_BR_DROP);
 +                            continue;
 +                        }
 +
 +                        it.velocity = player.velocity;
 +                        it.angles = player.angles;
 +                    });
 +                }
 +                else if((player.br_drop_detached != 2) && IN_SQUAD(player))
 +                {
 +                    player.br_drop_detached = 2;
 +                    Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_BR_DROP);
 +                }
 +            }
 +            else
 +            {
 +                if(!br_PositionDropMember(player, player.br_squad.br_squad_drop_leader, player.br_drop_position, drop_distance_disconnect))
 +                {
 +                    player.br_drop_detached = 2;
 +                    Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_BR_DROP);
 +                }
 +
 +                player.velocity = player.br_squad.br_squad_drop_leader.velocity;
 +                player.angles = player.br_squad.br_squad_drop_leader.angles; // no fixangles, only moves the player model not the player view
 +            }
 +
 +            return true;
 +        }
 +        else
 +        {
 +            if((player.br_drop_detached != 2) && IN_SQUAD(player) && (player != player.br_squad.br_squad_drop_leader))
 +            {
 +                player.br_drop_detached = 2;
 +                Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_BR_DROP);
 +            }
 +
 +            STAT(DROP, player) = DROP_LANDING;
 +            set_movetype(player, MOVETYPE_WALK);
 +            ITEMS_STAT(player) &= ~IT_USING_JETPACK;
 +            player.flags |= FL_PICKUPITEMS;
 +            player.dphitcontentsmask |= DPCONTENTS_BODY;
 +
 +            STAT(WEAPONS, player) = player.br_wepset_old;
 +
 +            .entity weaponentity = weaponentities[0];
 +            W_SwitchWeapon_Force(player, w_getbestweapon(player, weaponentity), weaponentity);
 +        }
 +    }
 +
 +    // injured players can't swim
 +    if(STAT(BLEEDING, player)){
 +        if(player.waterlevel >= WATERLEVEL_SWIMMING)
 +        {
 +            CS(player).movement.z = -60; // drift towards bottom
 +            player.v_angle.x = 0;
 +            player.com_in_jump = false;
 +        }
 +    }
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, Damage_Calculate)
 +{
 +    entity frag_target = M_ARGV(2, entity);
 +    float frag_deathtype = M_ARGV(3, float);
 +
 +    if(STAT(DROP, frag_target) != DROP_LANDED)
 +    {
 +        // weapon impact has no push force while dropping
 +        M_ARGV(6, vector) = '0 0 0';
 +
 +        if(STAT(DROP, frag_target) == DROP_TRANSPORT)
 +            M_ARGV(4, float) = M_ARGV(5, float) = 0; // can't take damage while on the dropship
 +        else if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)); // do not adjust vaporizer damage
 +        else
 +        {
 +            switch(frag_deathtype)
 +            {
 +                case DEATH_FALL.m_id:
 +                case DEATH_SHOOTING_STAR.m_id:
 +                    // do not take fall damage when landing from dropship
 +                    M_ARGV(4, float) = M_ARGV(5, float) = 0;
 +                    break;
 +                default:
 +                    // only take half of the usual damage
 +                    M_ARGV(4, float) *= max(autocvar_g_br_drop_damage, 0);
 +                    M_ARGV(5, float) *= max(autocvar_g_br_drop_damage, 0);
 +            }
 +        }
 +    }
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerDies, CBC_ORDER_FIRST)
 +{
 +    entity frag_attacker = M_ARGV(1, entity);
 +    entity frag_target = M_ARGV(2, entity);
 +    float frag_deathtype = M_ARGV(3, float); // float for some reason, breaks if changed to int
 +
 +    if(!IS_PLAYER(frag_target))
 +        return true;
 +
 +    if(STAT(DROP, frag_target) == DROP_TRANSPORT)
 +    {
 +        frag_target.alpha = frag_target.br_alpha_old;
 +        frag_target.takedamage = DAMAGE_AIM;
 +        frag_target.solid = SOLID_SLIDEBOX;
 +        if(!autocvar__notarget)
 +            frag_target.flags &= ~FL_NOTARGET;
 +        Kill_Notification(NOTIF_ONE_ONLY, frag_target, MSG_CENTER, CPID_BR_DROP);
 +    }
 +
 +    if(STAT(DROP, frag_target) == DROP_FALLING)
 +    {
 +        if((frag_target.br_drop_detached != 2) && IN_SQUAD(frag_target) && (frag_target != frag_target.br_squad.br_squad_drop_leader))
 +        {
 +            frag_target.br_drop_detached = 2;
 +            Kill_Notification(NOTIF_ONE_ONLY, frag_target, MSG_CENTER, CPID_BR_DROP);
 +        }
 +    }
 +
 +    if(STAT(DROP, frag_target) != DROP_LANDED)
 +    {
 +        set_movetype(frag_target, MOVETYPE_WALK);
 +        frag_target.dphitcontentsmask |= DPCONTENTS_BODY;
 +        STAT(WEAPONS, frag_target) = frag_target.br_wepset_old;
 +        STAT(DROP, frag_target) = DROP_LANDED;
 +    }
 +
 +    if(STAT(BLEEDING, frag_target) || BR_KILLS_INSTANTLY(frag_target, frag_deathtype))
 +    {
 +        if(STAT(BLEEDING, frag_target))
 +        {
 +            Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_BR_DOWN);
 +            STAT(BLEEDING, frag_target) = false;
 +
 +            // restore weapons on death to make weapon drop work
 +            STAT(WEAPONS, frag_target) = frag_target.br_wepset_old;
 +            for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +            {
 +                .entity weaponentity = weaponentities[slot];
 +                frag_target.(weaponentity).m_weapon = frag_target.br_weapon_prev[slot];
 +            }
 +        }
 +        WaypointSprite_Kill(frag_target.br_allywaypoint);
 +
 +        frag_target.respawn_flags = RESPAWN_SILENT | RESPAWN_FORCE;
 +        frag_target.respawn_time = time + 2;
 +        return true;
 +    }
 +
 +    frag_target.flags &= ~FL_PICKUPITEMS;
 +    RemoveGrapplingHooks(frag_target);
 +    StatusEffects_removeall(frag_target, STATUSEFFECT_REMOVE_NORMAL);
 +
 +    SetResource(frag_target, RES_HEALTH, start_health * max(autocvar_g_br_bleeding_health, 0));
 +    SetResource(frag_target, RES_ARMOR, max(autocvar_g_br_bleeding_armor, 0));
 +    Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_BR_DOWN_WAIT);
 +    STAT(BLEEDING, frag_target) = true;
 +
 +    FOREACH_CLIENT(IS_PLAYER(it),
 +    {
 +        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +        {
 +            .entity weaponentity = weaponentities[slot];
 +            if(it.(weaponentity).hook.aiment == frag_target)
 +                RemoveHook(it.(weaponentity).hook);
 +        }
 +    });
 +
 +    frag_target.br_wepset_old = STAT(WEAPONS, frag_target);
 +    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +    {
 +        .entity weaponentity = weaponentities[slot];
 +        frag_target.br_weapon_prev[slot] = frag_target.(weaponentity).m_switchweapon;
 +        frag_target.br_lastweapon_prev[slot] = frag_target.(weaponentity).cnt;
 +    }
 +    STAT(WEAPONS, frag_target) = '0 0 0';
 +
 +    WaypointSprite_Spawn(WP_BRBleeding, 0, 0, frag_target, '0 0 64', NULL, 0, frag_target, waypointsprite_attached, true, RADARICON_WAYPOINT);
 +
 +    if(frag_attacker == frag_target || !frag_attacker || ITEM_DAMAGE_NEEDKILL(frag_deathtype))
 +    {
 +        if(IS_PLAYER(frag_target))
 +            Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_BR_DOWN_SELF);
 +        Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_BR_DOWN_SELF, frag_target.netname);
 +    }
 +    else
 +    {
 +        if(IS_PLAYER(frag_target))
 +            Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_BR_DOWN, frag_attacker.netname);
 +        Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_BR_DOWN, frag_target.netname, frag_attacker.netname);
 +    }
 +
 +    br_SquadUpdateInfo();
 +
 +    return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerDied)
 +{
 +    entity player = M_ARGV(0, entity);
 +    if(br_started)
 +    {
 +        br_LastPlayerForSquad_Notify(player.br_squad);
 +        br_SquadUpdateInfo();
 +    }
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ClientObituary)
 +{
 +    entity frag_inflictor = M_ARGV(0, entity);
 +    entity frag_attacker = M_ARGV(1, entity);
 +    entity frag_target = M_ARGV(2, entity);
 +    float frag_deathtype = M_ARGV(3, float); // float for some reason, breaks if changed to int
 +    //entity frag_weaponentity = M_ARGV(4, entity);
 +
 +    if(!STAT(BLEEDING, frag_target) && !BR_KILLS_INSTANTLY(frag_target, frag_deathtype))
 +    {
 +        frag_target.br_bleeding_inflictor = frag_inflictor;
 +        frag_target.br_bleeding_attacker = frag_attacker;
 +        frag_target.br_bleeding_deathtype = frag_deathtype;
 +        //frag_target.br_bleeding_weaponentity = frag_weaponentity; // TODO: get entity field
 +        return true;
 +    }
 +
 +    if(STAT(BLEEDING, frag_target) && frag_target.br_bleeding_attacker)
 +    {
 +        entity new_inflictor = frag_target.br_bleeding_inflictor;
 +        entity new_attacker = frag_target.br_bleeding_attacker;
 +        int new_deathtype = frag_target.br_bleeding_deathtype;
 +        .entity new_weaponentity = frag_target.br_bleeding_weaponentity;
 +        frag_target.br_bleeding_attacker = frag_target.br_bleeding_inflictor = NULL;
 +
 +        Obituary(new_attacker, new_inflictor, frag_target, new_deathtype, new_weaponentity);
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SetResource)
 +{
 +    entity player = M_ARGV(7, entity);
 +    if(!IS_PLAYER(player))
 +        return false;
 +
 +    entity res_type = M_ARGV(8, entity);
 +    float amount = M_ARGV(9, float);
 +
 +    if(STAT(BLEEDING, player) && (res_type == RES_HEALTH || res_type == RES_ARMOR))
 +    {
 +        if(amount > GetResource(player, res_type)) // prevent the player from getting health or armor in any way
 +            return true;
 +    }
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, GetResourceLimit)
 +{
 +    entity player = M_ARGV(7, entity);
 +
 +    if(!IS_PLAYER(player) || !STAT(BLEEDING, player))
 +        return false;
 +
 +    entity res_type = M_ARGV(8, entity);
 +
 +    switch(res_type)
 +    {
 +        case RES_HEALTH:
 +            M_ARGV(9, float) *= max(autocvar_g_br_bleeding_health, 0);
 +            break;
 +        case RES_ARMOR:
 +            M_ARGV(9, float) = max(autocvar_g_br_bleeding_armor, 0);
 +    }
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerRegen, CBC_ORDER_FIRST)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    if(STAT(BLEEDING, player)){
 +        M_ARGV(7, float) = max(autocvar_g_br_bleed, 0);
 +        M_ARGV(8, float) = max(autocvar_g_br_bleedlinear, 0);
 +        M_ARGV(2, float) = M_ARGV(10, float) = 0;
 +    }
 +    else{
 +        M_ARGV(2, float) = M_ARGV(3, float) = 0; // no regeneration or rot in battle royale
 +    }
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerCanCrouch)
 +{
 +    entity player = M_ARGV(0, entity);
 +    if(STAT(BLEEDING, player))
 +        M_ARGV(1, bool) = true;
 +    else if(STAT(DROP, player) != DROP_LANDED)
 +        M_ARGV(1, bool) = false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerJump)
 +{
 +    entity player = M_ARGV(0, entity);
 +    return STAT(BLEEDING, player) || (STAT(DROP, player) != DROP_LANDED);
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, BotShouldAttack)
 +{
 +    entity bot = M_ARGV(0, entity);
 +    entity target = M_ARGV(1, entity);
 +
 +    return SAME_SQUAD(bot, target) || (STAT(DROP, bot) != DROP_LANDED) || (STAT(DROP, target) == DROP_TRANSPORT);
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, TurretValidateTarget)
 +{
 +    entity turret = M_ARGV(0, entity);
 +    entity target = M_ARGV(1, entity);
 +
 +    if(!br_started || SAME_SQUAD(turret, target) || (STAT(DROP, target) == DROP_TRANSPORT))
 +    {
 +        M_ARGV(3, float) = -1;
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, AccuracyTargetValid)
 +{
 +    entity attacker = M_ARGV(0, entity);
 +    entity target = M_ARGV(1, entity);
 +
 +    if(SAME_SQUAD(attacker, target) || (STAT(DROP, target) == DROP_TRANSPORT))
 +        return MUT_ACCADD_INDIFFERENT;
 +    return MUT_ACCADD_VALID;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, CustomizeWaypoint)
 +{
 +    entity wp = M_ARGV(0, entity);
 +    entity player = M_ARGV(1, entity);
 +
 +    if(wp.owner == NULL)
 +        return true;
 +
 +    if((wp == wp.owner.br_allywaypoint) && (vdist(wp.owner.origin - player.origin, <, autocvar_g_br_squad_waypoint_distance) || STAT(BLEEDING, wp.owner)))
 +        return true;
 +
 +    if(!IS_PLAYER(player) || (IN_SQUAD(wp.owner) && DIFF_SQUAD(wp.owner, player)))
 +        return true;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ClientKill)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    if(br_started)
 +    {
 +        // no forfeiting once the game started
 +        Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_BR_FORFEIT);
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, ClientCommand_Spectate)
 +{
 +    entity player = M_ARGV(0, entity);
 +
 +    if(br_started)
 +    {
 +        // no forfeiting once the game started
 +        Send_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CENTER_BR_FORFEIT);
 +        return MUT_SPECCMD_RETURN;
 +    }
 +    return MUT_SPECCMD_CONTINUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, PlayerSpawn)
 +{
 +    if(!br_started && !warmup_stage && (time > game_starttime) && br_CheckPlayers())
 +        br_Start();
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, SV_StartFrame)
 +{
 +    if(!br_started)
 +        return false;
 +
 +    if(autocvar_g_br_supply_interval > 0 && time - br_event_supply_time >= autocvar_g_br_supply_interval)
 +    {
 +        br_event_supply_time = time;
 +        spawn_supply();
 +    }
 +
 +    if(autocvar_g_br_vehicle_interval > 0 && time - br_event_vehicle_time >= autocvar_g_br_vehicle_interval)
 +    {
 +        br_event_vehicle_time = time;
 +        spawn_vehicle();
 +    }
 +
 +    if(ring)
 +    {
 +        float current_radius = ring_calculate_current_radius(ring);
 +
 +        IL_EACH(g_items, it.bot_pickup, {
 +            if(vdist(it.origin - ring.origin, >, current_radius))
 +                it.bot_pickup = false;
 +        });
 +    }
 +}
 +
 +void(entity this) havocbot_role_br_reviving;
 +void(entity this) havocbot_role_br_generic;
 +
 +bool squad_needs_revive(entity this)
 +{
 +    for(entity member = this.br_squad.br_squad_first; member; member = member.br_squad_next)
 +    {
 +        if(IS_DEAD(member) || !IS_PLAYER(member))
 +            continue;
 +
 +        if(STAT(BLEEDING, member))
 +            return true;
 +    }
 +
 +    return false;
 +}
 +
 +bool br_bot_ignore_in_ring(entity this)
 +{
 +    if(!ring)
 +        return false;
 +
 +    if(vlen(this.origin - ring.origin) > ring_calculate_current_radius(ring))
 +        return true;
 +
 +    return false;
 +}
 +
 +void havocbot_goalrating_br_findplayers(entity this, float ratingscale)
 +{
 +    if(!IN_SQUAD(this))
 +        return;
 +
 +    for(entity member = this.br_squad.br_squad_first; member; member = member.br_squad_next)
 +    {
 +        if(IS_DEAD(member) || !IS_PLAYER(member) || (member == this))
 +            continue;
 +
 +        // either wants to be revived by another player or wants to revive another player
 +        if(STAT(BLEEDING, member) != STAT(BLEEDING, this))
 +            navigation_routerating(this, member, ratingscale, 100000);
 +    }
 +}
 +
 +void havocbot_role_br_reviving(entity this)
 +{
 +    if(IS_DEAD(this))
 +        return;
 +
 +    if(!squad_needs_revive(this))
 +    {
 +        LOG_TRACE("changing role to generic");
 +        this.havocbot_role = havocbot_role_br_generic;
 +        navigation_goalrating_timeout_force(this);
 +        return;
 +    }
 +
 +    navigation_goalrating_start(this);
 +    havocbot_goalrating_br_findplayers(this, 20000);
 +    navigation_goalrating_end(this);
 +}
 +
 +void havocbot_role_br_generic(entity this)
 +{
 +    if(IS_DEAD(this))
 +        return;
 +
 +    if(squad_needs_revive(this))
 +    {
 +        LOG_TRACE("changing role to reviving");
 +        this.havocbot_role = havocbot_role_br_reviving;
 +        return;
 +    }
 +
 +    havocbot_role_generic(this);
 +}
 +
 +MUTATOR_HOOKFUNCTION(br, HavocBot_ChooseRole)
 +{
 +    entity bot = M_ARGV(0, entity);
 +
 +    if(!IS_DEAD(bot))
 +        bot.havocbot_role = havocbot_role_br_generic;
 +
 +    return true;
 +}
 +
 +float br_CalculatePlayerDropAngle(entity this)
 +{
 +    if(this.velocity.z < 0)
 +    {
 +        float dropspeed_xy = vlen(vec2(this.velocity));
 +        float dropspeed_z = fabs(this.velocity.z);
 +        return 90 - atan(dropspeed_xy / dropspeed_z) * RAD2DEG;
 +    }
 +
 +    return 0;
 +}
 +
 +bool br_PositionDropMember(entity this, entity leader, int position, float disconnect_range) {
 +    float pl_mins = min(leader.mins.x, leader.mins.y);
 +    float pl_maxs = max(leader.maxs.x, leader.maxs.y);
 +
 +    vector member_offset;
 +    member_offset.x = cos((leader.angles.y + 90) * DEG2RAD);
 +    member_offset.y = sin((leader.angles.y + 90) * DEG2RAD);
 +    member_offset.z = 0;
 +    member_offset *= pl_maxs - pl_mins + 32; // I hope individual players never get different mins/maxs
 +    member_offset *= position;
 +
 +    vector member_destination = leader.origin + member_offset;
 +
 +    tracebox(this.origin, this.mins, this.maxs, member_destination, MOVE_NORMAL, this);
 +
 +    if((trace_fraction < 1) && (disconnect_range >= 0))
 +    {
 +        if(vlen(member_destination - trace_endpos) > disconnect_range)
 +            return false;
 +    }
 +
 +    setorigin(this, trace_endpos);
 +    return true;
 +}
 +
 +void br_LastPlayerForSquad_Notify(entity squad)
 +{
 +    entity player = br_SquadFindLastAlive(squad, false);
 +    if(player)
 +        Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ALONE);
 +}
 +
 +void br_RemovePlayer(entity player)
 +{
 +    br_SquadMember_Remove(player);
 +
 +    FOREACH_CLIENT((it.br_bleeding_attacker == player) || (it.br_bleeding_inflictor == player), {
 +        it.br_bleeding_attacker = it.br_bleeding_inflictor = NULL;
 +    });
 +}
 +
 +void br_Revive(entity player)
 +{
 +    if(STAT(BLEEDING, player))
 +    {
 +        Kill_Notification(NOTIF_ONE, player, MSG_CENTER, CPID_BR_DOWN);
 +        STAT(BLEEDING, player) = false;
 +    }
 +    player.flags |= FL_PICKUPITEMS;
 +    SetResource(player, RES_HEALTH, start_health * max(autocvar_g_br_revive_health, 0));
 +    SetResource(player, RES_ARMOR, 0);
 +
 +    STAT(WEAPONS, player) = player.br_wepset_old;
 +    for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +    {
 +        .entity weaponentity = weaponentities[slot];
 +        W_SwitchWeapon_Force(player, player.br_weapon_prev[slot], weaponentity);
 +        player.(weaponentity).cnt = player.br_lastweapon_prev[slot];
 +    }
 +
 +    player.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
 +
 +    STAT(REVIVE_PROGRESS, player) = 0;
 +    player.revival_time = time;
 +
 +    WaypointSprite_Kill(player.waypointsprite_attached);
 +
 +    br_SquadUpdateInfo();
 +}
 +
 +int br_WinningCondition()
 +{
 +    int total_squads = br_SquadUpdateInfo();
 +
 +    if ((total_squads > 1) || !br_started)
 +        return WINNING_NEVER;
 +
 +    entity winner_squad = NULL;
 +    IL_EACH(squads, !it.br_squad_dead, { winner_squad = it; break; });
 +
 +    for(entity member = winner_squad.br_squad_first; member; member = member.br_squad_next)
 +    {
 +        GameRules_scoring_add(member, BR_RANK, 1);
 +        member.winning = true;
 +    }
 +
 +    delete(round_handler);
 +    round_handler = NULL;
 +
 +    return WINNING_YES;
 +}
 +
 +bool br_isEliminated(entity e)
 +{
 +    return (IN_SQUAD(e) && (IS_DEAD(e) || !IS_PLAYER(e)));
 +}
 +
 +bool br_CheckPlayers()
 +{
 +    total_players = 0;
 +    FOREACH_CLIENT(IS_PLAYER(it), ++total_players);
 +
 +    static int prev_players = 0;
 +    if (total_players >= autocvar_g_br_minplayers || total_players == 0)
 +    {
 +        if(prev_players > 0)
 +            Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS);
 +        prev_players = 0;
 +        return (total_players >= autocvar_g_br_minplayers);
 +    }
 +
 +    if(prev_players != total_players)
 +    {
 +        Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_PLAYERS, autocvar_g_br_minplayers - total_players);
 +        prev_players = total_players;
 +    }
 +
 +    return false;
 +}
 +
 +void br_Start(){
 +    // battle royale does not need those, besides, the timelimit won't be visible anymore after the game started
 +    cvar_set("timelimit", "0");
 +    cvar_set("fraglimit", "0");
 +    cvar_set("leadlimit", "0");
 +
++    reset_map(false);
 +
 +    ring = ring_initialize();
 +    dropship = dropship_initialize();
 +
 +    if(!ring || !dropship)
 +    {
 +        if(!ring)
 +            LOG_WARN("Failed to determine ring starting point");
 +        if(!dropship)
 +            LOG_WARN("Failed to determine dropship route");
 +
 +        LOG_WARN("Prerequisites not met. Cannot start battle royale, aborting...");
 +
 +        if(ring)
 +        {
 +            delete(ring);
 +            ring = NULL;
 +        }
 +
 +        if(dropship)
 +        {
 +            delete(dropship);
 +            dropship = NULL;
 +        }
 +
 +        NextLevel();
 +        return;
 +    }
 +
 +    br_started = true;
 +    int num_players = 0;
 +
 +    FOREACH_CLIENT(IS_PLAYER(it), {
 +        STAT(DROP, it) = DROP_TRANSPORT;
 +        PutPlayerInServer(it);
 +
 +        it.br_wepset_old = STAT(WEAPONS, it);
 +        STAT(WEAPONS, it) = '0 0 0';
 +        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
 +        {
 +            it.br_weapon_prev[slot] = WEP_Null;
 +            it.br_lastweapon_prev[slot] = 0;
 +        }
 +
 +        ++num_players;
 +    });
 +
 +    if(autocvar_g_br_squad_size >= 1)
 +        max_squad_size = min(autocvar_g_br_squad_size, ceil(num_players / 2));
 +    else if(autocvar_g_br_squad_size <= -1)
 +        max_squad_size = min(-autocvar_g_br_squad_size, floor(num_players / 2));
 +    else
 +        max_squad_size = floor(num_players / 2);
 +
 +    for(int num_squads = 0; (num_squads * max_squad_size) < num_players; ++num_squads)
 +    {
 +        entity new_squad = new_pure(squad);
 +        new_squad.br_squad_drop_leader = NULL;
 +        new_squad.br_squad_id = num_squads + 1;
 +
 +        IL_PUSH(squads, new_squad);
 +    }
 +
 +    FOREACH_CLIENT_RANDOM(IS_PLAYER(it), {
 +        entity current_squad = br_SquadGetRandomAvail();
 +        br_SquadMember_Add(current_squad, it);
 +        GameRules_scoring_add(it, BR_SQUAD, current_squad.br_squad_id);
 +
 +        setorigin(it, dropship.origin + eZ * (dropship.mins.z - it.maxs.z - 64));
 +        it.angles = vectoangles(dropship_path_direction) + '45 0 0';
 +        it.fixangle = true;
 +        it.velocity = '0 0 0';
 +        set_movetype(it, MOVETYPE_FLY);
 +        it.flags &= ~FL_PICKUPITEMS;
 +        it.flags |= FL_NOTARGET;
 +        it.dphitcontentsmask &= ~DPCONTENTS_BODY;
 +        it.br_alpha_old = it.alpha;
 +        it.alpha = -1;
 +        it.takedamage = DAMAGE_NO;
 +        it.solid = SOLID_NOT;
 +        it.br_drop_instructions = false;
 +        it.br_drop_launch = 0;
 +        UNSET_ONGROUND(it); // otherwise this isn't unset if the player drops in the same frame
 +
 +        WaypointSprite_Spawn(WP_BRAlly, 0, 0, it, '0 0 64', NULL, 0, it, br_allywaypoint, true, RADARICON_WAYPOINT);
 +    });
 +
 +    squads_colored = autocvar_g_br_squad_colors;
 +
 +    FOREACH_CLIENT(IS_REAL_CLIENT(it),
 +    {
 +        br_SendSquad(it);
 +        STAT(SQUADCOLORS, it) = squads_colored;
 +    });
 +
 +    int squad_colors_taken = 0;
 +    const int squad_colors_num = 6; // there are 6 colors which look distinct enough
 +    const int squad_colors_taken_mask = 2 ** squad_colors_num - 1;
 +    IL_EACH(squads, true,
 +    {
 +        if(squads_colored)
 +        {
 +            float squad_color = 0;
 +
 +            while(true)
 +            {
 +                squad_color = floor(random() * squad_colors_num);
 +                int squad_color_bit = 1 << squad_color;
 +
 +                if(!(squad_color_bit & squad_colors_taken))
 +                {
 +                    squad_colors_taken |= squad_color_bit;
 +
 +                    // only select easily distinguishable colors
 +                    switch(squad_color)
 +                    {
 +                        case 0:
 +                            squad_color = 0;
 +                            break;
 +                        case 1:
 +                            squad_color = 1;
 +                            break;
 +                        case 2:
 +                            squad_color = 3;
 +                            break;
 +                        case 3:
 +                            squad_color = 5;
 +                            break;
 +                        case 4:
 +                            squad_color = 9;
 +                            break;
 +                        case 5:
 +                            squad_color = 12;
 +                    }
 +                    break;
 +                }
 +            }
 +
 +            if(squad_colors_taken == squad_colors_taken_mask)
 +                squad_colors_taken = 0;
 +
 +            squad_color = 16 * squad_color + squad_color;
 +
 +            for(entity member = it.br_squad_first; member; member = member.br_squad_next)
 +            {
 +                member.clientcolors = 1024 + squad_color;
 +            }
 +        }
 +
 +        it.br_drop_time = time;
 +
 +        float min_distance = max(autocvar_g_br_drop_distance_force, 0);
 +        if(!br_SquadIsBotsOnly(it))
 +            it.br_force_drop_distance = min_distance;
 +        else
 +            it.br_force_drop_distance = min_distance + random() * max(dropship_path_length - (min_distance + dropship_speed * br_drop_time_secs), 0);
 +    });
 +
 +    br_event_supply_time = br_event_vehicle_time = time;
 +}
 +
 +void br_Initialize()
 +{
 +    br_started = false;
 +    squads_colored = autocvar_g_br_squad_colors;
 +
 +    EliminatedPlayers_Init(br_isEliminated);
 +}
Simple merge
Simple merge
index 2ba2a86ad5fa0dabcc4979f57b3e2f8c0bb41ba7,57877daccd6338f6e3463003cb0870dccb4f2901..4c8926c3ff626ac5b2116f2c19e95e703aac3a13
@@@ -390,13 -403,8 +405,12 @@@ string multiteam_info_sprintf(string in
      MULTITEAM_INFO(KEYHUNT_DESTROYED,                       N_CONSOLE,  1, 0, "s1", "",         "",     _("^BG%s^BG destroyed the ^TC^TT Key"), "", KEY)
      MULTITEAM_INFO(KEYHUNT_PICKUP,                          N_CONSOLE,  1, 0, "s1", "",         "",     _("^BG%s^BG picked up the ^TC^TT Key"), "", KEY)
  
-     MSG_INFO_NOTIF(LMS_FORFEIT,                             N_CHATCON,  1, 0, "s1", "",         "",     _("^BG%s^F3 forfeited"), "")
      MSG_INFO_NOTIF(LMS_NOLIVES,                             N_CONSOLE,  1, 0, "s1", "",         "",     _("^BG%s^F3 has no more lives left"), "")
  
 +    MSG_INFO_NOTIF(BR_REVIVED,                              N_CONSOLE,  2, 0, "s1 s2", "",      "",     _("^BG%s^K3 was revived by ^BG%s"), "")
 +    MSG_INFO_NOTIF(BR_DOWN,                                 N_CONSOLE,  2, 0, "s1 s2", "",      "",     _("^BG%s^K1 was downed by ^BG%s"), "")
 +    MSG_INFO_NOTIF(BR_DOWN_SELF,                            N_CONSOLE,  1, 0, "s1", "",         "",     _("^BG%s^K1 downed themself"), "")
 +
      MSG_INFO_NOTIF(MONSTERS_DISABLED,                       N_CONSOLE,  0, 0, "", "",           "",     _("^BGMonsters are currently disabled"), "")
  
      MULTITEAM_INFO(NEXBALL_RETURN_HELD,                     N_CONSOLE,  0, 0, "", "",           "",     _("^BGThe ^TC^TT^BG team held the ball for too long"), "", NAME)
      MULTITEAM_CENTER(KEYHUNT_START,                     N_ENABLE,    0, 0, "",               CPID_KEYHUNT,           "0 0",  _("^BGYou are starting with the ^TC^TT Key"), "", KEY)
  
      MSG_CENTER_NOTIF(LMS_NOLIVES,                       N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^BGYou have no lives left, you must wait until the next match"), "")
-     MSG_CENTER_NOTIF(LMS_SPECWARN,                      N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^F4WARNING:^BG you can't rejoin this match after spectating.\nUse the same command again to spectate anyway."), "")
+     MSG_CENTER_NOTIF(LMS_VISIBLE_LEADER,                N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^BGEnemies can now see you on radar!"), "")
+     MSG_CENTER_NOTIF(LMS_VISIBLE_OTHER,                 N_ENABLE,    0, 0, "",               CPID_LMS,               "0 0",  _("^BGLeaders can now be seen by enemies on radar!"), "")
  
 +    MSG_CENTER_NOTIF(BR_JOIN_LATE,                      N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^BGMatch already started, you must wait until the next match"), "")
 +    MSG_CENTER_NOTIF(BR_JOIN_DEAD,                      N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^BGYou are dead, you must wait until the next match"), "")
 +    MSG_CENTER_NOTIF(BR_FORFEIT,                        N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^BGForfeiting not possible"), "")
 +    MSG_CENTER_NOTIF(BR_DOWN_WAIT,                      N_ENABLE,    0, 0, "",               CPID_BR_DOWN,          "-1 0",  _("^F1Waiting for revive..."), "")
 +    MSG_CENTER_NOTIF(BR_DEAD_SQUAD,                     N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^K1Your squad has been eliminated"), "")
 +    MSG_CENTER_NOTIF(BR_REVIVE,                         N_ENABLE,    1, 0, "s1",             CPID_Null,              "0 0",  _("^K3You revived ^BG%s"), "")
 +    MSG_CENTER_NOTIF(BR_REVIVED,                        N_ENABLE,    1, 0, "s1",             CPID_Null,              "0 0",  _("^K3You were revived by ^BG%s"), "")
 +    MSG_CENTER_NOTIF(BR_DOWN,                           N_ENABLE,    1, 0, "s1",             CPID_Null,              "0 0",  _("^K1You were downed by ^BG%s"), "")
 +    MSG_CENTER_NOTIF(BR_DOWN_SELF,                      N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^K1You downed yourself"), "")
 +    MSG_CENTER_NOTIF(BR_DROPSHIP,                       N_ENABLE,    0, 0, "",               CPID_BR_DROP,           "-1 0",  _("^BG^F1Jump^BG to drop"), "")
 +    MSG_CENTER_NOTIF(BR_DROP_DETACH,                    N_ENABLE,    0, 0, "",               CPID_BR_DROP,           "-1 0",  _("^BG^F1Secondary fire^BG to detach"), "")
 +    MSG_CENTER_NOTIF(BR_RING_WARN,                      N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  _("^F4You are outside of ^F2the ring^F4, get back in fast!"), "")
 +    MSG_CENTER_NOTIF(BR_RING_CLOSE,                     N_ENABLE,    0, 1, "f1",             CPID_Null,              "0 0",  _("^F2Ring^F4 is closing! (strength: ^F1%s^F4)"), "")
 +
      MSG_CENTER_NOTIF(MISSING_TEAMS,                     N_ENABLE,    0, 1, "missing_teams",  CPID_MISSING_TEAMS,     "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "")
      MSG_CENTER_NOTIF(MISSING_PLAYERS,                   N_ENABLE,    0, 1, "f1",             CPID_MISSING_PLAYERS,   "-1 0", _("^BGWaiting for %s player(s) to join..."), "")
  
Simple merge
index ada97467154f3f678108128fbfb389b942ddf4ea,8a01893f1019c423927029d57b004baa8f66b88c..571e8584e409252a3b49052efa601e4c38140527
@@@ -85,12 -57,40 +57,44 @@@ REGISTER_SP(LMS_LIVES)
  REGISTER_SP(NEXBALL_GOALS);
  REGISTER_SP(NEXBALL_FAULTS);
  
- REGISTER_SP(ONS_TAKES);
  REGISTER_SP(ONS_CAPS);
+ REGISTER_SP(ONS_TAKES);
+ REGISTER_SP(TKA_PICKUPS);
+ REGISTER_SP(TKA_BCTIME);
+ REGISTER_SP(TKA_CARRIERKILLS);
+ REGISTER_SP(SURV_SURVIVALS);
+ REGISTER_SP(SURV_HUNTS);
+ REGISTER_SP(SCORE);
+ REGISTER_SP(KILLS);
+ REGISTER_SP(DEATHS);
+ REGISTER_SP(TEAMKILLS);
+ REGISTER_SP(SUICIDES);
+ REGISTER_SP(DMG);
+ REGISTER_SP(DMGTAKEN);
+ REGISTER_SP(ROUNDS_PL);
+ REGISTER_SP(ELO); // not sortable
+ REGISTER_SP(FPS); // not sortable
+ // fields not networked via the score system
+ REGISTER_SP(END);
+ REGISTER_SP(PING);
+ REGISTER_SP(PL);
+ REGISTER_SP(NAME);
+ REGISTER_SP(SEPARATOR);
+ REGISTER_SP(KDRATIO); // kills / deaths
+ REGISTER_SP(SUM); // kills - deaths
+ REGISTER_SP(FRAGS); // kills - suicides
 +
 +REGISTER_SP(BR_RANK);
 +REGISTER_SP(BR_SQUAD);
 +REGISTER_SP(BR_REVIVALS);
  #endif
  
  
index 889aba13c12c7b49949ddae99fc14de9c5084abb,96a136cadc0dd3b11a6b5007345d6421afa80deb..f16352ef7c3973a47defe3cc5bce07a7a6abf866
@@@ -128,21 -128,11 +128,16 @@@ REGISTER_STAT(NADE_BONUS_SCORE, float
  REGISTER_STAT(PLASMA, int)
  REGISTER_STAT(FROZEN, int)
  REGISTER_STAT(REVIVE_PROGRESS, float)
 +REGISTER_STAT(BLEEDING, bool)
 +REGISTER_STAT(DROP, int)
 +REGISTER_STAT(SQUADSALIVE, int)
 +REGISTER_STAT(PLAYERSALIVE, int)
 +REGISTER_STAT(SQUADCOLORS, bool)
  REGISTER_STAT(ROUNDLOST, int)
  REGISTER_STAT(CAPTURE_PROGRESS, float)
- REGISTER_STAT(ENTRAP_ORB, float)
- REGISTER_STAT(ENTRAP_ORB_ALPHA, float)
  REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime)
  REGISTER_STAT(KILL_TIME, float)
- REGISTER_STAT(VEIL_ORB, float)
- REGISTER_STAT(VEIL_ORB_ALPHA, float)
+ REGISTER_STAT(TKA_BALLSTATUS, int)
  
  #ifdef SVQC
  float autocvar_sv_showfps = 0;
Simple merge
Simple merge
index 9c9ed148ed1d73ef9a0b12c3817cfafbf1481c89,ac3c798a9f3f424e8fbe338b8459fc9ada444f27..a28c99a9b9f3299326a600e56842e3ef94c92b0d
@@@ -668,8 -672,8 +672,9 @@@ float updateCompression(
        GAMETYPE(MAPINFO_TYPE_NEXBALL) \
        GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
        GAMETYPE(MAPINFO_TYPE_ASSAULT) \
+       GAMETYPE(MAPINFO_TYPE_SURVIVAL) \
        /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
 +      /* GAMETYPE(MAPINFO_TYPE_BR) */ \
        /**/
  
  // hidden gametypes come last so indexing always works correctly
index 37cbcdee296200c2070661264379641c4be9fa7f,5a72b59774478a32cf5aacaf046ad4a864ab510b..af62c8c915ac5de622972f1acecf9ca7d937c99a
@@@ -306,7 -336,10 +338,10 @@@ int Say(entity source, int teamsay, ent
                        dedicated_print(msgstr); // send to server console too
                        if(sourcecmsgstr != "")
                                centerprint(source, sourcecmsgstr);
 -                      FOREACH_CLIENT((IS_PLAYER(it) || INGAME(it)) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
 +                      FOREACH_CLIENT((IS_PLAYER(it) || INGAME(it) || IN_SQUAD(it)) && IS_REAL_CLIENT(it) && it != source && (((it.team == source.team) && !IN_SQUAD(source)) || SAME_SQUAD(it, source)) && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, it)) // check ignored players from personal chat log (from "ignore" command)
+                                       continue; // no sending to this player, thank you very much
                                sprint(it, msgstr);
                                if(cmsgstr != "")
                                        centerprint(it, cmsgstr);
                {
                        sprint(source, sourcemsgstr);
                        dedicated_print(msgstr); // send to server console too
 -                      FOREACH_CLIENT(!(IS_PLAYER(it) || INGAME(it)) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
 +                      FOREACH_CLIENT(!(IS_PLAYER(it) || INGAME(it) || IN_SQUAD(it)) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               if(IS_REAL_CLIENT(source) && ignore_playerinlist(source, it)) // check ignored players from personal chat log (from "ignore" command)
+                                       continue; // no sending to this player, thank you very much
                                sprint(it, msgstr);
                        });
                        event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin));
Simple merge
Simple merge
index 0408f93d2f73c6b74b1fc145c4e83841f176b404,977f235715a90a90064d4a8f332f3dbd36595af1..502a886ae1d26e06d6e0be7016e703723e56c90d
@@@ -298,7 -306,12 +306,13 @@@ void cvar_changes_init(
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_on_dm_maps");
                BADCVAR("g_tdm_teams");
+               BADCVAR("g_tka");
+               BADCVAR("g_tka_on_ka_maps");
+               BADCVAR("g_tka_on_tdm_maps");
+               BADCVAR("g_tka_teams");
+               BADCVAR("g_tmayhem");
+               BADCVAR("g_tmayhem_teams");
 +              BADCVAR("g_br");
                BADCVAR("g_vip");
                BADCVAR("leadlimit");
                BADCVAR("nextmap");