jkxr/Projects/Android/jni/OpenJK/codeJK2/game/g_utils.cpp
Simon 0f7803e934 Squashed commit of the following:
commit 0c85ac4704
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 15 18:47:10 2023 +0200

    Menu updates

commit 35b89acc87
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat Apr 15 12:32:35 2023 +0100

    Support for changeable fresh rate

commit 539bfa8956
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 15 00:02:59 2023 +0200

    Ensure proper menu defaults on the very first start

commit 216a225aa6
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Apr 14 22:41:24 2023 +0200

    Fix storing of force crosshair option

commit 48c2486aad
Merge: a72ca45 fcb9169
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Apr 14 22:23:24 2023 +0200

    Merge remote-tracking branch 'origin/main' into contributions

commit a72ca459c5
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Apr 14 22:19:47 2023 +0200

    Intern all default values; Remove no longer needed configuration files

commit 32c4e6eac1
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Apr 14 20:59:41 2023 +0200

    Ensure weapon adjustment is applied when resetting to defaults

commit fcb9169955
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Fri Apr 14 18:58:32 2023 +0200

    Increased default force motion trigger

commit 2433ae4110
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Fri Apr 14 18:55:56 2023 +0200

    Change Force Crosshair

commit 4ab6a6024a
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Fri Apr 14 17:01:16 2023 +0200

    Fixed player knockback.... hopefully

commit 7deeee7a6f
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Fri Apr 14 17:01:00 2023 +0200

    Increased Force Power Visibility

commit 1fdcb7c48f
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Fri Apr 14 15:16:34 2023 +0200

    Changed Brightness Range and Default

commit fada09a0bb
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Apr 13 20:38:13 2023 +0200

    Fix remote turret on-screen help

commit 82af313786
Merge: 7680cb1 d13176c
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Apr 13 18:59:46 2023 +0200

    Merge branch 'main' into contributions

commit d13176c9ba
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 13 13:29:49 2023 +0200

    New Quick Save / Load Video

commit 7680cb1288
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Wed Apr 12 22:26:44 2023 +0200

    Fix use action when controlling droid; fix droid view help

commit 4d90595139
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 12 18:21:10 2023 +0200

    Fencing Speed on Pico default

commit 37d5ac4184
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 12 18:12:07 2023 +0200

    Changes to TBDC

    Different menu text + added vanilla mode

commit d6e40ead64
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 12 17:44:29 2023 +0200

    Set Fencing Speed back to Default (by Default)

commit 3f7d116e25
Merge: 7424628 c7c66e4
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 12 14:50:26 2023 +0200

    Merge branch 'main' of https://github.com/DrBeef/JKXR

commit 7424628a5d
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 12 14:50:23 2023 +0200

    Updated Weapons

    + Shader fix for FX mod

commit c7c66e46a7
Author: Simon <simonbrown77@googlemail.com>
Date:   Wed Apr 12 08:37:36 2023 +0100

    Revert "Arrange quick save icons horizontally"

    This reverts commit 20f8fff3fe.

    Also make the icons white

commit fa54045159
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 11 21:02:29 2023 +0100

    Update .gitignore

commit 20f8fff3fe
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 11 21:00:09 2023 +0100

    Arrange quick save icons horizontally

commit d3dcac8f9d
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 11 20:58:56 2023 +0100

    Save game crash fix in JKO

commit 6314561b52
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 11 08:49:42 2023 +0100

    Switch selector on offhand using offhand thumbstick

commit 3b2ffd7289
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 11 08:39:32 2023 +0100

    Attempted fix for save game crash

    - Not really a proper fix, but might at least workaround it

commit c135eefa05
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 11 08:38:54 2023 +0100

    Fixed save/load icon image size

commit f51270a294
Merge: a2ff16b e243d0b
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Apr 10 19:42:11 2023 +0100

    Merge branch 'main' of https://github.com/DrBeef/JKXR

commit a2ff16b576
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Apr 10 19:42:06 2023 +0100

    Quick Save/Load in Selector

    Haven't been able to test yet!

commit e243d0bdfa
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Mon Apr 10 18:29:18 2023 +0200

    Update README.md

commit b9d0314a6a
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sun Apr 9 19:38:32 2023 +0200

    Make getting into AT-ST easier

commit d121206f83
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 8 22:49:28 2023 +0200

    Tune touch gesture distance; Improve use interaction in 3rd person.

commit 260d501776
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 8 22:34:59 2023 +0200

    Fix and improve weapon adjustment helper axes

commit a6318867bb
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 6 20:35:02 2023 +0200

    Update README.md

commit 4a1d90e729
Merge: 425db0f 821a56d
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Apr 6 19:12:07 2023 +0200

    Merge branch 'main' into contributions

commit 821a56d4b9
Author: Simon Brown <simonbrown77@googlemail.com>
Date:   Thu Apr 6 17:24:20 2023 +0100

    Update README.md

commit 67b7d26de8
Merge: b407932 ccd63d4
Author: Simon Brown <simonbrown77@googlemail.com>
Date:   Thu Apr 6 16:54:01 2023 +0100

    Merge pull request #4 from DrBeef/TBDC

    TBDC Scales in Code

commit b407932bb2
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 6 17:43:26 2023 +0200

    Update README.md

commit 174c3ce96c
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 6 17:33:33 2023 +0200

    Update README.md

commit e9af8e87c2
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 6 17:23:36 2023 +0200

    Update README.md

commit ccd63d4eec
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 6 16:57:36 2023 +0200

    Forced for Extended Menu

commit d6b3e8eb65
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Thu Apr 6 16:55:02 2023 +0200

    TBDC Scales in Code

commit a5f3adf725
Author: Simon Brown <simonbrown77@googlemail.com>
Date:   Thu Apr 6 15:04:07 2023 +0100

    Update README.md

commit 316c2a2904
Merge: 387c34b 90b694f
Author: Simon Brown <simonbrown77@googlemail.com>
Date:   Thu Apr 6 10:02:01 2023 +0100

    Merge pull request #2 from DrBeef/TBDC

    Team Beef Directors Cut

commit 387c34b53e
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Apr 6 09:59:18 2023 +0100

    Update Beef Crawl

commit a271b61ac7
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Apr 6 09:58:18 2023 +0100

    Switch to use Bummser's NPC file if TBDC is disabled

commit a2f1644d72
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Apr 6 08:39:02 2023 +0100

    Update version to 1.0.0 for release

commit f785fdc393
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Apr 6 08:00:11 2023 +0100

    update github banner

    Update README.md

    Update README.md

    Update README.md

    Update README.md

    Update README.md

    Added Team Beef Patreon banner

commit 90b694ff60
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 5 22:21:41 2023 +0200

    Last Cleanup

commit 70468332d6
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 5 22:19:45 2023 +0200

    TBDC Cleanup

commit 6660e8c984
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 5 21:58:00 2023 +0200

    Credits / NPC Scale

commit e1b03fdcc6
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 5 21:57:07 2023 +0200

    Fixing Quick Save

commit 95950f2390
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 5 20:08:10 2023 +0200

    Updated ratios per difficulty

commit d6235ef199
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Wed Apr 5 00:30:35 2023 +0200

    Darkened Menu GFX

commit a3fdb460c4
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Apr 4 23:03:31 2023 +0100

    Prevent crash when throwing Saber

    seems it is indexing the g2 model surface that doesn't exist, might be something to do with the new hilt and the old hilt being in the save. Not sure, but this stops it crashing.

commit 4c751fcb59
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Tue Apr 4 23:59:12 2023 +0200

    Y Close Datapad

commit a1216665c8
Merge: 1939a26 d841464
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Tue Apr 4 23:48:20 2023 +0200

    Merge pull request #1 from DrBeef/main

    Main -> TDBC

commit 1939a26542
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Tue Apr 4 23:37:41 2023 +0200

    TBDC

    Laser Saber deflections
    Guns balancing.

commit d841464924
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Apr 4 21:59:31 2023 +0200

    Update help resources and menu

commit 425db0f108
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Apr 4 21:59:31 2023 +0200

    Update help resources and menu

commit 3c895d27de
Merge: fe9e12a c3819fa
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Apr 4 21:05:48 2023 +0200

    Merge branch 'main' into contributions

commit c3819fa407
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Mon Apr 3 00:36:57 2023 +0200

    Demo folder assets fix

commit fe9e12a3f9
Merge: d79f57b 2b255ac
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Mon Apr 3 22:55:29 2023 +0200

    Merge branch 'main' into contributions

commit 2b255ac3ea
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Apr 3 21:26:39 2023 +0100

    Improved version string

commit 62284414d0
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Apr 3 21:26:20 2023 +0100

    Ensure intro vid can be skipped without having to press trigger first

commit 65674abe2a
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Mon Apr 3 21:36:35 2023 +0200

    Fix Controller Location Buzz for Quick Save / Load

commit ba2e726a79
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Mon Apr 3 00:36:57 2023 +0200

    Demo folder assets fix

commit 7175c872a9
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Mon Apr 3 00:36:46 2023 +0200

    Knockback settings TBDC

commit cc7e73d04a
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Apr 2 22:59:47 2023 +0100

    Update beef_crawl.tga

commit dd71a05e36
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Apr 2 22:51:49 2023 +0100

    Put version at the bottom of the main menu

commit cb52d310c1
Author: Grant Bagwell <general@grantbagwell.co.uk>
Date:   Sun Apr 2 23:18:28 2023 +0200

    TBDC Weapons

commit ac71f24e78
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sun Apr 2 18:43:10 2023 +0200

    Updated control scheme picture

commit 3f5c767e8d
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Apr 2 19:42:42 2023 +0100

    Update AndroidManifest.xml

commit 64ab392fc5
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Apr 2 19:42:39 2023 +0100

    JKA: Some CVAR change to increase performance

commit d43de53e8a
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Apr 2 19:41:50 2023 +0100

    Added missed saber blocking check

    might explain why some people find JKA a bit easy if it is auto-blocking in 1st person

commit 8b23e255d8
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Apr 2 19:41:16 2023 +0100

    Update open xr headers

commit d79f57ba2d
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sun Apr 2 18:43:10 2023 +0200

    Updated control scheme picture

commit e3524e8e48
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 1 18:48:42 2023 +0200

    Add use haptic feedback; Fix use in 3rd person mode

commit 05fc4d5ab4
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 1 12:07:13 2023 +0200

    Fix menu haptics

commit eec46c183d
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 1 18:48:42 2023 +0200

    Add use haptic feedback; Fix use in 3rd person mode

commit fe9891f8db
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 1 12:07:13 2023 +0200

    Fix menu haptics

commit 21483d0d5a
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 1 11:25:11 2023 +0200

    Make weapon adjustment mode independent for each weapon; Optimize loading of weapon adjustments

commit baec8832ab
Merge: 82706df 52fcc8a
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Sat Apr 1 11:15:25 2023 +0200

    Merge branch 'main' into contributions

commit 52fcc8a49e
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat Apr 1 09:47:48 2023 +0100

    Revert "Removed the no-backface-culling for weapons as it is no longer needed"

    This reverts commit b899b99178.

commit 5e66ebf6fc
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Mar 31 15:40:54 2023 +0200

    Allow to skip cinematics also by triggers

commit 587277fa7f
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Mar 31 14:52:40 2023 +0200

    Fix use/crouch buttons on switched control schemes

commit 82706df34f
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Mar 31 15:40:54 2023 +0200

    Allow to skip cinematics also by triggers

commit 1111766032
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Mar 31 14:52:40 2023 +0200

    Fix use/crouch buttons on switched control schemes

commit 822d1ddbba
Merge: a1a7d54 5f56cf4
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Fri Mar 31 08:43:50 2023 +0200

    Merge branch 'main' into contributions

commit 5f56cf48fc
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Mar 30 22:16:37 2023 +0100

    Camera shake fix part 2

commit 3dd7833cd0
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Mar 30 22:06:18 2023 +0100

    Disable camera shake when charging a weapon's alt fire

commit 6202017b6a
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 21:52:44 2023 +0200

    Fix virtual gun stock

commit 6d49f87150
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 19:26:36 2023 +0200

    Do not check angle on non-facing triggers

commit 926c64c691
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 17:54:33 2023 +0200

    Add angle check for triggers touched by hand

commit 78f7d9bcfc
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Wed Mar 29 22:59:27 2023 +0200

    JKA mod menu warning

commit 4219f996e9
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Wed Mar 29 22:42:11 2023 +0200

    JKO mod menu warning

commit b899b99178
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Mar 30 21:45:10 2023 +0100

    Removed the no-backface-culling for weapons as it is no longer needed

    used only for hand models now

commit 9877859676
Author: Simon <simonbrown77@googlemail.com>
Date:   Thu Mar 30 21:44:36 2023 +0100

    Update to the VC+Elin weapons pack

    includes a sweet new saber hilt

commit a1a7d541fa
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 21:52:44 2023 +0200

    Fix virtual gun stock

commit 15d932c75f
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 19:26:36 2023 +0200

    Do not check angle on non-facing triggers

commit 402277e717
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 17:54:33 2023 +0200

    Add angle check for triggers touched by hand

commit 9b22378c88
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Thu Mar 30 17:52:26 2023 +0200

    Add special delay for re-triggering security cameras

commit c4cc218f8b
Author: Simon <simonbrown77@googlemail.com>
Date:   Wed Mar 29 22:44:50 2023 +0100

    Turn the stun baton into an "always active" weapon

    movement still triggers the sound, but it will always shock an enemy when it makes contact

commit a4e99c20f9
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Wed Mar 29 22:59:27 2023 +0200

    JKA mod menu warning

commit 442a1fc8e2
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Wed Mar 29 22:42:11 2023 +0200

    JKO mod menu warning

commit 02261c57f0
Merge: 3c64fa7 51c703a
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Wed Mar 29 20:51:42 2023 +0200

    Merge branch 'contributions' of github.com:DrBeef/JKXR into contributions

commit 3c64fa7e3e
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 23:19:29 2023 +0100

    Lowered Stun Baton trigger velocity

commit 43192a355d
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 23:03:17 2023 +0100

    Move beef_crawl back into the game specific assets

    as we will be fixing the JKO one on release, but updating JKA

commit 94e82c2da3
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Mar 28 23:21:28 2023 +0200

    Fix subtitles rendering; Fix rendering of other centered texts

commit 40128567be
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Mar 28 18:02:08 2023 +0200

    Add help to menu

commit 4f1b6b5f07
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Mar 28 17:36:13 2023 +0200

    Split vr asset packs to avoid duplicates

commit 51c703a481
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Mar 28 23:21:28 2023 +0200

    Fix subtitles rendering; Fix rendering of other centered texts

commit 04a539f890
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Mar 28 18:02:08 2023 +0200

    Add help to menu

commit d9126738d3
Author: Petr Bartos <petr.bartos@plus4u.net>
Date:   Tue Mar 28 17:36:13 2023 +0200

    Split vr asset packs to avoid duplicates

commit 62b9a0bfab
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 13:52:45 2023 +0100

    Hide force power aura when item selector is shown

commit 0a9206642e
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 13:52:23 2023 +0100

    Slight adjustment to muzzle position based on whether scoped/two-handed

commit f452b9cf06
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 13:51:49 2023 +0100

    Slight tweak to NPC Combat

commit 20de6ec478
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 13:50:54 2023 +0100

    Added simple readme and license

commit 77672f612b
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 07:20:22 2023 +0100

    Updated website link to be the patreon

commit 2a1cd0e6e7
Author: Simon <simonbrown77@googlemail.com>
Date:   Tue Mar 28 07:20:10 2023 +0100

    Don't send roll to the server, this might be causing gradual roll drift

commit 1b22652e5c
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Mar 27 21:38:46 2023 +0100

    Fixed aiming of bowcaster and demp alt fire

commit 895b09041f
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Mar 27 21:38:25 2023 +0100

    JKA - Ensure UseVR Position is only true when in first person

commit 3897531544
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Mar 27 21:38:17 2023 +0100

    JKO - Ensure UseVR Position is only true when in first person

commit 3b5121e349
Author: Simon <simonbrown77@googlemail.com>
Date:   Mon Mar 27 21:37:49 2023 +0100

    Render Special Effects on hand/weapon when force power is activated for protection etc

commit 50db9039df
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Mar 26 16:33:52 2023 +0100

    Separate no copy file flag for JK3

commit 1968a7d8ba
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Mar 26 16:33:22 2023 +0100

    Always use right hand as saber home

commit 74dcd955d2
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Mar 26 16:32:49 2023 +0100

    Fix crash in JKA

commit 1e4692d04a
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Mar 26 16:32:16 2023 +0100

    Update z_Crusty_and_Elin_vr_weapons.pk3

commit 134dec8264
Author: Simon <simonbrown77@googlemail.com>
Date:   Sun Mar 26 16:31:53 2023 +0100

    Fix possible crash on JK2

commit f6dc432f6a
Author: Simon <simonbrown77@googlemail.com>
Date:   Sat Mar 25 15:36:01 2023 +0000

    Copy mods to jk2demo if it exists

    to resolve the issue with mods not being picked up from the base folder
2023-04-16 09:38:26 +01:00

1868 lines
44 KiB
C++

/*
===========================================================================
Copyright (C) 1999 - 2005, Id Software, Inc.
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/
// g_utils.c -- misc utility functions for game module
#include "g_headers.h"
#include "g_local.h"
#include "g_functions.h"
#include "g_nav.h"
#include "g_icarus.h"
#include "b_local.h"
#include "anims.h"
#include "../../code/rd-common/mdx_format.h"
#include <JKXR/VrClientInfo.h>
#define ACT_ACTIVE qtrue
#define ACT_INACTIVE qfalse
extern void NPC_UseResponse ( gentity_t *self, gentity_t *user, qboolean useWhenDone );
extern qboolean PM_CrouchAnim( int anim );
/*
=========================================================================
model / sound configstring indexes
=========================================================================
*/
/*
================
G_FindConfigstringIndex
================
*/
extern void ForceTelepathy( gentity_t *self );
int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create ) {
int i;
char s[MAX_STRING_CHARS];
if ( !name || !name[0] ) {
return 0;
}
for ( i=1 ; i<max ; i++ ) {
gi.GetConfigstring( start + i, s, sizeof( s ) );
if ( !s[0] ) {
break;
}
if ( !Q_stricmp( s, name ) ) {
return i;
}
}
if ( !create ) {
return 0;
}
if ( i == max ) {
G_Error( "G_FindConfigstringIndex: overflow adding %s to set %d-%d", name, start, max );
}
gi.SetConfigstring( start + i, name );
return i;
}
/*
Ghoul2 Insert Start
*/
int G_SkinIndex( const char *name ) {
return G_FindConfigstringIndex (name, CS_CHARSKINS, MAX_CHARSKINS, qtrue);
}
/*
Ghoul2 Insert End
*/
int G_ModelIndex( const char *name ) {
return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
}
int G_SoundIndex( const char *name ) {
char stripped[MAX_QPATH];
COM_StripExtension(name, stripped, sizeof(stripped));
return G_FindConfigstringIndex (stripped, CS_SOUNDS, MAX_SOUNDS, qtrue);
}
int G_EffectIndex( const char *name )
{
char temp[MAX_QPATH];
// We just don't want extensions on the things we are registering
COM_StripExtension( name, temp, sizeof(temp) );
return G_FindConfigstringIndex( temp, CS_EFFECTS, MAX_FX, qtrue );
}
#define FX_ENT_RADIUS 32
//-----------------------------
// Effect playing utilities
//-----------------------------
//-----------------------------
void G_PlayEffect( int fxID, vec3_t origin, vec3_t fwd )
{
gentity_t *tent;
vec3_t temp;
tent = G_TempEntity( origin, EV_PLAY_EFFECT );
tent->s.eventParm = fxID;
VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
VectorScale( tent->maxs, -1, tent->mins );
VectorCopy( fwd, tent->pos3 );
// Assume angles, we'll do a cross product on the other end to finish up
MakeNormalVectors( fwd, tent->pos4, temp );
gi.linkentity( tent );
}
// Play an effect at the origin of the specified entity
//----------------------------
void G_PlayEffect( int fxID, int entNum, vec3_t fwd )
{
gentity_t *tent;
vec3_t temp;
tent = G_TempEntity( g_entities[entNum].currentOrigin, EV_PLAY_EFFECT );
tent->s.eventParm = fxID;
tent->s.otherEntityNum = entNum;
VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
VectorScale( tent->maxs, -1, tent->mins );
VectorCopy( fwd, tent->pos3 );
// Assume angles, we'll do a cross product on the other end to finish up
MakeNormalVectors( fwd, tent->pos4, temp );
}
// Play an effect bolted onto the muzzle of the specified client
//----------------------------
void G_PlayEffect( const char *name, int clientNum )
{
gentity_t *tent;
tent = G_TempEntity( g_entities[clientNum].currentOrigin, EV_PLAY_MUZZLE_EFFECT );
tent->s.eventParm = G_EffectIndex( name );
tent->s.otherEntityNum = clientNum;
VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
VectorScale( tent->maxs, -1, tent->mins );
}
//-----------------------------
void G_PlayEffect( int fxID, vec3_t origin, vec3_t axis[3] )
{
gentity_t *tent;
tent = G_TempEntity( origin, EV_PLAY_EFFECT );
tent->s.eventParm = fxID;
VectorSet( tent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
VectorScale( tent->maxs, -1, tent->mins );
// We can just assume axis[2] from doing a cross product on these.
VectorCopy( axis[0], tent->pos3 );
VectorCopy( axis[1], tent->pos4 );
}
// Effect playing utilities - bolt an effect to a ghoul2 models bolton point
//-----------------------------
void G_PlayEffect( int fxID, const int modelIndex, const int boltIndex, const int entNum)
{
gentity_t *tent;
vec3_t origin = {0,0,0};
tent = G_TempEntity( origin, EV_PLAY_EFFECT );
tent->s.eventParm = fxID;
tent->svFlags |=SVF_BROADCAST;
gi.G2API_AttachEnt(&tent->s.boltInfo, &g_entities[entNum].ghoul2[modelIndex], boltIndex, entNum, modelIndex);
}
//-----------------------------
void G_PlayEffect( const char *name, vec3_t origin )
{
vec3_t up = {0, 0, 1};
G_PlayEffect( G_EffectIndex( name ), origin, up );
}
//-----------------------------
void G_PlayEffect( const char *name, vec3_t origin, vec3_t fwd )
{
G_PlayEffect( G_EffectIndex( name ), origin, fwd );
}
//-----------------------------
void G_PlayEffect( const char *name, vec3_t origin, vec3_t axis[3] )
{
G_PlayEffect( G_EffectIndex( name ), origin, axis );
}
//-----------------------------
void G_PlayEffect( const char *name, const int modelIndex, const int boltIndex, const int entNum )
{
G_PlayEffect( G_EffectIndex( name ), modelIndex, boltIndex, entNum );
}
//===Bypass network for sounds on specific channels====================
extern void cgi_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx );
#include "../cgame/cg_media.h" //access to cgs
#include "bg_local.h"
extern void CG_TryPlayCustomSound( vec3_t origin, int entityNum, soundChannel_t channel, const char *soundName, int customSoundSet );
//NOTE: Do NOT Try to use this before the cgame DLL is valid, it will NOT work!
void G_SoundOnEnt (gentity_t *ent, soundChannel_t channel, const char *soundPath)
{
int index = G_SoundIndex( (char *)soundPath );
if ( !ent )
{
return;
}
cgi_S_UpdateEntityPosition( ent->s.number, ent->currentOrigin );
if ( cgs.sound_precache[ index ] )
{
cgi_S_StartSound( NULL, ent->s.number, channel, cgs.sound_precache[ index ] );
}
else
{
CG_TryPlayCustomSound( NULL, ent->s.number, channel, soundPath, -1 );
}
}
void G_SpeechEvent( gentity_t *self, int event )
{
//update entity pos, too
cgi_S_UpdateEntityPosition( self->s.number, self->currentOrigin );
switch ( event )
{
case EV_ANGER1: //Say when acquire an enemy when didn't have one before
case EV_ANGER2:
case EV_ANGER3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*anger%i.wav", event - EV_ANGER1 + 1), CS_COMBAT );
break;
case EV_VICTORY1: //Say when killed an enemy
case EV_VICTORY2:
case EV_VICTORY3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*victory%i.wav", event - EV_VICTORY1 + 1), CS_COMBAT );
break;
case EV_CONFUSE1: //Say when confused
case EV_CONFUSE2:
case EV_CONFUSE3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*confuse%i.wav", event - EV_CONFUSE1 + 1), CS_COMBAT );
break;
case EV_PUSHED1: //Say when pushed
case EV_PUSHED2:
case EV_PUSHED3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*pushed%i.wav", event - EV_PUSHED1 + 1), CS_COMBAT );
break;
case EV_CHOKE1: //Say when choking
case EV_CHOKE2:
case EV_CHOKE3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*choke%i.wav", event - EV_CHOKE1 + 1), CS_COMBAT );
break;
case EV_FFWARN: //Warn ally to stop shooting you
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*ffwarn.wav", CS_COMBAT );
break;
case EV_FFTURN: //Turn on ally after being shot by them
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*ffturn.wav", CS_COMBAT );
break;
//extra sounds for ST
case EV_CHASE1:
case EV_CHASE2:
case EV_CHASE3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*chase%i.wav", event - EV_CHASE1 + 1), CS_EXTRA );
break;
case EV_COVER1:
case EV_COVER2:
case EV_COVER3:
case EV_COVER4:
case EV_COVER5:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*cover%i.wav", event - EV_COVER1 + 1), CS_EXTRA );
break;
case EV_DETECTED1:
case EV_DETECTED2:
case EV_DETECTED3:
case EV_DETECTED4:
case EV_DETECTED5:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*detected%i.wav", event - EV_DETECTED1 + 1), CS_EXTRA );
break;
case EV_GIVEUP1:
case EV_GIVEUP2:
case EV_GIVEUP3:
case EV_GIVEUP4:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*giveup%i.wav", event - EV_GIVEUP1 + 1), CS_EXTRA );
break;
case EV_LOOK1:
case EV_LOOK2:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*look%i.wav", event - EV_LOOK1 + 1), CS_EXTRA );
break;
case EV_LOST1:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*lost1.wav", CS_EXTRA );
break;
case EV_OUTFLANK1:
case EV_OUTFLANK2:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*outflank%i.wav", event - EV_OUTFLANK1 + 1), CS_EXTRA );
break;
case EV_ESCAPING1:
case EV_ESCAPING2:
case EV_ESCAPING3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*escaping%i.wav", event - EV_ESCAPING1 + 1), CS_EXTRA );
break;
case EV_SIGHT1:
case EV_SIGHT2:
case EV_SIGHT3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*sight%i.wav", event - EV_SIGHT1 + 1), CS_EXTRA );
break;
case EV_SOUND1:
case EV_SOUND2:
case EV_SOUND3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*sound%i.wav", event - EV_SOUND1 + 1), CS_EXTRA );
break;
case EV_SUSPICIOUS1:
case EV_SUSPICIOUS2:
case EV_SUSPICIOUS3:
case EV_SUSPICIOUS4:
case EV_SUSPICIOUS5:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*suspicious%i.wav", event - EV_SUSPICIOUS1 + 1), CS_EXTRA );
break;
//extra sounds for Jedi
case EV_COMBAT1:
case EV_COMBAT2:
case EV_COMBAT3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*combat%i.wav", event - EV_COMBAT1 + 1), CS_JEDI );
break;
case EV_JDETECTED1:
case EV_JDETECTED2:
case EV_JDETECTED3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jdetected%i.wav", event - EV_JDETECTED1 + 1), CS_JEDI );
break;
case EV_TAUNT1:
case EV_TAUNT2:
case EV_TAUNT3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*taunt%i.wav", event - EV_TAUNT1 + 1), CS_JEDI );
break;
case EV_JCHASE1:
case EV_JCHASE2:
case EV_JCHASE3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jchase%i.wav", event - EV_JCHASE1 + 1), CS_JEDI );
break;
case EV_JLOST1:
case EV_JLOST2:
case EV_JLOST3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*jlost%i.wav", event - EV_JLOST1 + 1), CS_JEDI );
break;
case EV_DEFLECT1:
case EV_DEFLECT2:
case EV_DEFLECT3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*deflect%i.wav", event - EV_DEFLECT1 + 1), CS_JEDI );
break;
case EV_GLOAT1:
case EV_GLOAT2:
case EV_GLOAT3:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, va("*gloat%i.wav", event - EV_GLOAT1 + 1), CS_JEDI );
break;
case EV_PUSHFAIL:
CG_TryPlayCustomSound( NULL, self->s.number, CHAN_VOICE, "*pushfail.wav", CS_JEDI );
break;
}
}
//=====================================================================
/*
=============
G_Find
Searches all active entities for the next one that holds
the matching string at fieldofs (use the FOFS() macro) in the structure.
Searches beginning at the entity after from, or the beginning if NULL
NULL will be returned if the end of the list is reached.
=============
*/
gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
{
char *s;
if(!match || !match[0])
{
return NULL;
}
if (!from)
from = g_entities;
else
from++;
// for ( ; from < &g_entities[globals.num_entities] ; from++)
int i=from-g_entities;
for ( ; i < globals.num_entities ; i++)
{
// if (!from->inuse)
if(!PInUse(i))
continue;
from=&g_entities[i];
s = *(char **) ((byte *)from + fieldofs);
if (!s)
continue;
if (!Q_stricmp (s, match))
return from;
}
return NULL;
}
/*
============
G_RadiusList - given an origin and a radius, return all entities that are in use that are within the list
============
*/
int G_RadiusList ( vec3_t origin, float radius, gentity_t *ignore, qboolean takeDamage, gentity_t *ent_list[MAX_GENTITIES])
{
float dist;
gentity_t *ent;
gentity_t *entityList[MAX_GENTITIES];
int numListedEntities;
vec3_t mins, maxs;
vec3_t v;
int i, e;
int ent_count = 0;
if ( radius < 1 )
{
radius = 1;
}
for ( i = 0 ; i < 3 ; i++ )
{
mins[i] = origin[i] - radius;
maxs[i] = origin[i] + radius;
}
numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
for ( e = 0 ; e < numListedEntities ; e++ )
{
ent = entityList[ e ];
if ((ent == ignore) || !(ent->inuse) || ent->takedamage != takeDamage)
continue;
// find the distance from the edge of the bounding box
for ( i = 0 ; i < 3 ; i++ )
{
if ( origin[i] < ent->absmin[i] )
{
v[i] = ent->absmin[i] - origin[i];
} else if ( origin[i] > ent->absmax[i] )
{
v[i] = origin[i] - ent->absmax[i];
} else
{
v[i] = 0;
}
}
dist = VectorLength( v );
if ( dist >= radius )
{
continue;
}
// ok, we are within the radius, add us to the incoming list
ent_list[ent_count] = ent;
ent_count++;
}
// we are done, return how many we found
return(ent_count);
}
/*
=============
G_PickTarget
Selects a random entity from among the targets
=============
*/
#define MAXCHOICES 32
gentity_t *G_PickTarget (char *targetname)
{
gentity_t *ent = NULL;
int num_choices = 0;
gentity_t *choice[MAXCHOICES];
if (!targetname)
{
gi.Printf("G_PickTarget called with NULL targetname\n");
return NULL;
}
while(1)
{
ent = G_Find (ent, FOFS(targetname), targetname);
if (!ent)
break;
choice[num_choices++] = ent;
if (num_choices == MAXCHOICES)
break;
}
if (!num_choices)
{
gi.Printf("G_PickTarget: target %s not found\n", targetname);
return NULL;
}
return choice[rand() % num_choices];
}
void G_UseTargets2 (gentity_t *ent, gentity_t *activator, const char *string)
{
gentity_t *t;
//
// fire targets
//
if (string)
{
t = NULL;
while ( (t = G_Find (t, FOFS(targetname), (char *) string)) != NULL )
{
if (t == ent)
{
// gi.Printf ("WARNING: Entity used itself.\n");
}
if (t->e_UseFunc != useF_NULL) // check can be omitted
{
GEntity_UseFunc(t, ent, activator);
}
if (!ent->inuse)
{
gi.Printf("entity was removed while using targets\n");
return;
}
}
}
}
/*
==============================
G_UseTargets
"activator" should be set to the entity that initiated the firing.
Search for (string)targetname in all entities that
match (string)self.target and call their .use function
==============================
*/
void G_UseTargets (gentity_t *ent, gentity_t *activator)
{
//
// fire targets
//
G_UseTargets2 (ent, activator, ent->target);
}
/*
=============
TempVector
This is just a convenience function
for making temporary vectors for function calls
=============
*/
float *tv( float x, float y, float z ) {
static int index;
static vec3_t vecs[8];
float *v;
// use an array so that multiple tempvectors won't collide
// for a while
v = vecs[index];
index = (index + 1)&7;
v[0] = x;
v[1] = y;
v[2] = z;
return v;
}
/*
=============
VectorToString
This is just a convenience function
for printing vectors
=============
*/
char *vtos( const vec3_t v ) {
static int index;
static char str[8][32];
char *s;
// use an array so that multiple vtos won't collide
s = str[index];
index = (index + 1)&7;
Com_sprintf (s, 32, "(%4.2f %4.2f %4.2f)", v[0], v[1], v[2]);
return s;
}
/*
===============
G_SetMovedir
The editor only specifies a single value for angles (yaw),
but we have special constants to generate an up or down direction.
Angles will be cleared, because it is being used to represent a direction
instead of an orientation.
===============
*/
void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
static vec3_t VEC_UP = {0, -1, 0};
static vec3_t MOVEDIR_UP = {0, 0, 1};
static vec3_t VEC_DOWN = {0, -2, 0};
static vec3_t MOVEDIR_DOWN = {0, 0, -1};
if ( VectorCompare (angles, VEC_UP) ) {
VectorCopy (MOVEDIR_UP, movedir);
} else if ( VectorCompare (angles, VEC_DOWN) ) {
VectorCopy (MOVEDIR_DOWN, movedir);
} else {
AngleVectors (angles, movedir, NULL, NULL);
}
VectorClear( angles );
}
float vectoyaw( const vec3_t vec ) {
float yaw;
if (vec[YAW] == 0 && vec[PITCH] == 0) {
yaw = 0;
} else {
if (vec[PITCH]) {
yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
} else if (vec[YAW] > 0) {
yaw = 90;
} else {
yaw = 270;
}
if (yaw < 0) {
yaw += 360;
}
}
return yaw;
}
void G_InitGentity( gentity_t *e )
{
e->inuse = qtrue;
SetInUse(e);
e->classname = "noclass";
e->s.number = e - g_entities;
ICARUS_FreeEnt( e ); //ICARUS information must be added after this point
// remove any ghoul2 models here
//let not gi.G2API_CleanGhoul2Models(e->ghoul2);
//Navigational setups
e->waypoint = WAYPOINT_NONE;
e->lastWaypoint = WAYPOINT_NONE;
e->lastValidWaypoint = WAYPOINT_NONE;
}
/*
=================
G_Spawn
Either finds a free entity, or allocates a new one.
The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
never be used by anything else.
Try to avoid reusing an entity that was recently freed, because it
can cause the client to think the entity morphed into something else
instead of being removed and recreated, which can cause interpolated
angles and bad trails.
=================
*/
gentity_t *G_Spawn( void )
{
int i, force;
gentity_t *e;
e = NULL; // shut up warning
i = 0; // shut up warning
for ( force = 0 ; force < 2 ; force++ )
{
// if we go through all entities and can't find one to free,
// override the normal minimum times before use
e = &g_entities[MAX_CLIENTS];
// for ( i = MAX_CLIENTS ; i<globals.num_entities ; i++, e++)
// {
// if ( e->inuse )
// {
// continue;
// }
for ( i = MAX_CLIENTS ; i<globals.num_entities ; i++)
{
if(PInUse(i))
{
continue;
}
e=&g_entities[i];
// the first couple seconds of server time can involve a lot of
// freeing and allocating, so relax the replacement policy
if ( !force && e->freetime > 2000 && level.time - e->freetime < 1000 ) {
continue;
}
// reuse this slot
G_InitGentity( e );
return e;
}
e=&g_entities[i];
if ( i != ENTITYNUM_MAX_NORMAL )
{
break;
}
}
if ( i == ENTITYNUM_MAX_NORMAL )
{
/*
e = &g_entities[0];
//--------------Use this to dump directly to a file
char buff[256];
FILE *fp;
fp = fopen( "c:/dump.txt", "w" );
for ( i = 0 ; i<globals.num_entities ; i++, e++)
{
if ( e->classname )
{
sprintf( buff, "%d: %s\n", i, e->classname );
}
fputs( buff, fp );
}
fclose( fp );
//---------------Or use this to dump to the console -- beware though, the console will fill quickly and you probably won't see the full list
for ( i = 0 ; i<globals.num_entities ; i++, e++)
{
if ( e->classname )
{
Com_Printf( "%d: %s\n", i, e->classname );
}
}
*/
G_Error( "G_Spawn: no free entities" );
}
// open up a new slot
globals.num_entities++;
G_InitGentity( e );
return e;
}
/*
=================
G_FreeEntity
Marks the entity as free
=================
*/
void G_FreeEntity( gentity_t *ed ) {
gi.unlinkentity (ed); // unlink from world
ICARUS_FreeEnt( ed );
/*if ( ed->neverFree ) {
return;
}*/
// remove any ghoul2 models here
gi.G2API_CleanGhoul2Models(ed->ghoul2);
memset (ed, 0, sizeof(*ed));
//FIXME: This sets the entity number to zero, which is the player.
// This is really fucking stupid and has caused us no end of
// trouble!!! Change it to set the s.number to the proper index
// (ed - g_entities) *OR* ENTITYNUM_NONE!!!
ed->s.number = ENTITYNUM_NONE;
ed->classname = "freed";
ed->freetime = level.time;
ed->inuse = qfalse;
ClearInUse(ed);
}
/*
=================
G_TempEntity
Spawns an event entity that will be auto-removed
The origin will be snapped to save net bandwidth, so care
must be taken if the origin is right on a surface (snap towards start vector first)
=================
*/
gentity_t *G_TempEntity( vec3_t origin, int event ) {
gentity_t *e;
vec3_t snapped;
e = G_Spawn();
e->s.eType = ET_EVENTS + event;
e->classname = "tempEntity";
e->eventTime = level.time;
e->freeAfterEvent = qtrue;
VectorCopy( origin, snapped );
SnapVector( snapped ); // save network bandwidth
G_SetOrigin( e, snapped );
// find cluster for PVS
gi.linkentity( e );
return e;
}
/*
==============================================================================
Kill box
==============================================================================
*/
/*
=================
G_KillBox
Kills all entities that would touch the proposed new positioning
of ent. Ent should be unlinked before calling this!
=================
*/
void G_KillBox (gentity_t *ent) {
int i, num;
gentity_t *touch[MAX_GENTITIES], *hit;
vec3_t mins, maxs;
VectorAdd( ent->client->ps.origin, ent->mins, mins );
VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
for (i=0 ; i<num ; i++) {
hit = touch[i];
if ( !hit->client ) {
continue;
}
if ( hit == ent ) {
continue;
}
if ( ent->s.number && hit->client->ps.stats[STAT_HEALTH] <= 0 )
{//NPC
continue;
}
if ( ent->s.number )
{//NPC
if ( !(hit->contents&CONTENTS_BODY) )
{
continue;
}
}
else
{//player
if ( !(hit->contents & ent->contents) )
{
continue;
}
}
// nail it
G_Damage ( hit, ent, ent, NULL, NULL,
100000, DAMAGE_NO_PROTECTION, MOD_UNKNOWN);
}
}
//==============================================================================
/*
===============
G_AddEvent
Adds an event+parm and twiddles the event counter
===============
*/
void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
int bits;
if ( !event ) {
gi.Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
return;
}
#if 0 // FIXME: allow multiple events on an entity
// if the entity has an event that hasn't expired yet, don't overwrite
// it unless it is identical (repeated footsteps / muzzleflashes / etc )
if ( ent->s.event && ent->s.event != event ) {
gentity_t *temp;
// generate a temp entity that references the original entity
gi.Printf( "eventPush\n" );
temp = G_Spawn();
temp->s.eType = ET_EVENT_ONLY;
temp->s.otherEntityNum = ent->s.number;
G_SetOrigin( temp, ent->s.origin );
G_AddEvent( temp, event, eventParm );
temp->freeAfterEvent = qtrue;
gi.linkentity( temp );
return;
}
#endif
// clients need to add the event in playerState_t instead of entityState_t
if ( !ent->s.number ) //only one client
{
#if 0
bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
ent->client->ps.externalEvent = event | bits;
ent->client->ps.externalEventParm = eventParm;
ent->client->ps.externalEventTime = level.time;
#endif
if ( eventParm > 255 )
{
if ( event == EV_PAIN )
{//must have cheated, in undying?
eventParm = 255;
}
else
{
assert( eventParm < 256 );
}
}
AddEventToPlayerstate( event, eventParm, &ent->client->ps );
} else {
bits = ent->s.event & EV_EVENT_BITS;
bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
ent->s.event = event | bits;
ent->s.eventParm = eventParm;
}
ent->eventTime = level.time;
}
/*
=============
G_Sound
=============
*/
void G_Sound( gentity_t *ent, int soundIndex )
{
gentity_t *te;
te = G_TempEntity( ent->currentOrigin, EV_GENERAL_SOUND );
te->s.eventParm = soundIndex;
}
/*
=============
G_Sound
=============
*/
void G_SoundAtSpot( vec3_t org, int soundIndex )
{
gentity_t *te;
te = G_TempEntity( org, EV_GENERAL_SOUND );
te->s.eventParm = soundIndex;
}
/*
=============
G_SoundBroadcast
Plays sound that can permeate PVS blockage
=============
*/
void G_SoundBroadcast( gentity_t *ent, int soundIndex )
{
gentity_t *te;
te = G_TempEntity( ent->currentOrigin, EV_GLOBAL_SOUND ); //full volume
te->s.eventParm = soundIndex;
te->svFlags |= SVF_BROADCAST;
}
//==============================================================================
/*
================
G_SetOrigin
Sets the pos trajectory for a fixed position
================
*/
void G_SetOrigin( gentity_t *ent, const vec3_t origin )
{
VectorCopy( origin, ent->s.pos.trBase );
if(ent->client)
{
VectorCopy( origin, ent->client->ps.origin );
VectorCopy( origin, ent->s.origin );
}
else
{
ent->s.pos.trType = TR_STATIONARY;
}
ent->s.pos.trTime = 0;
ent->s.pos.trDuration = 0;
VectorClear( ent->s.pos.trDelta );
VectorCopy( origin, ent->currentOrigin );
}
//===============================================================================
qboolean G_CheckInSolid (gentity_t *self, qboolean fix)
{
trace_t trace;
vec3_t end, mins;
VectorCopy(self->currentOrigin, end);
end[2] += self->mins[2];
VectorCopy(self->mins, mins);
mins[2] = 0;
gi.trace(&trace, self->currentOrigin, mins, self->maxs, end, self->s.number, self->clipmask, G2_NOCOLLIDE, 0);
if(trace.allsolid || trace.startsolid)
{
return qtrue;
}
if(trace.fraction < 1.0)
{
if(fix)
{//Put them at end of trace and check again
vec3_t neworg;
VectorCopy(trace.endpos, neworg);
neworg[2] -= self->mins[2];
G_SetOrigin(self, neworg);
gi.linkentity(self);
return G_CheckInSolid(self, qfalse);
}
else
{
return qtrue;
}
}
return qfalse;
}
qboolean infront(gentity_t *from, gentity_t *to)
{
vec3_t angles, dir, forward;
float dot;
angles[PITCH] = angles[ROLL] = 0;
angles[YAW] = from->s.angles[YAW];
AngleVectors(angles, forward, NULL, NULL);
VectorSubtract(to->s.origin, from->s.origin, dir);
VectorNormalize(dir);
dot = DotProduct(forward, dir);
if(dot < 0.0f)
{
return qfalse;
}
return qtrue;
}
void Svcmd_Use_f( void )
{
char *cmd1 = gi.argv(1);
if ( !cmd1 || !cmd1[0] )
{
//FIXME: warning message
gi.Printf( "'use' takes targetname of ent or 'list' (lists all usable ents)\n" );
return;
}
else if ( !Q_stricmp("list", cmd1) )
{
gentity_t *ent;
gi.Printf("Listing all usable entities:\n");
for ( int i = 1; i < ENTITYNUM_WORLD; i++ )
{
ent = &g_entities[i];
if ( ent )
{
if ( ent->targetname && ent->targetname[0] )
{
if ( ent->e_UseFunc != useF_NULL )
{
if ( ent->NPC )
{
gi.Printf( "%s (NPC)\n", ent->targetname );
}
else
{
gi.Printf( "%s\n", ent->targetname );
}
}
}
}
}
gi.Printf("End of list.\n");
}
else
{
G_UseTargets2( &g_entities[0], &g_entities[0], cmd1 );
}
}
//======================================================
void G_SetActiveState(char *targetstring, qboolean actState)
{
gentity_t *target = NULL;
while( NULL != (target = G_Find(target, FOFS(targetname), targetstring)) )
{
target->svFlags = actState ? (target->svFlags&~SVF_INACTIVE) : (target->svFlags|SVF_INACTIVE);
}
}
void target_activate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
{
G_ActivateBehavior(self,BSET_USE);
G_SetActiveState(self->target, ACT_ACTIVE);
}
void target_deactivate_use(gentity_t *self, gentity_t *other, gentity_t *activator)
{
G_ActivateBehavior(self,BSET_USE);
G_SetActiveState(self->target, ACT_INACTIVE);
}
//FIXME: make these apply to doors, etc too?
/*QUAKED target_activate (1 0 0) (-4 -4 -4) (4 4 4)
Will set the target(s) to be usable/triggerable
*/
void SP_target_activate( gentity_t *self )
{
G_SetOrigin( self, self->s.origin );
self->e_UseFunc = useF_target_activate_use;
}
/*QUAKED target_deactivate (1 0 0) (-4 -4 -4) (4 4 4)
Will set the target(s) to be non-usable/triggerable
*/
void SP_target_deactivate( gentity_t *self )
{
G_SetOrigin( self, self->s.origin );
self->e_UseFunc = useF_target_deactivate_use;
}
//======================================================
/*
==============
ValidUseTarget
Returns whether or not the targeted entity is useable
==============
*/
qboolean ValidUseTarget( gentity_t *ent )
{
if ( ent->e_UseFunc == useF_NULL )
{
return qfalse;
}
if ( ent->svFlags & SVF_INACTIVE )
{//set by target_deactivate
return qfalse;
}
if ( !(ent->svFlags & SVF_PLAYER_USABLE) )
{//Check for flag that denotes BUTTON_USE useability
return qfalse;
}
//FIXME: This is only a temp fix..
if ( !strncmp( ent->classname, "trigger", 7) )
{
return qfalse;
}
return qtrue;
}
static qboolean G_ValidActivateBehavior (gentity_t* self, int bset)
{
if ( !self )
{
return qfalse;
}
const char *bs_name = self->behaviorSet[bset];
if( !(VALIDSTRING( bs_name )) )
{
return qfalse;
}
return qtrue;
}
static qboolean G_IsTriggerUsable(gentity_t* self, gentity_t* other)
{
if ( self->svFlags & SVF_INACTIVE )
{//set by target_deactivate
return qfalse;
}
if( self->noDamageTeam )
{
if ( other->client->playerTeam != self->noDamageTeam )
{
return qfalse;
}
}
if ( self->spawnflags & 4 )
{//USE_BUTTON
if ( !other->client )
{
return qfalse;
}
}
else
{
return qfalse;
}
if ( self->spawnflags & 2 )
{//FACING
vec3_t forward;
if ( other->client )
{
AngleVectors( other->client->ps.viewangles, forward, NULL, NULL );
}
else
{
AngleVectors( other->currentAngles, forward, NULL, NULL );
}
if ( DotProduct( self->movedir, forward ) < 0.5 )
{//Not Within 45 degrees
return qfalse;
}
}
if ((!G_ValidActivateBehavior (self, BSET_USE) && !self->target) ||
(self->target &&
(Q_stricmp(self->target, "n") == 0 ||
(Q_stricmp(self->target, "neveropen") == 0 ||
(Q_stricmp(self->target, "run_gran_drop") == 0) ||
(Q_stricmp(self->target, "speaker") == 0) ||
(Q_stricmp(self->target, "locked") == 0)
))))
{
return qfalse;
}
/*
//NOTE: This doesn't stop you from using it, just delays the use action!
if(self->delay && self->painDebounceTime < (level.time + self->delay) )
{
return qfalse;
}
*/
return qtrue;
}
static qboolean CanUseInfrontOfPartOfLevel(gentity_t* ent ) //originally from VV
{
int i, num;
gentity_t *touch[MAX_GENTITIES], *hit;
vec3_t mins, maxs;
const vec3_t range = { 40, 40, 52 };
if ( !ent->client ) {
return qfalse;
}
VectorSubtract( ent->client->ps.origin, range, mins );
VectorAdd( ent->client->ps.origin, range, maxs );
num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
// can't use ent->absmin, because that has a one unit pad
VectorAdd( ent->client->ps.origin, ent->mins, mins );
VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
for ( i=0 ; i<num ; i++ ) {
hit = touch[i];
if ( (hit->e_TouchFunc == touchF_NULL) && (ent->e_TouchFunc == touchF_NULL) ) {
continue;
}
if ( !( hit->contents & CONTENTS_TRIGGER ) ) {
continue;
}
if ( !gi.EntityContact( mins, maxs, hit ) ) {
continue;
}
if ( hit->e_TouchFunc != touchF_NULL ) {
switch (hit->e_TouchFunc )
{
case touchF_Touch_Multi:
if (G_IsTriggerUsable(hit, ent))
{
return qtrue;
}
continue;
break;
default:
continue;
}
}
}
return qfalse;
}
#define USE_DISTANCE 64.0f
qboolean CanUseInfrontOf(gentity_t *ent)
{
gentity_t *target;
trace_t trace;
vec3_t src, dest, vf;
if ( ent->s.number && ent->client->NPC_class == CLASS_ATST )
{//a player trying to get out of his ATST
// GEntity_UseFunc( ent->activator, ent, ent );
return qfalse;
}
if (ent->client->ps.viewEntity != ent->s.number)
{
ent = &g_entities[ent->client->ps.viewEntity];
if ( !Q_stricmp( "misc_camera", ent->classname ) )
{ // we are in a camera
gentity_t *next = 0;
if ( ent->target2 != NULL )
{
next = G_Find( NULL, FOFS(targetname), ent->target2 );
}
if ( next )
{//found another one
if ( !Q_stricmp( "misc_camera", next->classname ) )
{//make sure it's another camera
return qtrue;
}
}
else //if ( ent->health > 0 )
{//I was the last (only?) one, clear out the viewentity
return qfalse;
}
}
}
if ( !ent->client ) {
return qfalse;
}
// Check if player is standing on drivable AT-ST
if (ent->client->ps.groundEntityNum < ENTITYNUM_WORLD) {
target = &g_entities[ent->client->ps.groundEntityNum];
if (target && target->e_UseFunc == useF_misc_atst_use) {
return qtrue;
}
}
bool thirdPersonActive = gi.cvar("cg_thirdPerson", "0", CVAR_TEMP)->integer;
if (thirdPersonActive) {
VectorCopy(ent->currentOrigin, src);
AngleVectors(ent->currentAngles, vf, NULL, NULL);
} else {
VectorCopy( ent->client->renderInfo.eyePoint, src );
AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );
}
//extend to find end of use trace
VectorMA( src, USE_DISTANCE, vf, dest );
//Trace ahead to find a valid target
gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE , G2_NOCOLLIDE, 10);
if ( trace.fraction == 1.0f || trace.entityNum >= ENTITYNUM_WORLD )
{
return (CanUseInfrontOfPartOfLevel(ent));
}
target = &g_entities[trace.entityNum];
if ( target && target->client && target->client->NPC_class == CLASS_ATST )
{
// Attempt to board this vehicle.
return qtrue;
}
//Check for a use command
if (ValidUseTarget( target )) {
if ( target->s.eType == ET_ITEM )
{//item, see if we could actually pick it up
if ( (target->spawnflags&128/*ITMSF_USEPICKUP*/) )
{//player has to be touching me and hit use to pick it up, so don't allow this
if ( !G_BoundsOverlap( target->absmin, target->absmax, ent->absmin, ent->absmax ) )
{//not touching
return qfalse;
}
}
if ( !BG_CanItemBeGrabbed( &target->s, &ent->client->ps ) )
{//nope, so don't indicate that we can use it
return qfalse;
}
}
else if ( target->NPC!=NULL && target->health<=0 )
{
return qfalse;
}
return qtrue;
}
if ( target->client
&& target->client->ps.pm_type < PM_DEAD
&& target->NPC!=NULL
&& target->client->playerTeam
&& (target->client->playerTeam == ent->client->playerTeam || target->client->playerTeam == TEAM_NEUTRAL)
&& !(target->NPC->scriptFlags&SCF_NO_RESPONSE)
&& G_ValidActivateBehavior (target, BSET_USE))
{
return qtrue;
}
if (CanUseInfrontOfPartOfLevel(ent)) {
return qtrue;
}
return qfalse;
}
/*
==============
TryUse
Try and use an entity in the world, directly ahead of us
==============
*/
#define USE_DISTANCE_BUTTON 64.0f
#define USE_DISTANCE_GESTURE 26.0f
// Move controller origin a bit back to prevent reach
// through usable entities when use gesture is active
#define USE_OFFSET -10.0f
void TryUse_Internal( bool offHand, gentity_t *ent, vec3_t src, vec3_t vf ) {
gentity_t *target;
trace_t trace;
vec3_t dest;
// Drivable AT-ST can be used if player stands on it
if (ent->client->ps.groundEntityNum < ENTITYNUM_WORLD) {
target = &g_entities[ent->client->ps.groundEntityNum];
if (target && target->e_UseFunc == useF_misc_atst_use) {
GEntity_UseFunc( target, ent, ent );
return;
}
}
//extend to find end of use trace
bool thirdPersonActive = gi.cvar("cg_thirdPerson", "0", CVAR_TEMP)->integer;
bool useGestureEnabled = gi.cvar("vr_gesture_triggered_use", "1", CVAR_ARCHIVE)->integer; // defined in VrCvars.h
bool useGestureAllowed = useGestureEnabled && !thirdPersonActive && !vr->remote_droid;
float useOffset = useGestureAllowed ? USE_OFFSET : 0.0f;
float useDistance = useGestureAllowed ? USE_DISTANCE_GESTURE : USE_DISTANCE_BUTTON;
VectorMA( src, useOffset, vf, src );
VectorMA( src, useDistance, vf, dest );
//Trace ahead to find a valid target
gi.trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE, G2_NOCOLLIDE, 0 );
if ( trace.fraction == 1.0f || trace.entityNum < 1 )
{
//TODO: Play a failure sound
/*
if ( ent->s.number == 0 )
{//if nothing else, try the force telepathy power
ForceTelepathy( ent );
}
*/
return;
}
target = &g_entities[trace.entityNum];
//Check for a use command
if ( ValidUseTarget( target ) )
{
NPC_SetAnim( ent, SETANIM_TORSO, BOTH_BUTTON_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
/*
if ( !VectorLengthSquared( ent->client->ps.velocity ) && !PM_CrouchAnim( ent->client->ps.legsAnim ) )
{
NPC_SetAnim( ent, SETANIM_LEGS, BOTH_BUTTON_HOLD, SETANIM_FLAG_NORMAL|SETANIM_FLAG_HOLD );
}
*/
//ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer;
if (ent->client->ps.clientNum == 0) {
int channel = vr->right_handed != offHand ? 1 : 2;
if (level.time > vr->useHapticFeedbackTime[channel - 1]) {
cgi_HapticEvent("use_button", 0, channel, 60, 0, 0);
vr->useHapticFeedbackTime[channel - 1] = level.time + + USE_HAPTIC_FEEDBACK_DELAY;
}
}
GEntity_UseFunc( target, ent, ent );
return;
}
else if ( target->client
&& target->client->ps.pm_type < PM_DEAD
&& target->NPC!=NULL
&& target->client->playerTeam
&& (target->client->playerTeam == ent->client->playerTeam || target->client->playerTeam == TEAM_NEUTRAL)
&& !(target->NPC->scriptFlags&SCF_NO_RESPONSE) )
{
if (ent->client->ps.clientNum == 0) {
int channel = vr->right_handed != offHand ? 1 : 2;
if (level.time > vr->useHapticFeedbackTime[channel - 1]) {
cgi_HapticEvent("use_button", 0, channel, 60, 0, 0);
vr->useHapticFeedbackTime[channel - 1] = level.time + + USE_HAPTIC_FEEDBACK_DELAY;
}
}
NPC_UseResponse ( target, ent, qfalse );
return;
}
/*
if ( ent->s.number == 0 )
{//if nothing else, try the force telepathy power
ForceTelepathy( ent );
}
*/
}
void TryUse( gentity_t *ent ) {
if (ent->s.number == 0 &&
ent->client->NPC_class == CLASS_ATST) {//a player trying to get out of his ATST
GEntity_UseFunc(ent->activator, ent, ent);
return;
}
bool thirdPersonActive = gi.cvar("cg_thirdPerson", "0", CVAR_TEMP)->integer;
vec3_t src, angles, vf;
if (ent->client->ps.clientNum == 0 && !(vr && vr->remote_droid)) {
if (thirdPersonActive) {
VectorCopy(ent->currentOrigin, src);
AngleVectors(ent->currentAngles, vf, NULL, NULL);
TryUse_Internal(false, ent, src, vf);
} else {
BG_CalculateVRWeaponPosition(src, angles);
AngleVectors( angles, vf, NULL, NULL );
TryUse_Internal(false, ent, src, vf);
}
} else {
VectorCopy(ent->client->renderInfo.eyePoint, src);
AngleVectors(ent->client->ps.viewangles, vf, NULL, NULL);
TryUse_Internal(false, ent, src, vf);
}
}
void TryAltUse( gentity_t *ent ) {
if (ent->s.number == 0 &&
ent->client->NPC_class == CLASS_ATST) {//a player trying to get out of his ATST
GEntity_UseFunc(ent->activator, ent, ent);
return;
}
vec3_t src, angles, vf;
if (ent->client->ps.clientNum == 0) {
BG_CalculateVROffHandPosition(src, angles);
AngleVectors( angles, vf, NULL, NULL );
TryUse_Internal(true, ent, src, vf);
} else {
VectorCopy(ent->client->renderInfo.eyePoint, src);
AngleVectors(ent->client->ps.viewangles, vf, NULL, NULL);
TryUse_Internal(false, ent, src, vf);
}
}
extern int killPlayerTimer;
void G_ChangeMap (const char *mapname, const char *spawntarget, qboolean hub)
{
// gi.Printf("Loading...");
//ignore if player is dead
if (g_entities[0].client->ps.pm_type == PM_DEAD)
return;
if ( killPlayerTimer )
{//can't go to next map if your allies have turned on you
return;
}
if ( spawntarget == NULL ) {
spawntarget = ""; //prevent it from becoming "(null)"
}
if ( hub == qtrue )
{
gi.SendConsoleCommand( va("loadtransition %s %s\n", mapname, spawntarget) );
}
else
{
gi.SendConsoleCommand( va("maptransition %s %s\n", mapname, spawntarget) );
}
}
qboolean G_PointInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs )
{
for(int i = 0; i < 3; i++ )
{
if ( point[i] < mins[i] )
{
return qfalse;
}
if ( point[i] > maxs[i] )
{
return qfalse;
}
}
return qtrue;
}
qboolean G_BoxInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs, const vec3_t boundsMins, const vec3_t boundsMaxs )
{
vec3_t boxMins;
vec3_t boxMaxs;
VectorAdd( point, mins, boxMins );
VectorAdd( point, maxs, boxMaxs );
if(boxMaxs[0]>boundsMaxs[0])
return qfalse;
if(boxMaxs[1]>boundsMaxs[1])
return qfalse;
if(boxMaxs[2]>boundsMaxs[2])
return qfalse;
if(boxMins[0]<boundsMins[0])
return qfalse;
if(boxMins[1]<boundsMins[1])
return qfalse;
if(boxMins[2]<boundsMins[2])
return qfalse;
//box is completely contained within bounds
return qtrue;
}
void G_SetAngles( gentity_t *ent, const vec3_t angles )
{
VectorCopy( angles, ent->currentAngles );
VectorCopy( angles, ent->s.angles );
VectorCopy( angles, ent->s.apos.trBase );
}
qboolean G_ClearTrace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int ignore, int clipmask )
{
static trace_t tr;
gi.trace( &tr, start, mins, maxs, end, ignore, clipmask, G2_NOCOLLIDE, 0 );
if ( tr.allsolid || tr.startsolid || tr.fraction < 1.0 )
{
return qfalse;
}
return qtrue;
}
extern void CG_TestLine( vec3_t start, vec3_t end, int time, unsigned int color, int radius);
void G_DebugLine(vec3_t A, vec3_t B, int duration, int color, qboolean deleteornot)
{
/*
gentity_t *tent = G_TempEntity( A, EV_DEBUG_LINE );
VectorCopy(B, tent->s.origin2 );
tent->s.time = duration; // Pause
tent->s.time2 = color; // Color
tent->s.weapon = 1; // Dimater
tent->freeAfterEvent = deleteornot;
*/
CG_TestLine( A, B, duration, color, 1 );
}
qboolean G_ExpandPointToBBox( vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask )
{
trace_t tr;
vec3_t start, end;
VectorCopy( point, start );
for ( int i = 0; i < 3; i++ )
{
VectorCopy( start, end );
end[i] += mins[i];
gi.trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask, G2_NOCOLLIDE, 0 );
if ( tr.allsolid || tr.startsolid )
{
return qfalse;
}
if ( tr.fraction < 1.0 )
{
VectorCopy( start, end );
end[i] += maxs[i]-(mins[i]*tr.fraction);
gi.trace( &tr, start, vec3_origin, vec3_origin, end, ignore, clipmask, G2_NOCOLLIDE, 0 );
if ( tr.allsolid || tr.startsolid )
{
return qfalse;
}
if ( tr.fraction < 1.0 )
{
return qfalse;
}
VectorCopy( end, start );
}
}
//expanded it, now see if it's all clear
gi.trace( &tr, start, mins, maxs, start, ignore, clipmask, G2_NOCOLLIDE, 0 );
if ( tr.allsolid || tr.startsolid )
{
return qfalse;
}
VectorCopy( start, point );
return qtrue;
}
/*
Ghoul2 Insert Start
*/
void removeBoltSurface( gentity_t *ent)
{
gentity_t *hitEnt = &g_entities[ent->cantHitEnemyCounter];
// check first to be sure the bolt is still there on the model
if ((hitEnt->ghoul2.size() > ent->damage) &&
(hitEnt->ghoul2[ent->damage].mModelindex != -1) &&
((int)hitEnt->ghoul2[ent->damage].mSlist.size() > ent->aimDebounceTime) &&
(hitEnt->ghoul2[ent->damage].mSlist[ent->aimDebounceTime].surface != -1) &&
(hitEnt->ghoul2[ent->damage].mSlist[ent->aimDebounceTime].offFlags == G2SURFACEFLAG_GENERATED))
{
// remove the bolt
gi.G2API_RemoveBolt(&hitEnt->ghoul2[ent->damage], ent->attackDebounceTime);
// now remove a surface if there is one
if (ent->aimDebounceTime != -1)
{
gi.G2API_RemoveSurface(&hitEnt->ghoul2[ent->damage], ent->aimDebounceTime);
}
}
// we are done with this entity.
G_FreeEntity(ent);
}
void G_SetBoltSurfaceRemoval( const int entNum, const int modelIndex, const int boltIndex, const int surfaceIndex , float duration ) {
gentity_t *e;
vec3_t snapped = {0,0,0};
e = G_Spawn();
e->classname = "BoltRemoval";
e->cantHitEnemyCounter = entNum;
e->damage = modelIndex;
e->attackDebounceTime = boltIndex;
e->aimDebounceTime = surfaceIndex;
G_SetOrigin( e, snapped );
// find cluster for PVS
gi.linkentity( e );
e->nextthink = level.time + duration;
e->e_ThinkFunc = thinkF_removeBoltSurface;
}
/*
Ghoul2 Insert End
*/