mirror of
https://github.com/DrBeef/JKXR.git
synced 2025-05-07 16:51:17 +00:00
commit0c85ac4704
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sat Apr 15 18:47:10 2023 +0200 Menu updates commit35b89acc87
Author: Simon <simonbrown77@googlemail.com> Date: Sat Apr 15 12:32:35 2023 +0100 Support for changeable fresh rate commit539bfa8956
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 commit216a225aa6
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Fri Apr 14 22:41:24 2023 +0200 Fix storing of force crosshair option commit48c2486aad
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 commita72ca459c5
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 commit32c4e6eac1
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 commitfcb9169955
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Fri Apr 14 18:58:32 2023 +0200 Increased default force motion trigger commit2433ae4110
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Fri Apr 14 18:55:56 2023 +0200 Change Force Crosshair commit4ab6a6024a
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Fri Apr 14 17:01:16 2023 +0200 Fixed player knockback.... hopefully commit7deeee7a6f
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Fri Apr 14 17:01:00 2023 +0200 Increased Force Power Visibility commit1fdcb7c48f
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Fri Apr 14 15:16:34 2023 +0200 Changed Brightness Range and Default commitfada09a0bb
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Apr 13 20:38:13 2023 +0200 Fix remote turret on-screen help commit82af313786
Merge:7680cb1
d13176c
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Apr 13 18:59:46 2023 +0200 Merge branch 'main' into contributions commitd13176c9ba
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 13 13:29:49 2023 +0200 New Quick Save / Load Video commit7680cb1288
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 commit4d90595139
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 12 18:21:10 2023 +0200 Fencing Speed on Pico default commit37d5ac4184
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 commitd6e40ead64
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 12 17:44:29 2023 +0200 Set Fencing Speed back to Default (by Default) commit3f7d116e25
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 commit7424628a5d
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 12 14:50:23 2023 +0200 Updated Weapons + Shader fix for FX mod commitc7c66e46a7
Author: Simon <simonbrown77@googlemail.com> Date: Wed Apr 12 08:37:36 2023 +0100 Revert "Arrange quick save icons horizontally" This reverts commit20f8fff3fe
. Also make the icons white commitfa54045159
Author: Simon <simonbrown77@googlemail.com> Date: Tue Apr 11 21:02:29 2023 +0100 Update .gitignore commit20f8fff3fe
Author: Simon <simonbrown77@googlemail.com> Date: Tue Apr 11 21:00:09 2023 +0100 Arrange quick save icons horizontally commitd3dcac8f9d
Author: Simon <simonbrown77@googlemail.com> Date: Tue Apr 11 20:58:56 2023 +0100 Save game crash fix in JKO commit6314561b52
Author: Simon <simonbrown77@googlemail.com> Date: Tue Apr 11 08:49:42 2023 +0100 Switch selector on offhand using offhand thumbstick commit3b2ffd7289
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 commitc135eefa05
Author: Simon <simonbrown77@googlemail.com> Date: Tue Apr 11 08:38:54 2023 +0100 Fixed save/load icon image size commitf51270a294
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 commita2ff16b576
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! commite243d0bdfa
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Mon Apr 10 18:29:18 2023 +0200 Update README.md commitb9d0314a6a
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sun Apr 9 19:38:32 2023 +0200 Make getting into AT-ST easier commitd121206f83
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. commit260d501776
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sat Apr 8 22:34:59 2023 +0200 Fix and improve weapon adjustment helper axes commita6318867bb
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 6 20:35:02 2023 +0200 Update README.md commit4a1d90e729
Merge:425db0f
821a56d
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Apr 6 19:12:07 2023 +0200 Merge branch 'main' into contributions commit821a56d4b9
Author: Simon Brown <simonbrown77@googlemail.com> Date: Thu Apr 6 17:24:20 2023 +0100 Update README.md commit67b7d26de8
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 commitb407932bb2
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 6 17:43:26 2023 +0200 Update README.md commit174c3ce96c
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 6 17:33:33 2023 +0200 Update README.md commite9af8e87c2
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 6 17:23:36 2023 +0200 Update README.md commitccd63d4eec
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 6 16:57:36 2023 +0200 Forced for Extended Menu commitd6b3e8eb65
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Thu Apr 6 16:55:02 2023 +0200 TBDC Scales in Code commita5f3adf725
Author: Simon Brown <simonbrown77@googlemail.com> Date: Thu Apr 6 15:04:07 2023 +0100 Update README.md commit316c2a2904
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 commit387c34b53e
Author: Simon <simonbrown77@googlemail.com> Date: Thu Apr 6 09:59:18 2023 +0100 Update Beef Crawl commita271b61ac7
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 commita2f1644d72
Author: Simon <simonbrown77@googlemail.com> Date: Thu Apr 6 08:39:02 2023 +0100 Update version to 1.0.0 for release commitf785fdc393
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 commit90b694ff60
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 5 22:21:41 2023 +0200 Last Cleanup commit70468332d6
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 5 22:19:45 2023 +0200 TBDC Cleanup commit6660e8c984
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 5 21:58:00 2023 +0200 Credits / NPC Scale commite1b03fdcc6
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 5 21:57:07 2023 +0200 Fixing Quick Save commit95950f2390
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 5 20:08:10 2023 +0200 Updated ratios per difficulty commitd6235ef199
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Wed Apr 5 00:30:35 2023 +0200 Darkened Menu GFX commita3fdb460c4
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. commit4c751fcb59
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Tue Apr 4 23:59:12 2023 +0200 Y Close Datapad commita1216665c8
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 commit1939a26542
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Tue Apr 4 23:37:41 2023 +0200 TBDC Laser Saber deflections Guns balancing. commitd841464924
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Apr 4 21:59:31 2023 +0200 Update help resources and menu commit425db0f108
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Apr 4 21:59:31 2023 +0200 Update help resources and menu commit3c895d27de
Merge:fe9e12a
c3819fa
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Apr 4 21:05:48 2023 +0200 Merge branch 'main' into contributions commitc3819fa407
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Mon Apr 3 00:36:57 2023 +0200 Demo folder assets fix commitfe9e12a3f9
Merge:d79f57b
2b255ac
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Mon Apr 3 22:55:29 2023 +0200 Merge branch 'main' into contributions commit2b255ac3ea
Author: Simon <simonbrown77@googlemail.com> Date: Mon Apr 3 21:26:39 2023 +0100 Improved version string commit62284414d0
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 commit65674abe2a
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Mon Apr 3 21:36:35 2023 +0200 Fix Controller Location Buzz for Quick Save / Load commitba2e726a79
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Mon Apr 3 00:36:57 2023 +0200 Demo folder assets fix commit7175c872a9
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Mon Apr 3 00:36:46 2023 +0200 Knockback settings TBDC commitcc7e73d04a
Author: Simon <simonbrown77@googlemail.com> Date: Sun Apr 2 22:59:47 2023 +0100 Update beef_crawl.tga commitdd71a05e36
Author: Simon <simonbrown77@googlemail.com> Date: Sun Apr 2 22:51:49 2023 +0100 Put version at the bottom of the main menu commitcb52d310c1
Author: Grant Bagwell <general@grantbagwell.co.uk> Date: Sun Apr 2 23:18:28 2023 +0200 TBDC Weapons commitac71f24e78
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sun Apr 2 18:43:10 2023 +0200 Updated control scheme picture commit3f5c767e8d
Author: Simon <simonbrown77@googlemail.com> Date: Sun Apr 2 19:42:42 2023 +0100 Update AndroidManifest.xml commit64ab392fc5
Author: Simon <simonbrown77@googlemail.com> Date: Sun Apr 2 19:42:39 2023 +0100 JKA: Some CVAR change to increase performance commitd43de53e8a
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 commit8b23e255d8
Author: Simon <simonbrown77@googlemail.com> Date: Sun Apr 2 19:41:16 2023 +0100 Update open xr headers commitd79f57ba2d
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sun Apr 2 18:43:10 2023 +0200 Updated control scheme picture commite3524e8e48
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 commit05fc4d5ab4
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sat Apr 1 12:07:13 2023 +0200 Fix menu haptics commiteec46c183d
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 commitfe9891f8db
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sat Apr 1 12:07:13 2023 +0200 Fix menu haptics commit21483d0d5a
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 commitbaec8832ab
Merge:82706df
52fcc8a
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Sat Apr 1 11:15:25 2023 +0200 Merge branch 'main' into contributions commit52fcc8a49e
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 commitb899b99178
. commit5e66ebf6fc
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Fri Mar 31 15:40:54 2023 +0200 Allow to skip cinematics also by triggers commit587277fa7f
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Fri Mar 31 14:52:40 2023 +0200 Fix use/crouch buttons on switched control schemes commit82706df34f
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Fri Mar 31 15:40:54 2023 +0200 Allow to skip cinematics also by triggers commit1111766032
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Fri Mar 31 14:52:40 2023 +0200 Fix use/crouch buttons on switched control schemes commit822d1ddbba
Merge:a1a7d54
5f56cf4
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Fri Mar 31 08:43:50 2023 +0200 Merge branch 'main' into contributions commit5f56cf48fc
Author: Simon <simonbrown77@googlemail.com> Date: Thu Mar 30 22:16:37 2023 +0100 Camera shake fix part 2 commit3dd7833cd0
Author: Simon <simonbrown77@googlemail.com> Date: Thu Mar 30 22:06:18 2023 +0100 Disable camera shake when charging a weapon's alt fire commit6202017b6a
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 21:52:44 2023 +0200 Fix virtual gun stock commit6d49f87150
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 19:26:36 2023 +0200 Do not check angle on non-facing triggers commit926c64c691
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 17:54:33 2023 +0200 Add angle check for triggers touched by hand commit78f7d9bcfc
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Wed Mar 29 22:59:27 2023 +0200 JKA mod menu warning commit4219f996e9
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Wed Mar 29 22:42:11 2023 +0200 JKO mod menu warning commitb899b99178
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 commit9877859676
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 commita1a7d541fa
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 21:52:44 2023 +0200 Fix virtual gun stock commit15d932c75f
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 19:26:36 2023 +0200 Do not check angle on non-facing triggers commit402277e717
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 17:54:33 2023 +0200 Add angle check for triggers touched by hand commit9b22378c88
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Thu Mar 30 17:52:26 2023 +0200 Add special delay for re-triggering security cameras commitc4cc218f8b
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 commita4e99c20f9
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Wed Mar 29 22:59:27 2023 +0200 JKA mod menu warning commit442a1fc8e2
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Wed Mar 29 22:42:11 2023 +0200 JKO mod menu warning commit02261c57f0
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 commit3c64fa7e3e
Author: Simon <simonbrown77@googlemail.com> Date: Tue Mar 28 23:19:29 2023 +0100 Lowered Stun Baton trigger velocity commit43192a355d
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 commit94e82c2da3
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 commit40128567be
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Mar 28 18:02:08 2023 +0200 Add help to menu commit4f1b6b5f07
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Mar 28 17:36:13 2023 +0200 Split vr asset packs to avoid duplicates commit51c703a481
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 commit04a539f890
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Mar 28 18:02:08 2023 +0200 Add help to menu commitd9126738d3
Author: Petr Bartos <petr.bartos@plus4u.net> Date: Tue Mar 28 17:36:13 2023 +0200 Split vr asset packs to avoid duplicates commit62b9a0bfab
Author: Simon <simonbrown77@googlemail.com> Date: Tue Mar 28 13:52:45 2023 +0100 Hide force power aura when item selector is shown commit0a9206642e
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 commitf452b9cf06
Author: Simon <simonbrown77@googlemail.com> Date: Tue Mar 28 13:51:49 2023 +0100 Slight tweak to NPC Combat commit20de6ec478
Author: Simon <simonbrown77@googlemail.com> Date: Tue Mar 28 13:50:54 2023 +0100 Added simple readme and license commit77672f612b
Author: Simon <simonbrown77@googlemail.com> Date: Tue Mar 28 07:20:22 2023 +0100 Updated website link to be the patreon commit2a1cd0e6e7
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 commit1b22652e5c
Author: Simon <simonbrown77@googlemail.com> Date: Mon Mar 27 21:38:46 2023 +0100 Fixed aiming of bowcaster and demp alt fire commit895b09041f
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 commit3897531544
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 commit3b5121e349
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 commit50db9039df
Author: Simon <simonbrown77@googlemail.com> Date: Sun Mar 26 16:33:52 2023 +0100 Separate no copy file flag for JK3 commit1968a7d8ba
Author: Simon <simonbrown77@googlemail.com> Date: Sun Mar 26 16:33:22 2023 +0100 Always use right hand as saber home commit74dcd955d2
Author: Simon <simonbrown77@googlemail.com> Date: Sun Mar 26 16:32:49 2023 +0100 Fix crash in JKA commit1e4692d04a
Author: Simon <simonbrown77@googlemail.com> Date: Sun Mar 26 16:32:16 2023 +0100 Update z_Crusty_and_Elin_vr_weapons.pk3 commit134dec8264
Author: Simon <simonbrown77@googlemail.com> Date: Sun Mar 26 16:31:53 2023 +0100 Fix possible crash on JK2 commitf6dc432f6a
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
3089 lines
79 KiB
C++
3089 lines
79 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/>.
|
|
===========================================================================
|
|
*/
|
|
|
|
// cg_draw.c -- draw all of the graphical elements during
|
|
// active (after loading) gameplay
|
|
|
|
#include "../game/g_local.h"
|
|
#include "cg_local.h"
|
|
#include "cg_media.h"
|
|
#include "../game/objectives.h"
|
|
#include "bg_local.h"
|
|
#include <JKXR/VrClientInfo.h>
|
|
|
|
#include "FxUtil.h"
|
|
|
|
void CG_DrawIconBackground(void);
|
|
void CG_DrawMoveSpeedIcon(void);
|
|
void CG_DrawMissionInformation( void );
|
|
void CG_DrawInventorySelect( void );
|
|
void CG_DrawForceSelect( void );
|
|
qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y);
|
|
qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y);
|
|
extern float g_crosshairEntDist;
|
|
extern int g_crosshairSameEntTime;
|
|
extern int g_crosshairEntNum;
|
|
extern int g_crosshairEntTime;
|
|
qboolean cg_forceCrosshair = qfalse;
|
|
|
|
// bad cheating
|
|
extern int g_rocketLockEntNum;
|
|
extern int g_rocketLockTime;
|
|
extern int g_rocketSlackTime;
|
|
|
|
vec3_t vfwd;
|
|
vec3_t vright;
|
|
vec3_t vup;
|
|
vec3_t vfwd_n;
|
|
vec3_t vright_n;
|
|
vec3_t vup_n;
|
|
int infoStringCount;
|
|
|
|
//===============================================================
|
|
|
|
|
|
/*
|
|
================
|
|
CG_Draw3DModel
|
|
|
|
================
|
|
*/
|
|
static void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) {
|
|
refdef_t refdef;
|
|
refEntity_t ent;
|
|
|
|
memset( &refdef, 0, sizeof( refdef ) );
|
|
|
|
memset( &ent, 0, sizeof( ent ) );
|
|
AnglesToAxis( angles, ent.axis );
|
|
VectorCopy( origin, ent.origin );
|
|
ent.hModel = model;
|
|
ent.customSkin = skin;
|
|
ent.renderfx = RF_NOSHADOW; // no stencil shadows
|
|
|
|
refdef.rdflags = RDF_NOWORLDMODEL;
|
|
|
|
AxisClear( refdef.viewaxis );
|
|
|
|
refdef.override_fov = true;
|
|
refdef.fov_x = 30;
|
|
refdef.fov_y = 30;
|
|
|
|
refdef.x = x;
|
|
refdef.y = y;
|
|
refdef.width = w;
|
|
refdef.height = h;
|
|
|
|
refdef.time = cg.time;
|
|
|
|
cgi_R_ClearScene();
|
|
cgi_R_AddRefEntityToScene( &ent );
|
|
cgi_R_RenderScene( &refdef );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawHead
|
|
|
|
Used for both the status bar and the scoreboard
|
|
================
|
|
*/
|
|
void CG_DrawHead( float x, float y, float w, float h, int speaker_i, vec3_t headAngles )
|
|
{
|
|
qhandle_t hm = 0;
|
|
qhandle_t hs = 0;
|
|
float len;
|
|
vec3_t origin;
|
|
vec3_t mins, maxs;
|
|
gentity_t *ent;
|
|
qboolean extensions = qfalse;
|
|
int talking = 0;
|
|
|
|
//If the talking ent is actually on the level, use his info
|
|
if ( cg.gameTextEntNum != -1 && cg.gameTextEntNum < ENTITYNUM_WORLD )
|
|
{
|
|
ent = &g_entities[cg.gameTextEntNum];
|
|
if ( ent && ent->client )
|
|
{
|
|
hm = ent->client->clientInfo.headModel;
|
|
if ( hm )
|
|
{
|
|
hs = ent->client->clientInfo.headSkin;
|
|
extensions = ent->client->clientInfo.extensions;
|
|
talking = gi.VoiceVolume[ent->s.number];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !hm )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !talking )
|
|
{//no sound playing, don't display the head any more
|
|
cg.gameNextTextTime = cg.time;
|
|
return;
|
|
}
|
|
|
|
//add talking anim
|
|
if ( extensions && talking > 0 )
|
|
{
|
|
hs = hs + talking;
|
|
}
|
|
|
|
// offset the origin y and z to center the head
|
|
cgi_R_ModelBounds( hm, mins, maxs );
|
|
|
|
origin[2] = -0.5 * ( mins[2] + maxs[2] );
|
|
origin[1] = 0.5 * ( mins[1] + maxs[1] );
|
|
|
|
// calculate distance so the head nearly fills the box
|
|
// assume heads are taller than wide
|
|
len = 0.7 * ( maxs[2] - mins[2] );
|
|
origin[0] = len / 0.268; // len / tan( fov/2 )
|
|
|
|
CG_Draw3DModel( x, y, w, h, hm, hs, origin, headAngles );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawTalk
|
|
|
|
================
|
|
*/
|
|
static void CG_DrawTalk(centity_t *cent)
|
|
{
|
|
float size;
|
|
vec3_t angles;
|
|
// int totalLines,y,i;
|
|
vec4_t color;
|
|
|
|
if ( cg.gameNextTextTime > cg.time)
|
|
{
|
|
color[0] = colorTable[CT_BLACK][0];
|
|
color[1] = colorTable[CT_BLACK][1];
|
|
color[2] = colorTable[CT_BLACK][2];
|
|
color[3] = 0.350F;
|
|
|
|
cgi_R_SetColor(color); // Background
|
|
CG_DrawPic( 5, 27, 50, 64, cgs.media.ammoslider );
|
|
|
|
cgi_R_SetColor(colorTable[CT_LTPURPLE1]);
|
|
CG_DrawPic( 5, 6, 128, 64, cgs.media.talkingtop );
|
|
/*
|
|
totalLines = cg.scrollTextLines - cg.gameTextCurrentLine;
|
|
|
|
y = 6;
|
|
CG_DrawPic( 55, y, 16, 16, cgs.media.bracketlu );
|
|
CG_DrawPic( 616, y, 16, 16, cgs.media.bracketru );
|
|
|
|
for (i=1;i<totalLines;++i)
|
|
{
|
|
y +=16;
|
|
CG_DrawPic( 55, y, 16, 16, cgs.media.ammoslider );
|
|
CG_DrawPic( 616,y, 16, 16, cgs.media.ammoslider );
|
|
}
|
|
|
|
y +=16;
|
|
CG_DrawPic( 55, y, 16, 16, cgs.media.bracketld );
|
|
CG_DrawPic( 616,y, 16, 16, cgs.media.bracketrd );
|
|
*/
|
|
size = ICON_SIZE * 1.5;
|
|
VectorClear( angles );
|
|
angles[YAW] = 180;
|
|
|
|
|
|
CG_DrawHead( -6, 25, size, size, cg.gameTextSpeaker, angles );
|
|
|
|
cgi_R_SetColor(colorTable[CT_LTPURPLE1]); // Bottom
|
|
CG_DrawPic( 5, 90, 64, 16, cgs.media.talkingbot );
|
|
cgi_R_SetColor(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
int cgi_UI_GetMenuInfo(char *menuFile,int *x,int *y);
|
|
|
|
/*
|
|
================
|
|
CG_DrawHUDRightFrame1
|
|
================
|
|
*/
|
|
static void CG_DrawHUDRightFrame1(int x,int y)
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
// Inner gray wire frame
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDInnerRight ); //
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawHUDRightFrame2
|
|
================
|
|
*/
|
|
static void CG_DrawHUDRightFrame2(int x,int y)
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDRightFrame ); // Metal frame
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawMessageLit
|
|
================
|
|
*/
|
|
static void CG_DrawMessageLit(centity_t *cent,int x,int y)
|
|
{
|
|
cgi_R_SetColor(colorTable[CT_WHITE]);
|
|
|
|
if (cg.missionInfoFlashTime > cg.time )
|
|
{
|
|
if (!((cg.time / 600 ) & 1))
|
|
{
|
|
if (!cg.messageLitActive)
|
|
{
|
|
cgi_S_StartSound( NULL, 0, CHAN_AUTO, cgs.media.messageLitSound );
|
|
cg.messageLitActive = qtrue;
|
|
}
|
|
|
|
cgi_R_SetColor(colorTable[CT_HUD_RED]);
|
|
CG_DrawPic( x + 33,y + 41, 16,16, cgs.media.messageLitOn);
|
|
}
|
|
else
|
|
{
|
|
cg.messageLitActive = qfalse;
|
|
}
|
|
}
|
|
|
|
cgi_R_SetColor(colorTable[CT_WHITE]);
|
|
CG_DrawPic( x + 33,y + 41, 16,16, cgs.media.messageLitOff);
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawForcePower
|
|
================
|
|
*/
|
|
static void CG_DrawForcePower(centity_t *cent,int x,int y)
|
|
{
|
|
int i;
|
|
vec4_t calcColor;
|
|
float value,extra=0,inc,percent;
|
|
|
|
if ( !cent->gent->client->ps.forcePowersKnown )
|
|
{
|
|
return;
|
|
}
|
|
inc = (float) cent->gent->client->ps.forcePowerMax / MAX_TICS;
|
|
value = cent->gent->client->ps.forcePower;
|
|
if ( value > cent->gent->client->ps.forcePowerMax )
|
|
{//supercharged with force
|
|
extra = value - cent->gent->client->ps.forcePowerMax;
|
|
value = cent->gent->client->ps.forcePowerMax;
|
|
}
|
|
|
|
for (i=MAX_TICS-1;i>=0;i--)
|
|
{
|
|
if ( extra )
|
|
{//supercharged
|
|
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
|
|
percent = 0.75f + (sin( cg.time * 0.005f )*((extra/cent->gent->client->ps.forcePowerMax)*0.25f));
|
|
calcColor[0] *= percent;
|
|
calcColor[1] *= percent;
|
|
calcColor[2] *= percent;
|
|
}
|
|
else if ( value <= 0 ) // partial tic
|
|
{
|
|
memcpy(calcColor, colorTable[CT_BLACK], sizeof(vec4_t));
|
|
}
|
|
else if (value < inc) // partial tic
|
|
{
|
|
memcpy(calcColor, colorTable[CT_LTGREY], sizeof(vec4_t));
|
|
percent = value / inc;
|
|
calcColor[0] *= percent;
|
|
calcColor[1] *= percent;
|
|
calcColor[2] *= percent;
|
|
}
|
|
else
|
|
{
|
|
memcpy(calcColor, colorTable[CT_LTGREY], sizeof(vec4_t));
|
|
}
|
|
|
|
cgi_R_SetColor( calcColor);
|
|
CG_DrawPic( x + forceTicPos[i].x,
|
|
y + forceTicPos[i].y,
|
|
forceTicPos[i].width,
|
|
forceTicPos[i].height,
|
|
forceTicPos[i].tic );
|
|
|
|
value -= inc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawAmmo
|
|
================
|
|
*/
|
|
static void CG_DrawAmmo(centity_t *cent,int x,int y)
|
|
{
|
|
playerState_t *ps;
|
|
int numColor_i;
|
|
int i;
|
|
vec4_t calcColor;
|
|
float value,inc,percent;
|
|
|
|
ps = &cg.snap->ps;
|
|
|
|
if (!cent->currentState.weapon ) // We don't have a weapon right now
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cent->currentState.weapon == WP_STUN_BATON )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cent->currentState.weapon == WP_SABER && cent->gent )
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
|
|
if ( !cg.saberAnimLevelPending && cent->gent->client )
|
|
{//uninitialized after a loadgame, cheat across and get it
|
|
cg.saberAnimLevelPending = cent->gent->client->ps.saberAnimLevel;
|
|
}
|
|
// don't need to draw ammo, but we will draw the current saber style in this window
|
|
switch ( cg.saberAnimLevelPending )
|
|
{
|
|
case 1://FORCE_LEVEL_1:
|
|
case 5://FORCE_LEVEL_5://Tavion
|
|
CG_DrawPic( x, y, 80, 40, cgs.media.HUDSaberStyleFast );
|
|
break;
|
|
case 2://FORCE_LEVEL_2:
|
|
CG_DrawPic( x, y, 80, 40, cgs.media.HUDSaberStyleMed );
|
|
break;
|
|
case 3://FORCE_LEVEL_3:
|
|
case 4://FORCE_LEVEL_4://Desann
|
|
CG_DrawPic( x, y, 80, 40, cgs.media.HUDSaberStyleStrong );
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
value = ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
|
|
}
|
|
|
|
if (value < 0) // No ammo
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// ammo
|
|
//
|
|
if (cg.oldammo < value)
|
|
{
|
|
cg.oldAmmoTime = cg.time + 200;
|
|
}
|
|
|
|
cg.oldammo = value;
|
|
|
|
// Firing or reloading?
|
|
if (( cg.predicted_player_state.weaponstate == WEAPON_FIRING
|
|
&& cg.predicted_player_state.weaponTime > 100 ))
|
|
{
|
|
numColor_i = CT_LTGREY;
|
|
}
|
|
else
|
|
{
|
|
if ( value > 0 )
|
|
{
|
|
if (cg.oldAmmoTime > cg.time)
|
|
{
|
|
numColor_i = CT_YELLOW;
|
|
}
|
|
else
|
|
{
|
|
numColor_i = CT_HUD_ORANGE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
numColor_i = CT_RED;
|
|
}
|
|
}
|
|
|
|
cgi_R_SetColor( colorTable[numColor_i] );
|
|
CG_DrawNumField(x + 29, y + 26, 3, value, 6, 12, NUM_FONT_SMALL,qfalse);
|
|
|
|
inc = (float) ammoData[weaponData[cent->currentState.weapon].ammoIndex].max / MAX_TICS;
|
|
value =ps->ammo[weaponData[cent->currentState.weapon].ammoIndex];
|
|
|
|
for (i=MAX_TICS-1;i>=0;i--)
|
|
{
|
|
|
|
if (value <= 0) // partial tic
|
|
{
|
|
memcpy(calcColor, colorTable[CT_BLACK], sizeof(vec4_t));
|
|
}
|
|
else if (value < inc) // partial tic
|
|
{
|
|
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
|
|
percent = value / inc;
|
|
calcColor[0] *= percent;
|
|
calcColor[1] *= percent;
|
|
calcColor[2] *= percent;
|
|
}
|
|
else
|
|
{
|
|
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
|
|
}
|
|
|
|
cgi_R_SetColor( calcColor);
|
|
CG_DrawPic( x + ammoTicPos[i].x,
|
|
y + ammoTicPos[i].y,
|
|
ammoTicPos[i].width,
|
|
ammoTicPos[i].height,
|
|
ammoTicPos[i].tic );
|
|
|
|
value -= inc;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawHUDLeftFrame1
|
|
================
|
|
*/
|
|
static void CG_DrawHUDLeftFrame1(int x,int y)
|
|
{
|
|
// Inner gray wire frame
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDInnerLeft );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawHUDLeftFrame2
|
|
================
|
|
*/
|
|
static void CG_DrawHUDLeftFrame2(int x,int y)
|
|
{
|
|
// Inner gray wire frame
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDLeftFrame ); // Metal frame
|
|
}
|
|
/*
|
|
================
|
|
CG_DrawHealth
|
|
================
|
|
*/
|
|
static void CG_DrawHealth(int x,int y)
|
|
{
|
|
vec4_t calcColor;
|
|
float healthPercent;
|
|
playerState_t *ps;
|
|
|
|
ps = &cg.snap->ps;
|
|
|
|
memcpy(calcColor, colorTable[CT_HUD_RED], sizeof(vec4_t));
|
|
healthPercent = (float) ps->stats[STAT_HEALTH]/ps->stats[STAT_MAX_HEALTH];
|
|
calcColor[0] *= healthPercent;
|
|
calcColor[1] *= healthPercent;
|
|
calcColor[2] *= healthPercent;
|
|
cgi_R_SetColor( calcColor);
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDHealth );
|
|
|
|
// Draw the ticks
|
|
if (cg.HUDHealthFlag)
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_HUD_RED] );
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDHealthTic );
|
|
}
|
|
|
|
cgi_R_SetColor( colorTable[CT_HUD_RED] );
|
|
CG_DrawNumField (x + 16, y + 40, 3, ps->stats[STAT_HEALTH], 6, 12,
|
|
NUM_FONT_SMALL,qtrue);
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawArmor
|
|
================
|
|
*/
|
|
static void CG_DrawArmor(int x,int y)
|
|
{
|
|
vec4_t calcColor;
|
|
float armorPercent,hold;
|
|
playerState_t *ps;
|
|
|
|
ps = &cg.snap->ps;
|
|
|
|
// Outer Armor circular
|
|
memcpy(calcColor, colorTable[CT_HUD_GREEN], sizeof(vec4_t));
|
|
|
|
hold = ps->stats[STAT_ARMOR]-(ps->stats[STAT_MAX_HEALTH]/2);
|
|
armorPercent = (float) hold/(ps->stats[STAT_MAX_HEALTH]/2);
|
|
if (armorPercent <0)
|
|
{
|
|
armorPercent = 0;
|
|
}
|
|
calcColor[0] *= armorPercent;
|
|
calcColor[1] *= armorPercent;
|
|
calcColor[2] *= armorPercent;
|
|
cgi_R_SetColor( calcColor);
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmor1 );
|
|
|
|
// Inner Armor circular
|
|
if (armorPercent>0)
|
|
{
|
|
armorPercent = 1;
|
|
}
|
|
else
|
|
{
|
|
armorPercent = (float) ps->stats[STAT_ARMOR]/(ps->stats[STAT_MAX_HEALTH]/2);
|
|
}
|
|
memcpy(calcColor, colorTable[CT_HUD_GREEN], sizeof(vec4_t));
|
|
calcColor[0] *= armorPercent;
|
|
calcColor[1] *= armorPercent;
|
|
calcColor[2] *= armorPercent;
|
|
cgi_R_SetColor( calcColor);
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmor2 ); // Inner Armor circular
|
|
/*
|
|
if (ps->stats[STAT_ARMOR]) // Is there armor? Draw the HUD Armor TIC
|
|
{
|
|
// Make tic flash if inner armor is at 50% (25% of full armor)
|
|
if (armorPercent<.5) // Do whatever the flash timer says
|
|
{
|
|
if (cg.HUDTickFlashTime < cg.time) // Flip at the same time
|
|
{
|
|
cg.HUDTickFlashTime = cg.time + 100;
|
|
if (cg.HUDArmorFlag)
|
|
{
|
|
cg.HUDArmorFlag = qfalse;
|
|
}
|
|
else
|
|
{
|
|
cg.HUDArmorFlag = qtrue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cg.HUDArmorFlag=qtrue;
|
|
}
|
|
}
|
|
else // No armor? Don't show it.
|
|
{
|
|
cg.HUDArmorFlag=qfalse;
|
|
}
|
|
|
|
if (cg.HUDArmorFlag)
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_HUD_GREEN] );
|
|
CG_DrawPic( x, y, 80, 80, cgs.media.HUDArmorTic );
|
|
}
|
|
*/
|
|
cgi_R_SetColor( colorTable[CT_HUD_GREEN] );
|
|
CG_DrawNumField (x + 16 + 14, y + 40 + 14, 3, ps->stats[STAT_ARMOR], 6, 12,
|
|
NUM_FONT_SMALL,qfalse);
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
static qboolean CG_DrawCustomHealthHud( centity_t *cent )
|
|
{
|
|
float health = 0;
|
|
vec4_t color;
|
|
|
|
if (( cent->currentState.eFlags & EF_LOCKED_TO_WEAPON ))
|
|
{
|
|
// DRAW emplaced HUD
|
|
color[0] = color[1] = color[2] = 0.0f;
|
|
color[3] = 0.3f;
|
|
|
|
cgi_R_SetColor( color );
|
|
CG_DrawPic( 14, 480 - 50, 94, 32, cgs.media.whiteShader );
|
|
|
|
// NOTE: this looks ugly
|
|
if ( cent->gent && cent->gent->owner )
|
|
{
|
|
if (( cent->gent->owner->flags & FL_GODMODE ))
|
|
{
|
|
// chair is in godmode, so render the health of the player instead
|
|
health = cent->gent->health / (float)cent->gent->max_health;
|
|
}
|
|
else
|
|
{
|
|
// render the chair health
|
|
health = cent->gent->owner->health / (float)cent->gent->owner->max_health;
|
|
}
|
|
}
|
|
|
|
color[0] = 1.0f;
|
|
color[3] = 0.5f;
|
|
|
|
cgi_R_SetColor( color );
|
|
CG_DrawPic( 18, 480 - 41, 87 * health, 19, cgs.media.whiteShader );
|
|
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( 2, 480 - 64, 128, 64, cgs.media.emplacedHealthBarShader);
|
|
|
|
return qfalse; // drew this hud, so don't draw the player one
|
|
}
|
|
else if (( cent->currentState.eFlags & EF_IN_ATST ))
|
|
{
|
|
// we are an ATST...
|
|
color[0] = color[1] = color[2] = 0.0f;
|
|
color[3] = 0.3f;
|
|
|
|
cgi_R_SetColor( color );
|
|
CG_DrawPic( 14, 480 - 50, 94, 32, cgs.media.whiteShader );
|
|
|
|
// we just calc the display value from the sum of health and armor
|
|
if ( g_entities[cg.snap->ps.viewEntity].activator ) // ensure we can look back to the atst_drivable to get the max health
|
|
{
|
|
health = ( g_entities[cg.snap->ps.viewEntity].health + g_entities[cg.snap->ps.viewEntity].client->ps.stats[STAT_ARMOR] ) /
|
|
(float)(g_entities[cg.snap->ps.viewEntity].max_health + g_entities[cg.snap->ps.viewEntity].activator->max_health );
|
|
}
|
|
else
|
|
{
|
|
health = ( g_entities[cg.snap->ps.viewEntity].health + g_entities[cg.snap->ps.viewEntity].client->ps.stats[STAT_ARMOR]) /
|
|
(float)(g_entities[cg.snap->ps.viewEntity].max_health + 800 ); // hacked max armor since we don't have an activator...should never happen
|
|
}
|
|
|
|
color[1] = 0.25f; // blue-green
|
|
color[2] = 1.0f;
|
|
color[3] = 0.5f;
|
|
|
|
cgi_R_SetColor( color );
|
|
CG_DrawPic( 18, 480 - 41, 87 * health, 19, cgs.media.whiteShader );
|
|
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( 2, 480 - 64, 128, 64, cgs.media.emplacedHealthBarShader);
|
|
|
|
return qfalse; // drew this hud, so don't draw the player one
|
|
}
|
|
else if ( cg.snap->ps.viewEntity && ( g_entities[cg.snap->ps.viewEntity].dflags & DAMAGE_CUSTOM_HUD ))
|
|
{
|
|
// if we've gotten this far, we are assuming that we are a misc_panel_turret
|
|
color[0] = color[1] = color[2] = 0.0f;
|
|
color[3] = 0.3f;
|
|
|
|
cgi_R_SetColor( color );
|
|
CG_DrawPic( 14, 480 - 50, 94, 32, cgs.media.whiteShader );
|
|
|
|
health = g_entities[cg.snap->ps.viewEntity].health / (float)g_entities[cg.snap->ps.viewEntity].max_health;
|
|
|
|
color[1] = 1.0f;
|
|
color[3] = 0.5f;
|
|
|
|
cgi_R_SetColor( color );
|
|
CG_DrawPic( 18, 480 - 41, 87 * health, 19, cgs.media.whiteShader );
|
|
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( 2, 480 - 64, 128, 64, cgs.media.ladyLuckHealthShader );
|
|
|
|
return qfalse; // drew this hud, so don't draw the player one
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
CG_DrawWeapReticle
|
|
==============
|
|
*/
|
|
static void CG_DrawWeapReticle( void )
|
|
{
|
|
vec4_t light_color = {0.7, 0.7, 0.7, 1};
|
|
vec4_t black = {0.0, 0.0, 0.0, 1};
|
|
|
|
float indent = 0.16;
|
|
float X_WIDTH=640;
|
|
float Y_HEIGHT=480;
|
|
|
|
float x = (X_WIDTH * indent), y = (Y_HEIGHT * indent), w = (X_WIDTH * (1-(2*indent))) / 2.0f, h = (Y_HEIGHT * (1-(2*indent))) / 2;
|
|
|
|
// sides
|
|
CG_FillRect( 0, 0, (X_WIDTH * indent), Y_HEIGHT, black );
|
|
CG_FillRect( X_WIDTH * (1 - indent), 0, (X_WIDTH * indent), Y_HEIGHT, black );
|
|
// top/bottom
|
|
CG_FillRect( X_WIDTH * indent, 0, X_WIDTH * (1-indent), Y_HEIGHT * indent, black );
|
|
CG_FillRect( X_WIDTH * indent, Y_HEIGHT * (1-indent), X_WIDTH * (1-indent), Y_HEIGHT * indent, black );
|
|
|
|
{
|
|
// center
|
|
if ( cgs.media.reticleShader ) {
|
|
cgi_R_DrawStretchPic( x, y, w, h, 0, 0, 1, 1, cgs.media.reticleShader ); // tl
|
|
cgi_R_DrawStretchPic( x + w, y, w, h, 1, 0, 0, 1, cgs.media.reticleShader ); // tr
|
|
cgi_R_DrawStretchPic( x, y + h, w, h, 0, 1, 1, 0, cgs.media.reticleShader ); // bl
|
|
cgi_R_DrawStretchPic( x + w, y + h, w, h, 1, 1, 0, 0, cgs.media.reticleShader ); // br
|
|
}
|
|
|
|
// hairs
|
|
CG_FillRect( 84, 239, 177, 2, black ); // left
|
|
CG_FillRect( 320, 242, 1, 58, black ); // center top
|
|
CG_FillRect( 319, 300, 2, 178, black ); // center bot
|
|
CG_FillRect( 380, 239, 177, 2, black ); // right
|
|
}
|
|
}
|
|
|
|
//--------------------------------------
|
|
static void CG_DrawBatteryCharge( void )
|
|
{
|
|
if ( cg.batteryChargeTime > cg.time )
|
|
{
|
|
vec4_t color;
|
|
|
|
// FIXME: drawing it here will overwrite zoom masks...find a better place
|
|
if ( cg.batteryChargeTime < cg.time + 1000 )
|
|
{
|
|
// fading out for the last second
|
|
color[0] = color[1] = color[2] = 1.0f;
|
|
color[3] = (cg.batteryChargeTime - cg.time) / 1000.0f;
|
|
}
|
|
else
|
|
{
|
|
// draw full
|
|
color[0] = color[1] = color[2] = color[3] = 1.0f;
|
|
}
|
|
|
|
cgi_R_SetColor( color );
|
|
|
|
// batteries were just charged
|
|
CG_DrawPic( 605, 295, 24, 32, cgs.media.batteryChargeShader );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawHUD
|
|
================
|
|
*/
|
|
extern void *cgi_UI_GetMenuByName( const char *menu );
|
|
extern void cgi_UI_Menu_Paint( void *menu, qboolean force );
|
|
static void CG_DrawHUD( centity_t *cent )
|
|
{
|
|
int x,y,value;
|
|
|
|
if (cgi_UI_GetMenuInfo("lefthud",&x,&y))
|
|
{
|
|
// Draw all the HUD elements --eez
|
|
cgi_UI_Menu_Paint( cgi_UI_GetMenuByName( "lefthud" ), qtrue );
|
|
|
|
// Draw armor & health values
|
|
if ( cg_draw2D.integer == 2 )
|
|
{
|
|
CG_DrawSmallStringColor(x+5, y - 60,va("Armor:%d",cg.snap->ps.stats[STAT_ARMOR]), colorTable[CT_HUD_GREEN] );
|
|
CG_DrawSmallStringColor(x+5, y - 40,va("Health:%d",cg.snap->ps.stats[STAT_HEALTH]), colorTable[CT_HUD_GREEN] );
|
|
}
|
|
|
|
CG_DrawHUDLeftFrame1(x,y);
|
|
CG_DrawArmor(x,y);
|
|
CG_DrawHealth(x,y);
|
|
CG_DrawHUDLeftFrame2(x,y);
|
|
}
|
|
|
|
if (cgi_UI_GetMenuInfo("righthud",&x,&y))
|
|
{
|
|
// Draw all the HUD elements --eez
|
|
cgi_UI_Menu_Paint( cgi_UI_GetMenuByName( "righthud" ), qtrue );
|
|
|
|
// Draw armor & health values
|
|
if ( cg_draw2D.integer == 2 )
|
|
{
|
|
if ( cent->currentState.weapon != WP_SABER && cent->currentState.weapon != WP_STUN_BATON && cent->gent )
|
|
{
|
|
// Bob, just didn't want the ammo text drawing when the saber or the stun baton is the current weapon...change it back if this is wrong.
|
|
value = cg.snap->ps.ammo[weaponData[cent->currentState.weapon].ammoIndex];
|
|
// value = cent->gent->client->ps.forcePower;
|
|
CG_DrawSmallStringColor(x, y - 60,va("Ammo:%d",value), colorTable[CT_HUD_GREEN] );
|
|
}
|
|
else
|
|
{
|
|
// value = cg.snap->ps.ammo[weaponData[cent->currentState.weapon].ammoIndex];
|
|
}
|
|
// CG_DrawSmallStringColor(x, y - 60,va("Ammo:%d",value), colorTable[CT_HUD_GREEN] );
|
|
CG_DrawSmallStringColor(x, y - 40,va("Force:%d",cent->gent->client->ps.forcePower), colorTable[CT_HUD_GREEN] );
|
|
}
|
|
|
|
CG_DrawHUDRightFrame1(x,y);
|
|
CG_DrawForcePower(cent,x,y);
|
|
CG_DrawAmmo(cent,x,y);
|
|
CG_DrawMessageLit(cent,x,y);
|
|
CG_DrawHUDRightFrame2(x,y);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_ClearDataPadCvars
|
|
================
|
|
*/
|
|
void CG_ClearDataPadCvars( void )
|
|
{
|
|
cg_updatedDataPadForcePower1.integer = 0; //don't wait for the cvar-refresh.
|
|
cg_updatedDataPadForcePower2.integer = 0; //don't wait for the cvar-refresh.
|
|
cg_updatedDataPadForcePower3.integer = 0; //don't wait for the cvar-refresh.
|
|
cgi_Cvar_Set( "cg_updatedDataPadForcePower1", "0" );
|
|
cgi_Cvar_Set( "cg_updatedDataPadForcePower2", "0" );
|
|
cgi_Cvar_Set( "cg_updatedDataPadForcePower3", "0" );
|
|
|
|
cg_updatedDataPadObjective.integer = 0; //don't wait for the cvar-refresh.
|
|
cgi_Cvar_Set( "cg_updatedDataPadObjective", "0" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawDataPadHUD
|
|
================
|
|
*/
|
|
void CG_DrawDataPadHUD( centity_t *cent )
|
|
{
|
|
int x,y;
|
|
|
|
x = 34;
|
|
y = 286;
|
|
|
|
CG_DrawHUDLeftFrame1(x,y);
|
|
CG_DrawArmor(x,y);
|
|
CG_DrawHealth(x,y);
|
|
|
|
x = 526;
|
|
|
|
if ((missionInfo_Updated) && ((cg_updatedDataPadForcePower1.integer) || (cg_updatedDataPadObjective.integer)))
|
|
{
|
|
// Stop flashing light
|
|
cg.missionInfoFlashTime = 0;
|
|
missionInfo_Updated = qfalse;
|
|
|
|
// Set which force power to show.
|
|
// cg_updatedDataPadForcePower is set from Q3_Interface, because force powers would only be given
|
|
// from a script.
|
|
if (cg_updatedDataPadForcePower1.integer)
|
|
{
|
|
cg.DataPadforcepowerSelect = cg_updatedDataPadForcePower1.integer - 1; // Not pretty, I know
|
|
if (cg.DataPadforcepowerSelect >= MAX_DPSHOWPOWERS)
|
|
{ //duh
|
|
cg.DataPadforcepowerSelect = MAX_DPSHOWPOWERS-1;
|
|
}
|
|
else if (cg.DataPadforcepowerSelect<0)
|
|
{
|
|
cg.DataPadforcepowerSelect=0;
|
|
}
|
|
}
|
|
// CG_ClearDataPadCvars();
|
|
}
|
|
|
|
CG_DrawHUDRightFrame1(x,y);
|
|
CG_DrawForcePower(cent,x,y);
|
|
CG_DrawAmmo(cent,x,y);
|
|
CG_DrawMessageLit(cent,x,y);
|
|
|
|
cgi_R_SetColor( colorTable[CT_WHITE]);
|
|
CG_DrawPic( 0, 0, 640, 480, cgs.media.dataPadFrame );
|
|
|
|
}
|
|
|
|
//------------------------
|
|
// CG_DrawZoomMask
|
|
//------------------------
|
|
static void CG_DrawBinocularNumbers( qboolean power )
|
|
{
|
|
vec4_t color1;
|
|
|
|
cgi_R_SetColor( colorTable[CT_BLACK]);
|
|
CG_DrawPic( 212, 367, 200, 40, cgs.media.whiteShader );
|
|
|
|
if ( power )
|
|
{
|
|
// Numbers should be kind of greenish
|
|
color1[0] = 0.2f;
|
|
color1[1] = 0.4f;
|
|
color1[2] = 0.2f;
|
|
color1[3] = 0.3f;
|
|
cgi_R_SetColor( color1 );
|
|
|
|
// Draw scrolling numbers, use intervals 10 units apart--sorry, this section of code is just kind of hacked
|
|
// up with a bunch of magic numbers.....
|
|
int val = ((int)((cg.refdefViewAngles[YAW] + 180) / 10)) * 10;
|
|
float off = (cg.refdefViewAngles[YAW] + 180) - val;
|
|
|
|
for ( int i = -10; i < 30; i += 10 )
|
|
{
|
|
val -= 10;
|
|
|
|
if ( val < 0 )
|
|
{
|
|
val += 360;
|
|
}
|
|
|
|
// we only want to draw the very far left one some of the time, if it's too far to the left it will poke outside the mask.
|
|
if (( off > 3.0f && i == -10 ) || i > -10 )
|
|
{
|
|
// draw the value, but add 200 just to bump the range up...arbitrary, so change it if you like
|
|
CG_DrawNumField( 155 + i * 10 + off * 10, 374, 3, val + 200, 24, 14, NUM_FONT_CHUNKY, qtrue );
|
|
CG_DrawPic( 245 + (i-1) * 10 + off * 10, 376, 6, 6, cgs.media.whiteShader );
|
|
}
|
|
}
|
|
|
|
CG_DrawPic( 212, 367, 200, 28, cgs.media.binocularOverlay );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawZoomMask
|
|
|
|
================
|
|
*/
|
|
extern float cg_zoomFov; //from cg_view.cpp
|
|
|
|
static void CG_DrawZoomMask( void )
|
|
{
|
|
vec4_t color1;
|
|
centity_t *cent;
|
|
float level;
|
|
static qboolean flip = qtrue;
|
|
float charge = cg.snap->ps.batteryCharge / (float)MAX_BATTERIES; // convert charge to a percentage
|
|
qboolean power = qfalse;
|
|
|
|
cent = &cg_entities[0];
|
|
|
|
if ( charge > 0.0f )
|
|
{
|
|
power = qtrue;
|
|
}
|
|
|
|
//-------------
|
|
// Binoculars
|
|
//--------------------------------
|
|
if ( cg.zoomMode == 1 )
|
|
{
|
|
CG_RegisterItemVisuals( ITM_BINOCULARS_PICKUP );
|
|
|
|
// zoom level
|
|
level = (float)(80.0f - cg_zoomFov) / 80.0f;
|
|
|
|
// ...so we'll clamp it
|
|
if ( level < 0.0f )
|
|
{
|
|
level = 0.0f;
|
|
}
|
|
else if ( level > 1.0f )
|
|
{
|
|
level = 1.0f;
|
|
}
|
|
|
|
// Using a magic number to convert the zoom level to scale amount
|
|
level *= 162.0f;
|
|
|
|
if ( power )
|
|
{
|
|
// draw blue tinted distortion mask, trying to make it as small as is necessary to fill in the viewable area
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( 34, 48, 570, 362, cgs.media.binocularStatic );
|
|
}
|
|
|
|
CG_DrawBinocularNumbers( power );
|
|
|
|
// Black out the area behind the battery display
|
|
cgi_R_SetColor( colorTable[CT_DKGREY]);
|
|
CG_DrawPic( 50, 389, 161, 16, cgs.media.whiteShader );
|
|
|
|
if ( power )
|
|
{
|
|
color1[0] = sin( cg.time * 0.01f ) * 0.5f + 0.5f;
|
|
color1[0] = color1[0] * color1[0];
|
|
color1[1] = color1[0];
|
|
color1[2] = color1[0];
|
|
color1[3] = 1.0f;
|
|
|
|
cgi_R_SetColor( color1 );
|
|
|
|
CG_DrawPic( 82, 94, 16, 16, cgs.media.binocularCircle );
|
|
}
|
|
|
|
CG_DrawPic( 0, 0, 640, 480, cgs.media.binocularMask );
|
|
|
|
if ( power )
|
|
{
|
|
// Flickery color
|
|
color1[0] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.1f;
|
|
color1[1] = 0.8f + Q_flrand(-1.0f, 1.0f) * 0.1f;
|
|
color1[2] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.1f;
|
|
color1[3] = 1.0f;
|
|
cgi_R_SetColor( color1 );
|
|
|
|
CG_DrawPic( 4, 282 - level, 16, 16, cgs.media.binocularArrow );
|
|
}
|
|
else
|
|
{
|
|
// No power color
|
|
color1[0] = 0.15f;
|
|
color1[1] = 0.15f;
|
|
color1[2] = 0.15f;
|
|
color1[3] = 1.0f;
|
|
cgi_R_SetColor( color1 );
|
|
}
|
|
|
|
// The top triangle bit randomly flips when the power is on
|
|
if ( flip && power )
|
|
{
|
|
CG_DrawPic( 330, 60, -26, -30, cgs.media.binocularTri );
|
|
}
|
|
else
|
|
{
|
|
CG_DrawPic( 307, 40, 26, 30, cgs.media.binocularTri );
|
|
}
|
|
|
|
if ( Q_flrand(0.0f, 1.0f) > 0.98f && ( cg.time & 1024 ))
|
|
{
|
|
flip = (qboolean)!flip;
|
|
}
|
|
|
|
if ( power )
|
|
{
|
|
color1[0] = 1.0f * ( charge < 0.2f ? !!(cg.time & 256) : 1 );
|
|
color1[1] = charge * color1[0];
|
|
color1[2] = 0.0f;
|
|
color1[3] = 0.2f;
|
|
|
|
cgi_R_SetColor( color1 );
|
|
CG_DrawPic( 60, 394.5f, charge * 141, 5, cgs.media.whiteShader );
|
|
}
|
|
}
|
|
//------------
|
|
// Disruptor
|
|
//--------------------------------
|
|
else if ( cg.zoomMode == 2 )
|
|
{
|
|
level = (float)(80.0f - cg_zoomFov) / 80.0f;
|
|
|
|
// ...so we'll clamp it
|
|
if ( level < 0.0f )
|
|
{
|
|
level = 0.0f;
|
|
}
|
|
else if ( level > 1.0f )
|
|
{
|
|
level = 1.0f;
|
|
}
|
|
|
|
// Using a magic number to convert the zoom level to a rotation amount that correlates more or less with the zoom artwork.
|
|
level *= 103.0f;
|
|
|
|
// Draw target mask
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( 0, 0, 640, 480, cgs.media.disruptorMask );
|
|
|
|
// apparently 99.0f is the full zoom level
|
|
if ( level >= 99 )
|
|
{
|
|
// Fully zoomed, so make the rotating insert pulse
|
|
color1[0] = 1.0f;
|
|
color1[1] = 1.0f;
|
|
color1[2] = 1.0f;
|
|
color1[3] = 0.7f + sin( cg.time * 0.01f ) * 0.3f;
|
|
|
|
cgi_R_SetColor( color1 );
|
|
}
|
|
|
|
// Draw rotating insert
|
|
CG_DrawRotatePic2( 320, 240, 640, 480, -level, cgs.media.disruptorInsert );
|
|
|
|
float cx, cy;
|
|
float max;
|
|
|
|
max = cg_entities[0].gent->client->ps.ammo[weaponData[WP_DISRUPTOR].ammoIndex] / (float)ammoData[weaponData[WP_DISRUPTOR].ammoIndex].max;
|
|
|
|
if ( max > 1.0f )
|
|
{
|
|
max = 1.0f;
|
|
}
|
|
|
|
color1[0] = (1.0f - max) * 2.0f;
|
|
color1[1] = max * 1.5f;
|
|
color1[2] = 0.0f;
|
|
color1[3] = 1.0f;
|
|
|
|
// If we are low on ammo, make us flash
|
|
if ( max < 0.15f && ( cg.time & 512 ))
|
|
{
|
|
VectorClear( color1 );
|
|
}
|
|
|
|
if ( color1[0] > 1.0f )
|
|
{
|
|
color1[0] = 1.0f;
|
|
}
|
|
|
|
if ( color1[1] > 1.0f )
|
|
{
|
|
color1[1] = 1.0f;
|
|
}
|
|
|
|
cgi_R_SetColor( color1 );
|
|
|
|
max *= 58.0f;
|
|
|
|
for ( float i = 18.5f; i <= 18.5f + max; i+= 3 ) // going from 15 to 45 degrees, with 5 degree increments
|
|
{
|
|
cx = 320 + sin( (i+90.0f)/57.296f ) * 190;
|
|
cy = 240 + cos( (i+90.0f)/57.296f ) * 190;
|
|
|
|
CG_DrawRotatePic2( cx, cy, 12, 24, 90 - i, cgs.media.disruptorInsertTick );
|
|
}
|
|
|
|
// FIXME: doesn't know about ammo!! which is bad because it draws charge beyond what ammo you may have..
|
|
if ( cg_entities[0].gent->client->ps.weaponstate == WEAPON_CHARGING_ALT )
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
|
|
// draw the charge level
|
|
max = ( cg.time - cg_entities[0].gent->client->ps.weaponChargeTime ) / ( 150.0f * 10.0f ); // bad hardcodedness 150 is disruptor charge unit and 10 is max charge units allowed.
|
|
|
|
if ( max > 1.0f )
|
|
{
|
|
max = 1.0f;
|
|
}
|
|
|
|
CG_DrawPic2( 257, 435, 134 * max, 34, 0,0,max,1,cgi_R_RegisterShaderNoMip( "gfx/2d/crop_charge" ));
|
|
}
|
|
}
|
|
//-----------
|
|
// Light Amp
|
|
//--------------------------------
|
|
else if ( cg.zoomMode == 3 )
|
|
{
|
|
CG_RegisterItemVisuals( ITM_LA_GOGGLES_PICKUP );
|
|
|
|
if ( power )
|
|
{
|
|
cgi_R_SetColor( colorTable[CT_WHITE] );
|
|
CG_DrawPic( 34, 29, 580, 410, cgs.media.laGogglesStatic );
|
|
|
|
CG_DrawPic( 570, 140, 12, 160, cgs.media.laGogglesSideBit );
|
|
|
|
float light = (128-cent->gent->lightLevel) * 0.5f;
|
|
|
|
if ( light < -81 ) // saber can really jack up local light levels....?magic number??
|
|
{
|
|
light = -81;
|
|
}
|
|
|
|
float pos1 = 220 + light;
|
|
float pos2 = 220 + cos( cg.time * 0.0004f + light * 0.05f ) * 40 + sin( cg.time * 0.0013f + 1 ) * 20 + sin( cg.time * 0.0021f ) * 5;
|
|
|
|
// Flickery color
|
|
color1[0] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.2f;
|
|
color1[1] = 0.8f + Q_flrand(-1.0f, 1.0f) * 0.2f;
|
|
color1[2] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.2f;
|
|
color1[3] = 1.0f;
|
|
cgi_R_SetColor( color1 );
|
|
|
|
CG_DrawPic( 565, pos1, 22, 8, cgs.media.laGogglesBracket );
|
|
CG_DrawPic( 558, pos2, 14, 5, cgs.media.laGogglesArrow );
|
|
}
|
|
|
|
// Black out the area behind the battery display
|
|
cgi_R_SetColor( colorTable[CT_DKGREY]);
|
|
CG_DrawPic( 236, 357, 164, 16, cgs.media.whiteShader );
|
|
|
|
if ( power )
|
|
{
|
|
// Power bar
|
|
color1[0] = 1.0f * ( charge < 0.2f ? !!(cg.time & 256) : 1 );
|
|
color1[1] = charge * color1[0];
|
|
color1[2] = 0.0f;
|
|
color1[3] = 0.4f;
|
|
|
|
cgi_R_SetColor( color1 );
|
|
CG_DrawPic( 247.0f, 362.5f, charge * 143.0f, 6, cgs.media.whiteShader );
|
|
|
|
// pulsing dot bit
|
|
color1[0] = sin( cg.time * 0.01f ) * 0.5f + 0.5f;
|
|
color1[0] = color1[0] * color1[0];
|
|
color1[1] = color1[0];
|
|
color1[2] = color1[0];
|
|
color1[3] = 1.0f;
|
|
|
|
cgi_R_SetColor( color1 );
|
|
|
|
CG_DrawPic( 65, 94, 16, 16, cgs.media.binocularCircle );
|
|
}
|
|
|
|
CG_DrawPic( 0, 0, 640, 480, cgs.media.laGogglesMask );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_DrawStats
|
|
|
|
================
|
|
*/
|
|
static void CG_DrawStats( void )
|
|
{
|
|
centity_t *cent;
|
|
|
|
if ( cg_drawStatus.integer == 0 ) {
|
|
return;
|
|
}
|
|
|
|
cent = &cg_entities[cg.snap->ps.clientNum];
|
|
|
|
if ((cg.snap->ps.viewEntity>0&&cg.snap->ps.viewEntity<ENTITYNUM_WORLD))
|
|
{
|
|
// MIGHT try and draw a custom hud if it wants...
|
|
CG_DrawCustomHealthHud( cent );
|
|
return;
|
|
}
|
|
|
|
cgi_UI_MenuPaintAll();
|
|
|
|
qboolean drawHud = qtrue;
|
|
|
|
if ( cent && cent->gent )
|
|
{
|
|
drawHud = CG_DrawCustomHealthHud( cent );
|
|
}
|
|
|
|
if (( drawHud ) && ( cg_drawHUD.integer ))
|
|
{
|
|
CG_DrawHUD( cent );
|
|
}
|
|
|
|
CG_DrawTalk(cent);
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
CG_DrawPickupItem
|
|
===================
|
|
*/
|
|
static void CG_DrawPickupItem( void ) {
|
|
int value;
|
|
float *fadeColor;
|
|
|
|
value = cg.itemPickup;
|
|
if ( value && cg_items[ value ].icon != -1 )
|
|
{
|
|
fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 );
|
|
if ( fadeColor )
|
|
{
|
|
CG_RegisterItemVisuals( value );
|
|
cgi_R_SetColor( fadeColor );
|
|
CG_DrawPic( 573, 340, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon );
|
|
//CG_DrawBigString( ICON_SIZE + 16, 398, bg_itemlist[ value ].classname, fadeColor[0] );
|
|
//CG_DrawProportionalString( ICON_SIZE + 16, 398,
|
|
// bg_itemlist[ value ].classname, CG_SMALLFONT,fadeColor );
|
|
cgi_R_SetColor( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMD_CGCam_Disable( void );
|
|
|
|
/*
|
|
===================
|
|
CG_DrawPickupItem
|
|
===================
|
|
*/
|
|
void CG_DrawCredits(void)
|
|
{
|
|
if (!cg.creditsStart)
|
|
{
|
|
//
|
|
cg.creditsStart = qtrue;
|
|
cgi_SP_Register("CREDITS", qfalse); // do not keep around after level
|
|
CG_Credits_Init("CREDITS_RAVEN", &colorTable[CT_ICON_BLUE]);
|
|
if ( cg_skippingcin.integer )
|
|
{//Were skipping a cinematic and it's over now
|
|
gi.cvar_set("timescale", "1");
|
|
gi.cvar_set("skippingCinematic", "0");
|
|
}
|
|
}
|
|
|
|
|
|
if (cg.creditsStart)
|
|
{
|
|
if ( !CG_Credits_Running() )
|
|
{
|
|
cgi_Cvar_Set( "cg_endcredits", "0" );
|
|
CMD_CGCam_Disable();
|
|
cgi_SendConsoleCommand("set nextmap disconnect ; cinematic outcast\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================================================================================
|
|
|
|
CROSSHAIR
|
|
|
|
================================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_DrawCrosshair
|
|
=================
|
|
*/
|
|
static void CG_DrawCrosshair( vec3_t worldPoint )
|
|
{
|
|
float w, h;
|
|
qhandle_t hShader;
|
|
qboolean corona = qfalse;
|
|
vec4_t ecolor;
|
|
float f;
|
|
float x, y;
|
|
|
|
if ( !cg_drawCrosshair.integer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cg.zoomMode > 0 && cg.zoomMode < 3 )
|
|
{
|
|
//not while scoped
|
|
return;
|
|
}
|
|
|
|
//set color based on what kind of ent is under crosshair
|
|
if ( g_crosshairEntNum >= ENTITYNUM_WORLD )
|
|
{
|
|
ecolor[0] = ecolor[1] = ecolor[2] = 1.0f;
|
|
}
|
|
else if ( cg_forceCrosshair && cg_crosshairForceHint.integer )
|
|
{
|
|
ecolor[0] = 0.2f;
|
|
ecolor[1] = 0.5f;
|
|
ecolor[2] = 1.0f;
|
|
|
|
corona = qtrue;
|
|
}
|
|
else if ( cg_crosshairIdentifyTarget.integer )
|
|
{
|
|
gentity_t *crossEnt = &g_entities[g_crosshairEntNum];
|
|
|
|
if ( crossEnt->client )
|
|
{
|
|
if ( crossEnt->client->ps.powerups[PW_CLOAKED] )
|
|
{//cloaked don't show up
|
|
ecolor[0] = 1.0f;//R
|
|
ecolor[1] = 1.0f;//G
|
|
ecolor[2] = 1.0f;//B
|
|
}
|
|
else if ( crossEnt->client->playerTeam == TEAM_PLAYER )
|
|
{
|
|
//Allies are green
|
|
ecolor[0] = 0.0f;//R
|
|
ecolor[1] = 1.0f;//G
|
|
ecolor[2] = 0.0f;//B
|
|
}
|
|
else if ( crossEnt->client->playerTeam == TEAM_NEUTRAL )
|
|
{
|
|
// NOTE: was yellow, but making it white unless they really decide they want to see colors
|
|
ecolor[0] = 1.0f;//R
|
|
ecolor[1] = 1.0f;//G
|
|
ecolor[2] = 1.0f;//B
|
|
}
|
|
else
|
|
{
|
|
//Enemies are red
|
|
ecolor[0] = 1.0f;//R
|
|
ecolor[1] = 0.1f;//G
|
|
ecolor[2] = 0.1f;//B
|
|
}
|
|
}
|
|
else if ( crossEnt->s.weapon == WP_TURRET && (crossEnt->svFlags&SVF_NONNPC_ENEMY) )
|
|
{
|
|
// a turret
|
|
if ( crossEnt->noDamageTeam == TEAM_PLAYER )
|
|
{
|
|
// mine are green
|
|
ecolor[0] = 0.0;//R
|
|
ecolor[1] = 1.0;//G
|
|
ecolor[2] = 0.0;//B
|
|
}
|
|
else
|
|
{
|
|
// hostile ones are red
|
|
ecolor[0] = 1.0;//R
|
|
ecolor[1] = 0.0;//G
|
|
ecolor[2] = 0.0;//B
|
|
}
|
|
}
|
|
else if ( crossEnt->s.weapon == WP_TRIP_MINE )
|
|
{
|
|
// tripmines are red
|
|
ecolor[0] = 1.0;//R
|
|
ecolor[1] = 0.0;//G
|
|
ecolor[2] = 0.0;//B
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( crossEnt->startRGBA, ecolor );
|
|
|
|
if ( !ecolor[0] && !ecolor[1] && !ecolor[2] )
|
|
{
|
|
// We don't want a black crosshair, so use white since it will show up better
|
|
ecolor[0] = 1.0f;//R
|
|
ecolor[1] = 1.0f;//G
|
|
ecolor[2] = 1.0f;//B
|
|
}
|
|
}
|
|
}
|
|
else // cg_crosshairIdentifyTarget is not on, so make it white
|
|
{
|
|
ecolor[0] = ecolor[1] = ecolor[2] = 1.0f;
|
|
}
|
|
|
|
ecolor[3] = 1.0;
|
|
cgi_R_SetColor( ecolor );
|
|
|
|
if ( cg.forceCrosshairStartTime )
|
|
{
|
|
// both of these calcs will fade the corona in one direction
|
|
if ( cg.forceCrosshairEndTime )
|
|
{
|
|
ecolor[3] = (cg.time - cg.forceCrosshairEndTime) / 500.0f;
|
|
}
|
|
else
|
|
{
|
|
ecolor[3] = (cg.time - cg.forceCrosshairStartTime) / 300.0f;
|
|
}
|
|
|
|
// clamp
|
|
if ( ecolor[3] < 0 )
|
|
{
|
|
ecolor[3] = 0;
|
|
}
|
|
else if ( ecolor[3] > 1.0f )
|
|
{
|
|
ecolor[3] = 1.0f;
|
|
}
|
|
|
|
if ( !cg.forceCrosshairEndTime )
|
|
{
|
|
// but for the other direction, we'll need to reverse it
|
|
ecolor[3] = 1.0f - ecolor[3];
|
|
}
|
|
}
|
|
|
|
if ( corona ) // we are pointing at a crosshair item
|
|
{
|
|
if ( !cg.forceCrosshairStartTime )
|
|
{
|
|
// must have just happened because we are not fading in yet...start it now
|
|
cg.forceCrosshairStartTime = cg.time;
|
|
cg.forceCrosshairEndTime = 0;
|
|
}
|
|
if ( cg.forceCrosshairEndTime )
|
|
{
|
|
// must have just gone over a force thing again...and we were in the process of fading out. Set fade in level to the level where the fade left off
|
|
cg.forceCrosshairStartTime = cg.time - ( 1.0f - ecolor[3] ) * 300.0f;
|
|
cg.forceCrosshairEndTime = 0;
|
|
}
|
|
}
|
|
else // not pointing at a crosshair item
|
|
{
|
|
if ( cg.forceCrosshairStartTime && !cg.forceCrosshairEndTime ) // were currently fading in
|
|
{
|
|
// must fade back out, but we will have to set the fadeout time to be equal to the current level of faded-in-edness
|
|
cg.forceCrosshairEndTime = cg.time - ecolor[3] * 500.0f;
|
|
}
|
|
if ( cg.forceCrosshairEndTime && cg.time - cg.forceCrosshairEndTime > 500.0f ) // not pointing at anything and fade out is totally done
|
|
{
|
|
// reset everything
|
|
cg.forceCrosshairStartTime = 0;
|
|
cg.forceCrosshairEndTime = 0;
|
|
}
|
|
}
|
|
|
|
w = h = cg_crosshairSize.value;
|
|
|
|
// pulse the size of the crosshair when picking up items
|
|
f = cg.time - cg.itemPickupBlendTime;
|
|
if ( f > 0 && f < ITEM_BLOB_TIME ) {
|
|
f /= ITEM_BLOB_TIME;
|
|
w *= ( 1 + f );
|
|
h *= ( 1 + f );
|
|
}
|
|
|
|
if ( worldPoint && VectorLength( worldPoint ) )
|
|
{
|
|
if ( !CG_WorldCoordToScreenCoordFloat( worldPoint, &x, &y ) )
|
|
{//off screen, don't draw it
|
|
cgi_R_SetColor( NULL );
|
|
return;
|
|
}
|
|
x -= 320;//????
|
|
y -= 240;//????
|
|
}
|
|
else
|
|
{
|
|
x = cg_crosshairX.integer;
|
|
y = cg_crosshairY.integer;
|
|
}
|
|
|
|
if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
|
|
{
|
|
if ( !Q_stricmp( "misc_panel_turret", g_entities[cg.snap->ps.viewEntity].classname ))
|
|
{
|
|
// draws a custom crosshair that is twice as large as normal
|
|
cgi_R_DrawStretchPic( x + cg.refdef.x + 320 - w,
|
|
y + cg.refdef.y + 240 - h,
|
|
w * 2, h * 2, 0, 0, 1, 1, cgs.media.turretCrossHairShader );
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ];
|
|
|
|
cgi_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (640 - w),
|
|
y + cg.refdef.y + 0.5 * (480 - h),
|
|
w, h, 0, 0, 1, 1, hShader );
|
|
}
|
|
|
|
if ( cg.forceCrosshairStartTime && cg_crosshairForceHint.integer ) // drawing extra bits
|
|
{
|
|
ecolor[0] = ecolor[1] = ecolor[2] = (1 - ecolor[3]) * ( sin( cg.time * 0.001f ) * 0.08f + 0.35f ); // don't draw full color
|
|
ecolor[3] = 1.0f;
|
|
|
|
cgi_R_SetColor( ecolor );
|
|
|
|
w *= 2.0f;
|
|
h *= 2.0f;
|
|
|
|
cgi_R_DrawStretchPic( x + cg.refdef.x + 0.5f * ( 640 - w ), y + cg.refdef.y + 0.5f * ( 480 - h ),
|
|
w, h,
|
|
0, 0, 1, 1,
|
|
cgs.media.forceCoronaShader );
|
|
}
|
|
|
|
cgi_R_SetColor( NULL );
|
|
}
|
|
|
|
/*
|
|
qboolean CG_WorldCoordToScreenCoord(vec3_t worldCoord, int *x, int *y)
|
|
|
|
Take any world coord and convert it to a 2D virtual 640x480 screen coord
|
|
*/
|
|
qboolean CG_WorldCoordToScreenCoordFloat(vec3_t worldCoord, float *x, float *y)
|
|
{
|
|
vec3_t trans;
|
|
float xc, yc;
|
|
float px, py;
|
|
float z;
|
|
|
|
px = tan(cg.refdef.fov_x * (M_PI / 360) );
|
|
py = tan(cg.refdef.fov_y * (M_PI / 360) );
|
|
|
|
VectorSubtract(worldCoord, cg.refdef.vieworg, trans);
|
|
|
|
xc = 640 / 2.0;
|
|
yc = 480 / 2.0;
|
|
|
|
// z = how far is the object in our forward direction
|
|
z = DotProduct(trans, cg.refdef.viewaxis[0]);
|
|
if (z <= 0.001)
|
|
return qfalse;
|
|
|
|
*x = xc - DotProduct(trans, cg.refdef.viewaxis[1])*xc/(z*px);
|
|
*y = yc - DotProduct(trans, cg.refdef.viewaxis[2])*yc/(z*py);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean CG_WorldCoordToScreenCoord( vec3_t worldCoord, int *x, int *y ) {
|
|
float xF, yF;
|
|
|
|
if ( CG_WorldCoordToScreenCoordFloat( worldCoord, &xF, &yF ) ) {
|
|
*x = (int)xF;
|
|
*y = (int)yF;
|
|
return qtrue;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
// I'm keeping the rocket tracking code separate for now since I may want to do different logic...but it still uses trace info from scanCrosshairEnt
|
|
//-----------------------------------------
|
|
static void CG_ScanForRocketLock( void )
|
|
//-----------------------------------------
|
|
{
|
|
gentity_t *traceEnt;
|
|
static qboolean tempLock = qfalse; // this will break if anything else uses this locking code ( other than the player )
|
|
|
|
traceEnt = &g_entities[g_crosshairEntNum];
|
|
|
|
if ( !traceEnt || g_crosshairEntNum <= 0 || g_crosshairEntNum >= ENTITYNUM_WORLD || (!traceEnt->client && traceEnt->s.weapon != WP_TURRET ) || !traceEnt->health
|
|
|| ( traceEnt && traceEnt->client && traceEnt->client->ps.powerups[PW_CLOAKED] ))
|
|
{
|
|
// see how much locking we have
|
|
int dif = ( cg.time - g_rocketLockTime ) / ( 1200.0f / 8.0f );
|
|
|
|
// 8 is full locking....also if we just traced onto the world,
|
|
// give them 1/2 second of slop before dropping the lock
|
|
if ( dif < 8 && g_rocketSlackTime + 500 < cg.time )
|
|
{
|
|
// didn't have a full lock and not in grace period, so drop the lock
|
|
g_rocketLockTime = 0;
|
|
g_rocketSlackTime = 0;
|
|
tempLock = qfalse;
|
|
}
|
|
|
|
if ( g_rocketSlackTime + 500 >= cg.time && g_rocketLockEntNum < ENTITYNUM_WORLD )
|
|
{
|
|
// were locked onto an ent, aren't right now.....but still within the slop grace period
|
|
// keep the current lock amount
|
|
g_rocketLockTime += cg.frametime;
|
|
}
|
|
|
|
if ( !tempLock && g_rocketLockEntNum < ENTITYNUM_WORLD && dif >= 8 )
|
|
{
|
|
tempLock = qtrue;
|
|
|
|
if ( g_rocketLockTime + 1200 < cg.time )
|
|
{
|
|
g_rocketLockTime = cg.time - 1200; // doh, hacking the time so the targetting still gets drawn full
|
|
}
|
|
}
|
|
|
|
// keep locking to this thing for one second after it gets out of view
|
|
if ( g_rocketLockTime + 2000.0f < cg.time ) // since time was hacked above, I'm compensating so that 2000ms is really only 1000ms
|
|
{
|
|
// too bad, you had your chance
|
|
g_rocketLockEntNum = ENTITYNUM_NONE;
|
|
g_rocketSlackTime = 0;
|
|
g_rocketLockTime = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tempLock = qfalse;
|
|
|
|
if ( g_rocketLockEntNum >= ENTITYNUM_WORLD )
|
|
{
|
|
if ( g_rocketSlackTime + 500 < cg.time )
|
|
{
|
|
// we just locked onto something, start the lock at the current time
|
|
g_rocketLockEntNum = g_crosshairEntNum;
|
|
g_rocketLockTime = cg.time;
|
|
g_rocketSlackTime = cg.time;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( g_rocketLockEntNum != g_crosshairEntNum )
|
|
{
|
|
g_rocketLockTime = cg.time;
|
|
}
|
|
|
|
// may as well always set this when we can potentially lock to something
|
|
g_rocketSlackTime = cg.time;
|
|
g_rocketLockEntNum = g_crosshairEntNum;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern float forcePushPullRadius[];
|
|
|
|
void CG_ScanForForceCrosshairEntity( )
|
|
{
|
|
trace_t trace;
|
|
gentity_t *traceEnt = NULL;
|
|
vec3_t start, end;
|
|
int content;
|
|
int ignoreEnt = cg.snap->ps.clientNum;
|
|
|
|
//FIXME: debounce this to about 10fps?
|
|
|
|
cg_forceCrosshair = qfalse;
|
|
if ( cg_entities[0].gent && cg_entities[0].gent->client ) // <-Mike said it should always do this //if (cg_crosshairForceHint.integer &&
|
|
{//try to check for force-affectable stuff first
|
|
vec3_t angles, d_f, d_rt, d_up;
|
|
|
|
//VectorCopy( g_entities[0].client->renderInfo.eyePoint, start );
|
|
//AngleVectors( cg_entities[0].lerpAngles, d_f, d_rt, d_up );
|
|
BG_CalculateVROffHandPosition(start, angles);
|
|
AngleVectors( angles, d_f, d_rt, d_up );
|
|
|
|
|
|
VectorMA( start, 2048, d_f, end );//4028 is max for mind trick
|
|
|
|
//YES! This is very very bad... but it works! James made me do it. Really, he did. Blame James.
|
|
gi.trace( &trace, start, vec3_origin, vec3_origin, end,
|
|
ignoreEnt, MASK_OPAQUE|CONTENTS_SHOTCLIP|CONTENTS_BODY|CONTENTS_ITEM, G2_NOCOLLIDE, 10 );// ); took out CONTENTS_SOLID| so you can target people through glass.... took out CONTENTS_CORPSE so disintegrated guys aren't shown, could just remove their body earlier too...
|
|
|
|
if ( trace.entityNum < ENTITYNUM_WORLD )
|
|
{//hit something
|
|
traceEnt = &g_entities[trace.entityNum];
|
|
if ( traceEnt )
|
|
{
|
|
if ( traceEnt->client)
|
|
{//is a client
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
// No? Check for force-push/pullable doors and func_statics
|
|
else if ( traceEnt->s.eType == ET_MOVER )
|
|
{//hit a mover
|
|
if ( !Q_stricmp( "func_door", traceEnt->classname ) )
|
|
{//it's a func_door
|
|
if ( traceEnt->spawnflags & 2/*MOVER_FORCE_ACTIVATE*/ )
|
|
{//it's force-usable
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL] || cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH] )
|
|
{//player has push or pull
|
|
float maxRange;
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL] > cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH] )
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL]];
|
|
}
|
|
else
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH]];
|
|
}
|
|
if ( maxRange >= trace.fraction * 2048 )
|
|
{//actually close enough to use one of our force powers on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( !Q_stricmp( "func_static", traceEnt->classname ) )
|
|
{//it's a func_static
|
|
if ( (traceEnt->spawnflags & 1/*F_PUSH*/) && (traceEnt->spawnflags & 2/*F_PULL*/) )
|
|
{//push or pullable
|
|
float maxRange;
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL] > cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH] )
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL]];
|
|
}
|
|
else
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH]];
|
|
}
|
|
if ( maxRange >= trace.fraction * 2048 )
|
|
{//actually close enough to use one of our force powers on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
else if ( (traceEnt->spawnflags & 1/*F_PUSH*/) )
|
|
{//pushable only
|
|
if ( forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH]] >= trace.fraction * 2048 )
|
|
{//actually close enough to use force push on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
else if ( (traceEnt->spawnflags & 2/*F_PULL*/) )
|
|
{//pullable only
|
|
if ( forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL]] >= trace.fraction * 2048 )
|
|
{//actually close enough to use force pull on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !traceEnt || (traceEnt->s.eFlags & EF_NO_TED) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if the player is in fog, don't show it
|
|
content = cgi_CM_PointContents( trace.endpos, 0 );
|
|
if ( content & CONTENTS_FOG )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if the player is cloaked, don't show it
|
|
if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_CLOAKED ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// update the fade timer
|
|
if ( cg.crosshairClientNum != trace.entityNum )
|
|
{
|
|
infoStringCount = 0;
|
|
}
|
|
|
|
cg.crosshairClientNum = trace.entityNum;
|
|
cg.crosshairClientTime = cg.time;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawCrosshair3D
|
|
=================
|
|
*/
|
|
static void CG_DrawCrosshair3D(int type) // 0 - force, 1 - weapons
|
|
{
|
|
float w;
|
|
qhandle_t hShader;
|
|
float f;
|
|
int ca;
|
|
|
|
trace_t trace;
|
|
vec3_t endpos;
|
|
refEntity_t ent;
|
|
|
|
if (( type == 1 && !cg_drawCrosshair.integer) ||
|
|
(type == 0 && !cg_drawCrosshairForce.integer)) {
|
|
return;
|
|
}
|
|
|
|
if (cg.snap->ps.pm_type == PM_INTERMISSION)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( in_camera ) {
|
|
return;
|
|
}
|
|
|
|
if ( cg.zoomMode )
|
|
{
|
|
//not while scoped
|
|
return;
|
|
}
|
|
|
|
if ( in_misccamera )
|
|
{
|
|
//Not while viewing from another entity (such as a camera)
|
|
return;
|
|
}
|
|
|
|
if ( type == 1 && (cg.snap->ps.weapon == WP_NONE ||
|
|
cg.snap->ps.weapon == WP_SABER ||
|
|
cg.snap->ps.weapon == WP_STUN_BATON ||
|
|
cg.snap->ps.weapon == WP_THERMAL ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (type == 0)
|
|
{
|
|
if (showPowers[cg.forcepowerSelect] == FP_HEAL ||
|
|
showPowers[cg.forcepowerSelect] == FP_SPEED ||
|
|
vr->weapon_stabilised)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CG_ScanForForceCrosshairEntity();
|
|
}
|
|
|
|
w = cg_crosshairSize.value;
|
|
|
|
// pulse the size of the crosshair when picking up items
|
|
f = cg.time - cg.itemPickupBlendTime;
|
|
if ( f > 0 && f < ITEM_BLOB_TIME ) {
|
|
f /= ITEM_BLOB_TIME;
|
|
w *= ( 1 + f );
|
|
}
|
|
|
|
ca = (type == 1) ? cg_drawCrosshair.integer : cg_drawCrosshairForce.integer;
|
|
if (ca < 0) {
|
|
ca = 0;
|
|
}
|
|
hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ];
|
|
|
|
float xmax = 64.0f * tan(cg.refdef.fov_x * M_PI / 360.0f);
|
|
|
|
vec3_t forward, weaponangles, origin;
|
|
if (type == 0)
|
|
{
|
|
BG_CalculateVROffHandPosition(origin, weaponangles);
|
|
}
|
|
else
|
|
{
|
|
BG_CalculateVRWeaponPosition(origin, weaponangles);
|
|
}
|
|
AngleVectors(weaponangles, forward, NULL, NULL);
|
|
VectorMA(origin, 2048, forward, endpos);
|
|
CG_Trace(&trace, origin, NULL, NULL, endpos, 0, MASK_SHOT);
|
|
|
|
if (trace.fraction != 1.0f) {
|
|
memset(&ent, 0, sizeof(ent));
|
|
ent.reType = RT_SPRITE;
|
|
ent.renderfx = RF_FIRST_PERSON;
|
|
|
|
VectorCopy(trace.endpos, ent.origin);
|
|
|
|
ent.radius = w / 640 * xmax * trace.fraction * 2048 / 64.0f;
|
|
ent.customShader = hShader;
|
|
if(type == 0 && !cg_forceCrosshair) //Not Active Force Crosshair
|
|
{
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = 180;
|
|
ent.shaderRGBA[2] = 180;
|
|
}
|
|
else if(type == 0) //Active Force Crosshair
|
|
{
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = 90;
|
|
ent.shaderRGBA[2] = 90;
|
|
}
|
|
else //Regular Crosshair
|
|
{
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = 255;
|
|
ent.shaderRGBA[2] = 255;
|
|
}
|
|
ent.shaderRGBA[3] = 255;
|
|
|
|
cgi_R_AddRefEntityToScene(&ent);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_ScanForCrosshairEntity
|
|
=================
|
|
*/
|
|
static void CG_ScanForCrosshairEntity( qboolean scanAll )
|
|
{
|
|
trace_t trace;
|
|
gentity_t *traceEnt = NULL;
|
|
vec3_t start, end;
|
|
int content;
|
|
int ignoreEnt = cg.snap->ps.clientNum;
|
|
|
|
//FIXME: debounce this to about 10fps?
|
|
|
|
cg_forceCrosshair = qfalse;
|
|
if ( cg_entities[0].gent && cg_entities[0].gent->client ) // <-Mike said it should always do this //if (cg_crosshairForceHint.integer &&
|
|
{//try to check for force-affectable stuff first
|
|
vec3_t d_f, d_rt, d_up;
|
|
|
|
//VectorCopy( g_entities[0].client->renderInfo.eyePoint, start );
|
|
//AngleVectors( cg_entities[0].lerpAngles, d_f, d_rt, d_up );
|
|
|
|
|
|
VectorMA( start, 2048, d_f, end );//4028 is max for mind trick
|
|
|
|
//YES! This is very very bad... but it works! James made me do it. Really, he did. Blame James.
|
|
gi.trace( &trace, start, vec3_origin, vec3_origin, end,
|
|
ignoreEnt, MASK_OPAQUE|CONTENTS_SHOTCLIP|CONTENTS_BODY|CONTENTS_ITEM, G2_NOCOLLIDE, 10 );// ); took out CONTENTS_SOLID| so you can target people through glass.... took out CONTENTS_CORPSE so disintegrated guys aren't shown, could just remove their body earlier too...
|
|
|
|
if ( trace.entityNum < ENTITYNUM_WORLD )
|
|
{//hit something
|
|
traceEnt = &g_entities[trace.entityNum];
|
|
if ( traceEnt )
|
|
{
|
|
// Check for mind trickable-guys
|
|
if ( traceEnt->client )
|
|
{//is a client
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_TELEPATHY] && traceEnt->health > 0 && VALIDSTRING(traceEnt->behaviorSet[BSET_MINDTRICK]) )
|
|
{//I have the ability to mind-trick and he is alive and he has a mind trick script
|
|
//NOTE: no need to check range since it's always 2048
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
// No? Check for force-push/pullable doors and func_statics
|
|
else if ( traceEnt->s.eType == ET_MOVER )
|
|
{//hit a mover
|
|
if ( !Q_stricmp( "func_door", traceEnt->classname ) )
|
|
{//it's a func_door
|
|
if ( traceEnt->spawnflags & 2/*MOVER_FORCE_ACTIVATE*/ )
|
|
{//it's force-usable
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL] || cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH] )
|
|
{//player has push or pull
|
|
float maxRange;
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL] > cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH] )
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL]];
|
|
}
|
|
else
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH]];
|
|
}
|
|
if ( maxRange >= trace.fraction * 2048 )
|
|
{//actually close enough to use one of our force powers on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( !Q_stricmp( "func_static", traceEnt->classname ) )
|
|
{//it's a func_static
|
|
if ( (traceEnt->spawnflags & 1/*F_PUSH*/) && (traceEnt->spawnflags & 2/*F_PULL*/) )
|
|
{//push or pullable
|
|
float maxRange;
|
|
if ( cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL] > cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH] )
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL]];
|
|
}
|
|
else
|
|
{//use the better range
|
|
maxRange = forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH]];
|
|
}
|
|
if ( maxRange >= trace.fraction * 2048 )
|
|
{//actually close enough to use one of our force powers on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
else if ( (traceEnt->spawnflags & 1/*F_PUSH*/) )
|
|
{//pushable only
|
|
if ( forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PUSH]] >= trace.fraction * 2048 )
|
|
{//actually close enough to use force push on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
else if ( (traceEnt->spawnflags & 2/*F_PULL*/) )
|
|
{//pullable only
|
|
if ( forcePushPullRadius[cg_entities[0].gent->client->ps.forcePowerLevel[FP_PULL]] >= trace.fraction * 2048 )
|
|
{//actually close enough to use force pull on it
|
|
cg_forceCrosshair = qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( !cg_forceCrosshair )
|
|
{
|
|
if ( cg_dynamicCrosshair.integer )
|
|
{//100% accurate
|
|
vec3_t d_f, d_rt, d_up;
|
|
if ( cg.snap->ps.weapon == WP_NONE ||
|
|
cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_STUN_BATON )
|
|
{
|
|
if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
|
|
{//in camera ent view
|
|
ignoreEnt = cg.snap->ps.viewEntity;
|
|
if ( g_entities[cg.snap->ps.viewEntity].client )
|
|
{
|
|
VectorCopy( g_entities[cg.snap->ps.viewEntity].client->renderInfo.eyePoint, start );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( cg_entities[cg.snap->ps.viewEntity].lerpOrigin, start );
|
|
}
|
|
AngleVectors( cg_entities[cg.snap->ps.viewEntity].lerpAngles, d_f, d_rt, d_up );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( g_entities[0].client->renderInfo.eyePoint, start );
|
|
AngleVectors( cg_entities[0].lerpAngles, d_f, d_rt, d_up );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
extern void CalcMuzzlePoint( gentity_t *const ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint, float lead_in );
|
|
AngleVectors( cg_entities[0].lerpAngles, d_f, d_rt, d_up );
|
|
CalcMuzzlePoint( &g_entities[0], d_f, d_rt, d_up, start , 0 );
|
|
}
|
|
//VectorCopy( g_entities[0].client->renderInfo.muzzlePoint, start );
|
|
//FIXME: increase this? Increase when zoom in?
|
|
VectorMA( start, 4096, d_f, end );//was 8192
|
|
}
|
|
else
|
|
{//old way
|
|
VectorCopy( cg.refdef.vieworg, start );
|
|
//FIXME: increase this? Increase when zoom in?
|
|
VectorMA( start, 4096, cg.refdef.viewaxis[0], end );//was 8192
|
|
}
|
|
//YES! This is very very bad... but it works! James made me do it. Really, he did. Blame James.
|
|
gi.trace( &trace, start, vec3_origin, vec3_origin, end,
|
|
ignoreEnt, MASK_OPAQUE|CONTENTS_SHOTCLIP|CONTENTS_BODY|CONTENTS_ITEM, G2_NOCOLLIDE, 10 );// ); took out CONTENTS_SOLID| so you can target people through glass.... took out CONTENTS_CORPSE so disintegrated guys aren't shown, could just remove their body earlier too...
|
|
|
|
/*
|
|
CG_Trace( &trace, start, vec3_origin, vec3_origin, end,
|
|
cg.snap->ps.clientNum, MASK_PLAYERSOLID|CONTENTS_CORPSE|CONTENTS_ITEM );
|
|
*/
|
|
//FIXME: pick up corpses
|
|
if ( trace.startsolid || trace.allsolid )
|
|
{
|
|
// trace should not be allowed to pick up anything if it started solid. I tried actually moving the trace start back, which also worked,
|
|
// but the dynamic cursor drawing caused it to render around the clip of the gun when I pushed the blaster all the way into a wall.
|
|
// It looked quite horrible...but, if this is bad for some reason that I don't know
|
|
trace.entityNum = ENTITYNUM_NONE;
|
|
}
|
|
|
|
traceEnt = &g_entities[trace.entityNum];
|
|
}
|
|
|
|
|
|
// if the object is "dead", don't show it
|
|
/* if ( cg.crosshairClientNum && g_entities[cg.crosshairClientNum].health <= 0 )
|
|
{
|
|
cg.crosshairClientNum = 0;
|
|
return;
|
|
}
|
|
*/
|
|
if ( cg_entities[cg.snap->ps.clientNum].currentState.eFlags & EF_LOCKED_TO_WEAPON ||
|
|
(!Q_stricmp( "misc_panel_turret", g_entities[cg.snap->ps.viewEntity].classname ))) {
|
|
//draw crosshair at endpoint
|
|
CG_DrawCrosshair(trace.endpos);
|
|
}
|
|
|
|
g_crosshairEntNum = trace.entityNum;
|
|
g_crosshairEntDist = 4096*trace.fraction;
|
|
|
|
if ( !traceEnt )
|
|
{
|
|
//not looking at anything
|
|
g_crosshairSameEntTime = 0;
|
|
g_crosshairEntTime = 0;
|
|
}
|
|
else
|
|
{//looking at a valid ent
|
|
//store the distance
|
|
if ( trace.entityNum != g_crosshairEntNum )
|
|
{//new crosshair ent
|
|
g_crosshairSameEntTime = 0;
|
|
}
|
|
else if ( g_crosshairEntDist < 256 )
|
|
{//close enough to start counting how long you've been looking
|
|
g_crosshairSameEntTime += cg.frametime;
|
|
}
|
|
//remember the last time you looked at the person
|
|
g_crosshairEntTime = cg.time;
|
|
}
|
|
|
|
if ( !traceEnt || (traceEnt->s.eFlags & EF_NO_TED) )
|
|
{
|
|
if ( traceEnt && scanAll )
|
|
{
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if the player is in fog, don't show it
|
|
content = cgi_CM_PointContents( trace.endpos, 0 );
|
|
if ( content & CONTENTS_FOG )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if the player is cloaked, don't show it
|
|
if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_CLOAKED ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// update the fade timer
|
|
if ( cg.crosshairClientNum != trace.entityNum )
|
|
{
|
|
infoStringCount = 0;
|
|
}
|
|
|
|
cg.crosshairClientNum = trace.entityNum;
|
|
cg.crosshairClientTime = cg.time;
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CG_DrawCrosshairNames
|
|
=====================
|
|
*/
|
|
static void CG_DrawCrosshairNames( void )
|
|
{
|
|
qboolean scanAll = qfalse;
|
|
centity_t *player = &cg_entities[0];
|
|
|
|
if ( cg_dynamicCrosshair.integer )
|
|
{
|
|
// still need to scan for dynamic crosshair
|
|
CG_ScanForCrosshairEntity( scanAll );
|
|
return;
|
|
}
|
|
|
|
if ( !player->gent )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !player->gent->client )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// scan the known entities to see if the crosshair is sighted on one
|
|
// This is currently being called by the rocket tracking code, so we don't necessarily want to do duplicate traces :)
|
|
CG_ScanForCrosshairEntity( scanAll );
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
static void CG_DrawRocketLocking( int lockEntNum, int lockTime )
|
|
//--------------------------------------------------------------
|
|
{
|
|
gentity_t *gent = &g_entities[lockEntNum];
|
|
|
|
if ( !gent )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int cx, cy;
|
|
vec3_t org;
|
|
static int oldDif = 0;
|
|
|
|
VectorCopy( gent->currentOrigin, org );
|
|
org[2] += (gent->mins[2] + gent->maxs[2]) * 0.5f;
|
|
|
|
if ( CG_WorldCoordToScreenCoord( org, &cx, &cy ))
|
|
{
|
|
// we care about distance from enemy to eye, so this is good enough
|
|
float sz = Distance( gent->currentOrigin, cg.refdef.vieworg ) / 1024.0f;
|
|
|
|
if ( cg.zoomMode > 0 )
|
|
{
|
|
if ( cg.overrides.active & CG_OVERRIDE_FOV )
|
|
{
|
|
sz -= ( cg.overrides.fov - cg_zoomFov ) / 80.0f;
|
|
}
|
|
else
|
|
{
|
|
sz -= ( cg_fov.value - cg_zoomFov ) / 80.0f;
|
|
}
|
|
}
|
|
|
|
if ( sz > 1.0f )
|
|
{
|
|
sz = 1.0f;
|
|
}
|
|
else if ( sz < 0.0f )
|
|
{
|
|
sz = 0.0f;
|
|
}
|
|
|
|
sz = (1.0f - sz) * (1.0f - sz) * 32 + 6;
|
|
|
|
vec4_t color={0.0f,0.0f,0.0f,0.0f};
|
|
|
|
cy += sz * 0.5f;
|
|
|
|
// well now, take our current lock time and divide that by 8 wedge slices to get the current lock amount
|
|
int dif = ( cg.time - g_rocketLockTime ) / ( 1200.0f / 8.0f );
|
|
|
|
if ( dif < 0 )
|
|
{
|
|
oldDif = 0;
|
|
return;
|
|
}
|
|
else if ( dif > 8 )
|
|
{
|
|
dif = 8;
|
|
}
|
|
|
|
// do sounds
|
|
if ( oldDif != dif )
|
|
{
|
|
if ( dif == 8 )
|
|
{
|
|
cgi_S_StartSound( org, 0, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/rocket/lock.wav" ));
|
|
}
|
|
else
|
|
{
|
|
cgi_S_StartSound( org, 0, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/rocket/tick.wav" ));
|
|
}
|
|
}
|
|
|
|
oldDif = dif;
|
|
|
|
for ( int i = 0; i < dif; i++ )
|
|
{
|
|
color[0] = 1.0f;
|
|
color[1] = 0.0f;
|
|
color[2] = 0.0f;
|
|
color[3] = 0.1f * i + 0.2f;
|
|
|
|
cgi_R_SetColor( color );
|
|
|
|
// our slices are offset by about 45 degrees.
|
|
CG_DrawRotatePic( cx - sz, cy - sz, sz, sz, i * 45.0f, cgi_R_RegisterShaderNoMip( "gfx/2d/wedge" ));
|
|
}
|
|
|
|
// we are locked and loaded baby
|
|
if ( dif == 8 )
|
|
{
|
|
color[0] = color[1] = color[2] = sin( cg.time * 0.05f ) * 0.5f + 0.5f;
|
|
color[3] = 1.0f; // this art is additive, so the alpha value does nothing
|
|
|
|
cgi_R_SetColor( color );
|
|
|
|
CG_DrawPic( cx - sz, cy - sz * 2, sz * 2, sz * 2, cgi_R_RegisterShaderNoMip( "gfx/2d/lock" ));
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------
|
|
static void CG_RunRocketLocking( void )
|
|
//------------------------------------
|
|
{
|
|
centity_t *player = &cg_entities[0];
|
|
|
|
// Only bother with this when the player is holding down the alt-fire button of the rocket launcher
|
|
if ( player->currentState.weapon == WP_ROCKET_LAUNCHER )
|
|
{
|
|
if ( player->currentState.eFlags & EF_ALT_FIRING )
|
|
{
|
|
CG_ScanForRocketLock();
|
|
|
|
if ( g_rocketLockEntNum > 0 && g_rocketLockEntNum < ENTITYNUM_WORLD && g_rocketLockTime > 0 )
|
|
{
|
|
CG_DrawRocketLocking( g_rocketLockEntNum, g_rocketLockTime );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// disengage any residual locking
|
|
g_rocketLockEntNum = ENTITYNUM_WORLD;
|
|
g_rocketLockTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawIntermission
|
|
=================
|
|
*/
|
|
static void CG_DrawIntermission( void ) {
|
|
CG_DrawScoreboard();
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CG_DrawSnapshot
|
|
==================
|
|
*/
|
|
static float CG_DrawSnapshot( float y ) {
|
|
char *s;
|
|
int w;
|
|
|
|
s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime,
|
|
cg.latestSnapshotNum, cgs.serverCommandSequence );
|
|
|
|
w = cgi_R_Font_StrLenPixels(s, cgs.media.qhFontSmall, FONT_SCALE);
|
|
|
|
int tempX = 635 - w;
|
|
int tempY = y+2;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX, tempY, s, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
|
|
return y + BIGCHAR_HEIGHT + 10;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CG_DrawFPS
|
|
==================
|
|
*/
|
|
#define FPS_FRAMES 16
|
|
static float CG_DrawFPS( float y ) {
|
|
char *s;
|
|
static unsigned short previousTimes[FPS_FRAMES];
|
|
static unsigned short index;
|
|
static int previous, lastupdate;
|
|
int t, i, fps, total;
|
|
unsigned short frameTime;
|
|
|
|
// don't use serverTime, because that will be drifting to
|
|
// correct for internet lag changes, timescales, timedemos, etc
|
|
t = cgi_Milliseconds();
|
|
frameTime = t - previous;
|
|
previous = t;
|
|
if (t - lastupdate > 50) //don't sample faster than this
|
|
{
|
|
lastupdate = t;
|
|
previousTimes[index % FPS_FRAMES] = frameTime;
|
|
index++;
|
|
}
|
|
// average multiple frames together to smooth changes out a bit
|
|
total = 0;
|
|
for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
|
|
total += previousTimes[i];
|
|
}
|
|
if ( !total ) {
|
|
total = 1;
|
|
}
|
|
fps = 1000 * FPS_FRAMES / total;
|
|
|
|
s = va( "%ifps", fps );
|
|
const int w = cgi_R_Font_StrLenPixels(s, cgs.media.qhFontSmall, FONT_SCALE);
|
|
|
|
int tempX = 635 - w;
|
|
int tempY = y+2;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX, tempY, s, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
|
|
return y + BIGCHAR_HEIGHT + 10;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_DrawTimer
|
|
=================
|
|
*/
|
|
static float CG_DrawTimer( float y ) {
|
|
char *s;
|
|
int w;
|
|
int mins, seconds, tens;
|
|
|
|
seconds = cg.time / 1000;
|
|
mins = seconds / 60;
|
|
seconds -= mins * 60;
|
|
tens = seconds / 10;
|
|
seconds -= tens * 10;
|
|
|
|
s = va( "%i:%i%i", mins, tens, seconds );
|
|
|
|
w = cgi_R_Font_StrLenPixels(s, cgs.media.qhFontSmall, FONT_SCALE);
|
|
|
|
int tempX = 635 - w;
|
|
int tempY = y+2;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX, tempY, s, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
return y + BIGCHAR_HEIGHT + 10;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_DrawAmmoWarning
|
|
=================
|
|
*/
|
|
static void CG_DrawAmmoWarning( void ) {
|
|
char text[1024]={0};
|
|
int w;
|
|
|
|
if ( cg_drawAmmoWarning.integer == 0 ) {
|
|
return;
|
|
}
|
|
|
|
if ( !cg.lowAmmoWarning ) {
|
|
return;
|
|
}
|
|
|
|
if ( weaponData[cg.snap->ps.weapon].ammoIndex == AMMO_NONE )
|
|
{//doesn't use ammo, so no warning
|
|
return;
|
|
}
|
|
|
|
if ( cg.lowAmmoWarning == 2 ) {
|
|
cgi_SP_GetStringTextString( "INGAME_INSUFFICIENTENERGY", text, sizeof(text) );
|
|
} else {
|
|
return;
|
|
//s = "LOW AMMO WARNING";
|
|
}
|
|
|
|
w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontSmall, FONT_SCALE);
|
|
int offset = w / 2;
|
|
int tempX = SCREEN_WIDTH / 2;
|
|
int tempY = 64;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX - offset, tempY, text, colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
}
|
|
|
|
//---------------------------------------
|
|
static qboolean CG_RenderingFromMiscCamera()
|
|
{
|
|
//centity_t *cent;
|
|
|
|
//cent = &cg_entities[cg.snap->ps.clientNum];
|
|
|
|
if ( cg.snap->ps.viewEntity > 0 &&
|
|
cg.snap->ps.viewEntity < ENTITYNUM_WORLD )// cent && cent->gent && cent->gent->client && cent->gent->client->ps.viewEntity)
|
|
{
|
|
// Only check viewEntities
|
|
if ( !Q_stricmp( "misc_camera", g_entities[cg.snap->ps.viewEntity].classname ))
|
|
{
|
|
// Only doing a misc_camera, so check health.
|
|
if ( g_entities[cg.snap->ps.viewEntity].health > 0 )
|
|
{
|
|
CG_DrawPic( 0, 0, 640, 480, cgi_R_RegisterShader( "gfx/2d/workingCamera" ));
|
|
}
|
|
else
|
|
{
|
|
CG_DrawPic( 0, 0, 640, 480, cgi_R_RegisterShader( "gfx/2d/brokenCamera" ));
|
|
}
|
|
// don't render other 2d stuff
|
|
return qtrue;
|
|
}
|
|
if (vr->remote_droid)
|
|
{
|
|
//Render as if we are looking through a camera
|
|
CG_DrawPic( 0, 0, 640, 480, cgi_R_RegisterShader( "gfx/2d/workingCamera" ));
|
|
}
|
|
else if ( !Q_stricmp( "misc_panel_turret", g_entities[cg.snap->ps.viewEntity].classname ))
|
|
{
|
|
// could do a panel turret screen overlay...this is a cheesy placeholder
|
|
CG_DrawPic( 30, 90, 128, 300, cgs.media.turretComputerOverlayShader );
|
|
CG_DrawPic( 610, 90, -128, 300, cgs.media.turretComputerOverlayShader );
|
|
}
|
|
else
|
|
{
|
|
// FIXME: make sure that this assumption is correct...because I'm assuming that I must be a droid.
|
|
CG_DrawPic( 0, 0, 640, 480, cgi_R_RegisterShader( "gfx/2d/droid_view" ));
|
|
}
|
|
}
|
|
|
|
// not in misc_camera, render other stuff.
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean cg_usingInFrontOf = qfalse;
|
|
qboolean CanUseInfrontOf(gentity_t*);
|
|
static void CG_UseIcon()
|
|
{
|
|
if (cg_usableObjectsHint.integer) {
|
|
cg_usingInFrontOf = CanUseInfrontOf(cg_entities[cg.snap->ps.clientNum].gent);
|
|
if (cg_usingInFrontOf)
|
|
{
|
|
cgi_R_SetColor( NULL );
|
|
CG_DrawPic( 50, 285, 64, 64, cgs.media.useableHint );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
CG_DrawZoomBorders
|
|
-------------------------
|
|
*/
|
|
|
|
static void CG_DrawZoomBorders( void )
|
|
{
|
|
vec4_t modulate;
|
|
modulate[0] = modulate[1] = modulate[2] = 0.0f;
|
|
modulate[3] = 1.0f;
|
|
|
|
int bar_height = 80;
|
|
CG_FillRect( 0, 0, 640, bar_height, modulate );
|
|
CG_FillRect( 0, 480 - 80, 640, bar_height, modulate );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
CG_DrawVignette
|
|
==============
|
|
*/
|
|
float currentComfortVignetteValue = 0.0f;
|
|
float filteredViewYawDelta = 0.0f;
|
|
|
|
static void CG_DrawVignette( void )
|
|
{
|
|
playerState_t *ps;
|
|
ps = &cg.snap->ps;
|
|
|
|
cvar_t *vr_comfort_vignette = gi.cvar("vr_comfort_vignette", "0.0", CVAR_ARCHIVE); // defined in VrCvars.h
|
|
if (vr_comfort_vignette->value <= 0.0f || vr_comfort_vignette->value > 1.0f || !cg.zoomMode == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool isMoving = VectorLength(cg.predicted_player_state.velocity) > 30.0;
|
|
// When player is in the air, apply vignette (to prevent throbbing on top of jump)
|
|
bool isInAir = ps->groundEntityNum == ENTITYNUM_NONE;
|
|
cvar_t *vr_turn_mode = gi.cvar("vr_turn_mode", "0", CVAR_ARCHIVE); // defined in VrCvars.h
|
|
// Apply only for smooth turn
|
|
bool isTurning = (vr_turn_mode->integer == 2 || (vr_turn_mode->integer == 1 && vr->third_person));
|
|
if (isTurning) {
|
|
float yawDelta = fabsf(vr->clientview_yaw_delta);
|
|
if (yawDelta > 180)
|
|
{
|
|
yawDelta = fabs(yawDelta - 360);
|
|
}
|
|
filteredViewYawDelta = filteredViewYawDelta * 0.75f + yawDelta * 0.25f;
|
|
isTurning = filteredViewYawDelta > 1;
|
|
}
|
|
|
|
if (isMoving || isInAir || isTurning)
|
|
{
|
|
if (currentComfortVignetteValue < vr_comfort_vignette->value)
|
|
{
|
|
currentComfortVignetteValue += vr_comfort_vignette->value * 0.05;
|
|
if (currentComfortVignetteValue > 1.0f)
|
|
currentComfortVignetteValue = 1.0f;
|
|
}
|
|
} else{
|
|
if (currentComfortVignetteValue > 0.0f)
|
|
currentComfortVignetteValue -= vr_comfort_vignette->value * 0.05;
|
|
}
|
|
|
|
if (currentComfortVignetteValue > 0.0f && currentComfortVignetteValue <= 1.0f)
|
|
{
|
|
int screenWidth = 640; //cg.refdef.width;
|
|
int screenHeight = 480; //cg.refdef.height;
|
|
|
|
int x = (int)(0 + currentComfortVignetteValue * screenWidth / 3.5f);
|
|
int w = (int)(screenWidth - 2 * x);
|
|
int y = (int)(0 + currentComfortVignetteValue * screenHeight / 3.5f);
|
|
int h = (int)(screenHeight - 2 * y);
|
|
|
|
vec4_t black = {0.0, 0.0, 0.0, 1};
|
|
cgi_R_SetColor( black );
|
|
|
|
// sides
|
|
cgi_R_DrawStretchPic( 0, 0, x, screenHeight, 0, 0, 1, 1, cgs.media.whiteShader );
|
|
cgi_R_DrawStretchPic( screenWidth - x, 0, x, screenHeight, 0, 0, 1, 1, cgs.media.whiteShader );
|
|
// top/bottom
|
|
cgi_R_DrawStretchPic( x, 0, screenWidth - x, y, 0, 0, 1, 1, cgs.media.whiteShader );
|
|
cgi_R_DrawStretchPic( x, screenHeight - y, screenWidth - x, y, 0, 0, 1, 1, cgs.media.whiteShader );
|
|
// vignette
|
|
cgi_R_DrawStretchPic( x, y, w, h, 0, 0, 1, 1, cgs.media.vignetteShader );
|
|
|
|
cgi_R_SetColor( NULL );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_Draw2D
|
|
=================
|
|
*/
|
|
extern void CG_SaberClashFlare( void );
|
|
static void CG_Draw2D( void )
|
|
{
|
|
char text[1024]={0};
|
|
int w,y_pos;
|
|
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
|
|
|
|
// if we are taking a levelshot for the menu, don't draw anything
|
|
if ( cg.levelShot )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cg_draw2D.integer == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cg.snap->ps.pm_type == PM_INTERMISSION )
|
|
{
|
|
cg.drawingHUD = CG_HUD_SCALED;
|
|
CG_DrawIntermission();
|
|
cg.drawingHUD = CG_HUD_NORMAL;
|
|
return;
|
|
}
|
|
|
|
if (cg_endcredits.integer)
|
|
{
|
|
if (!CG_Credits_Draw())
|
|
{
|
|
CG_DrawCredits(); // will probably get rid of this soon
|
|
}
|
|
}
|
|
|
|
if (!vr->immersive_cinematics) {
|
|
CGCam_DrawWideScreen();
|
|
}
|
|
|
|
if (cg.zoomMode == 4)
|
|
{
|
|
CG_DrawWeapReticle();
|
|
}
|
|
else if (cg.zoomMode != 0)
|
|
{
|
|
CG_DrawZoomBorders();
|
|
}
|
|
|
|
cg.drawingHUD = CG_HUD_SCALED;
|
|
|
|
CG_DrawBatteryCharge();
|
|
|
|
// Draw this before the text so that any text won't get clipped off
|
|
if ( !in_camera )
|
|
{
|
|
cg.drawingHUD = CG_HUD_ZOOM;
|
|
CG_DrawZoomMask();
|
|
cg.drawingHUD = CG_HUD_SCALED;
|
|
}
|
|
|
|
CG_DrawScrollText();
|
|
|
|
CG_DrawCaptionText( in_camera && vr->immersive_cinematics );
|
|
|
|
if ( in_camera )
|
|
{//still draw the saber clash flare, but nothing else
|
|
cg.drawingHUD = CG_HUD_NORMAL;
|
|
CG_SaberClashFlare();
|
|
return;
|
|
}
|
|
|
|
cg.drawingHUD = CG_HUD_NORMAL;
|
|
if ( CG_RenderingFromMiscCamera())
|
|
{
|
|
// purposely doing an early out when in a misc_camera, change it if needed.
|
|
|
|
// allowing center print when in camera mode, probably just an alpha thing - dmv
|
|
CG_DrawCenterString();
|
|
return;
|
|
}
|
|
cg.drawingHUD = CG_HUD_SCALED;
|
|
|
|
// don't draw any status if dead
|
|
if ( cg.snap->ps.stats[STAT_HEALTH] > 0 )
|
|
{
|
|
CG_DrawVignette();
|
|
|
|
if ( !(cent->gent && cent->gent->s.eFlags & (EF_LOCKED_TO_WEAPON |EF_IN_ATST)))
|
|
{
|
|
CG_DrawIconBackground();
|
|
}
|
|
|
|
CG_DrawMoveSpeedIcon();
|
|
|
|
CG_DrawWeaponSelect();
|
|
|
|
if ( cg.zoomMode == 0 )
|
|
{
|
|
CG_DrawStats();
|
|
}
|
|
CG_DrawAmmoWarning();
|
|
|
|
//CROSSHAIR is now done from the crosshair ent trace
|
|
//if ( !cg.renderingThirdPerson && !cg_dynamicCrosshair.integer ) // disruptor draws it's own crosshair artwork; binocs draw nothing; third person draws its own crosshair
|
|
//{
|
|
// CG_DrawCrosshair( NULL );
|
|
//}
|
|
|
|
|
|
CG_DrawCrosshairNames();
|
|
|
|
CG_RunRocketLocking();
|
|
|
|
CG_DrawInventorySelect();
|
|
|
|
CG_DrawForceSelect();
|
|
|
|
CG_DrawPickupItem();
|
|
|
|
CG_UseIcon();
|
|
}
|
|
CG_SaberClashFlare();
|
|
|
|
float y = 0;
|
|
if (cg_drawSnapshot.integer) {
|
|
y=CG_DrawSnapshot(y);
|
|
}
|
|
if (cg_drawFPS.integer) {
|
|
y=CG_DrawFPS(y);
|
|
}
|
|
if (cg_drawTimer.integer) {
|
|
y=CG_DrawTimer(y);
|
|
}
|
|
|
|
// don't draw center string if scoreboard is up
|
|
if ( !CG_DrawScoreboard() ) {
|
|
CG_DrawCenterString();
|
|
}
|
|
|
|
if (missionInfo_Updated)
|
|
{
|
|
if (cg.predicted_player_state.pm_type != PM_DEAD)
|
|
{
|
|
// Was a objective given?
|
|
/* if ((cg_updatedDataPadForcePower.integer) || (cg_updatedDataPadObjective.integer))
|
|
{
|
|
// How long has the game been running? If within 15 seconds of starting, throw up the datapad.
|
|
if (cg.dataPadLevelStartTime>cg.time)
|
|
{
|
|
// Make it pop up
|
|
if (!in_camera)
|
|
{
|
|
cgi_SendConsoleCommand( "datapad" );
|
|
cg.dataPadLevelStartTime=cg.time; //and don't do it again this level!
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
if (!cg.missionInfoFlashTime)
|
|
{
|
|
cg.missionInfoFlashTime = cg.time + cg_missionInfoFlashTime.integer;
|
|
}
|
|
|
|
if (cg.missionInfoFlashTime < cg.time) // Time's up. They didn't read it.
|
|
{
|
|
cg.missionInfoFlashTime = 0;
|
|
missionInfo_Updated = qfalse;
|
|
|
|
CG_ClearDataPadCvars();
|
|
}
|
|
|
|
cgi_SP_GetStringTextString( "INGAME_DATAPAD_UPDATED", text, sizeof(text) );
|
|
|
|
int x_pos = 0;
|
|
int offset = 0;
|
|
y_pos = (SCREEN_HEIGHT/2)+80;
|
|
if ( cg_missionInfoCentered.integer )
|
|
{
|
|
w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontSmall, FONT_SCALE);
|
|
offset = w / 2;
|
|
x_pos = SCREEN_WIDTH / 2;
|
|
}
|
|
|
|
int tempX = x_pos;
|
|
int tempY = y_pos;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX - offset, tempY, text, colorTable[CT_LTRED1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
|
|
if (cg_updatedDataPadForcePower1.integer)
|
|
{
|
|
y_pos += 25;
|
|
cgi_SP_GetStringTextString("INGAME_NEW_FORCE_POWER_INFO", text, sizeof(text) );
|
|
if ( cg_missionInfoCentered.integer )
|
|
{
|
|
w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontSmall, FONT_SCALE);
|
|
offset = w / 2;
|
|
x_pos = SCREEN_WIDTH / 2;
|
|
}
|
|
|
|
tempX = x_pos;
|
|
tempY = y_pos;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX - offset, tempY, text, colorTable[CT_LTRED1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
}
|
|
|
|
if (cg_updatedDataPadObjective.integer)
|
|
{
|
|
y_pos += 25;
|
|
cgi_SP_GetStringTextString( "INGAME_NEW_OBJECTIVE_INFO", text, sizeof(text) );
|
|
if ( cg_missionInfoCentered.integer )
|
|
{
|
|
w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontSmall, FONT_SCALE);
|
|
offset = w / 2;
|
|
x_pos = SCREEN_WIDTH / 2;
|
|
}
|
|
|
|
tempX = x_pos;
|
|
tempY = y_pos;
|
|
CG_AdjustFrom640Int( &tempX, &tempY, NULL, NULL );
|
|
cgi_R_Font_DrawString(tempX - offset, tempY, text, colorTable[CT_LTRED1], cgs.media.qhFontSmall, -1, FONT_SCALE);
|
|
}
|
|
|
|
// if (cent->gent->client->sess.missionObjectivesShown<3)
|
|
// {
|
|
// CG_DrawProportionalString((SCREEN_WIDTH/2), (SCREEN_HEIGHT/2) + 20, ingame_text[IGT_MISSIONINFO_UPDATED2],
|
|
// CG_PULSE | CG_CENTER| CG_SMALLFONT, colorTable[CT_LTRED1] );
|
|
// }
|
|
}
|
|
}
|
|
|
|
cg.drawingHUD = CG_HUD_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CG_DrawActive
|
|
|
|
Perform all drawing needed to completely fill the screen
|
|
=====================
|
|
*/
|
|
void CG_DrawActive( stereoFrame_t stereoView ) {
|
|
float separation;
|
|
vec3_t baseOrg;
|
|
|
|
// optionally draw the info screen instead
|
|
if ( !cg.snap ) {
|
|
CG_DrawInformation();
|
|
return;
|
|
}
|
|
|
|
if (!vr->item_selector) {
|
|
CG_DrawCrosshair3D(0);
|
|
CG_DrawCrosshair3D(1);
|
|
}
|
|
|
|
//FIXME: these globals done once at start of frame for various funcs
|
|
AngleVectors (cg.refdefViewAngles, vfwd, vright, vup);
|
|
VectorCopy( vfwd, vfwd_n );
|
|
VectorCopy( vright, vright_n );
|
|
VectorCopy( vup, vup_n );
|
|
VectorNormalize( vfwd_n );
|
|
VectorNormalize( vright_n );
|
|
VectorNormalize( vup_n );
|
|
|
|
vr->cgzoommode = cg.zoomMode;
|
|
|
|
switch ( stereoView ) {
|
|
case STEREO_CENTER:
|
|
separation = 0;
|
|
break;
|
|
case STEREO_LEFT:
|
|
separation = cg_worldScale.value * (-cg_stereoSeparation.value / 2);
|
|
break;
|
|
case STEREO_RIGHT:
|
|
separation = cg_worldScale.value * (cg_stereoSeparation.value / 2);
|
|
break;
|
|
default:
|
|
separation = 0;
|
|
CG_Error( "CG_DrawActive: Undefined stereoView" );
|
|
}
|
|
|
|
//Only vehicle in JK2 is the AT-ST
|
|
vr->in_vehicle = (g_entities[0].client &&
|
|
g_entities[0].client->NPC_class == CLASS_ATST);
|
|
vr->remote_npc = !Q_stricmp( "NPC", g_entities[cg.snap->ps.viewEntity].classname );
|
|
vr->remote_droid = false;
|
|
vr->remote_turret = false;
|
|
vr->emplaced_gun = ( cg_entities[cg.snap->ps.clientNum].currentState.eFlags & EF_LOCKED_TO_WEAPON );
|
|
in_misccamera = false;
|
|
|
|
if (cg.snap->ps.viewEntity) {
|
|
|
|
if (g_entities[cg.snap->ps.viewEntity].NPC_type) {
|
|
char modelName[256];
|
|
Q_strncpyz(modelName, g_entities[cg.snap->ps.viewEntity].NPC_type, sizeof modelName);
|
|
|
|
vr->remote_droid = vr->remote_npc &&
|
|
(!Q_stricmp("gonk", modelName) || !Q_stricmp("seeker", modelName) ||
|
|
!Q_stricmp("remote", modelName)
|
|
|| !Q_strncmp("r2d2", modelName, 4) ||
|
|
!Q_strncmp("r5d2", modelName, 4) || !Q_stricmp("mouse", modelName));
|
|
}
|
|
|
|
vr->remote_turret = (!Q_stricmp("misc_panel_turret",
|
|
g_entities[cg.snap->ps.viewEntity].classname));
|
|
in_misccamera = (!Q_stricmp("misc_camera", g_entities[cg.snap->ps.viewEntity].classname))
|
|
|| vr->remote_droid
|
|
|| vr->remote_turret;
|
|
}
|
|
|
|
cg.refdef.worldscale = cg_worldScale.value;
|
|
|
|
bool usingScope = (cg.zoomMode == 2 || cg.zoomMode == 4);
|
|
//Normal 1st person view angles
|
|
if (!in_camera &&
|
|
!in_misccamera &&
|
|
!vr->remote_droid &&
|
|
!vr->remote_npc &&
|
|
!usingScope &&
|
|
!cg.renderingThirdPerson)
|
|
{
|
|
VectorCopy(vr->hmdorientation, cg.refdef.viewangles);
|
|
cg.refdef.viewangles[YAW] = vr->clientviewangles[YAW] +
|
|
SHORT2ANGLE(cg.snap->ps.delta_angles[YAW]);
|
|
AnglesToAxis(cg.refdef.viewangles, cg.refdef.viewaxis);
|
|
}
|
|
|
|
//Controlling an NPC that isn't a droid
|
|
if (vr->remote_npc &&
|
|
!vr->remote_droid)
|
|
{
|
|
if (vr->remote_cooldown > cg.time)
|
|
{
|
|
VectorCopy(cg.refdefViewAngles, vr->remote_angles);
|
|
vr->take_snap = true;
|
|
}
|
|
VectorCopy(vr->hmdorientation, cg.refdef.viewangles);
|
|
cg.refdef.viewangles[YAW] = vr->remote_angles[YAW] + (vr->hmdorientation[YAW] - vr->hmdorientation_snap[YAW]) + (vr->snapTurn - vr->remote_snapTurn);
|
|
AnglesToAxis(cg.refdef.viewangles, cg.refdef.viewaxis);
|
|
}
|
|
|
|
//Sniper/E11 scope
|
|
if (usingScope)
|
|
{
|
|
cg.refdef.viewangles[ROLL] = vr->clientviewangles[ROLL];
|
|
cg.refdef.viewangles[PITCH] = vr->weaponangles[ANGLES_ADJUSTED][PITCH];
|
|
cg.refdef.viewangles[YAW] = vr->clientviewangles[YAW]
|
|
+ vr->weaponangles[ANGLES_ADJUSTED][YAW] + SHORT2ANGLE(cg.snap->ps.delta_angles[YAW]);
|
|
AnglesToAxis(cg.refdef.viewangles, cg.refdef.viewaxis);
|
|
}
|
|
|
|
//Normal 3rd person view angles
|
|
if (!in_camera &&
|
|
!in_misccamera &&
|
|
cg.renderingThirdPerson)
|
|
{
|
|
VectorCopy(vr->hmdorientation, cg.refdef.viewangles);
|
|
cg.refdef.viewangles[YAW] = vr->clientviewangles[YAW] +
|
|
(vr->hmdorientation[YAW] - vr->hmdorientation_first[YAW]) +
|
|
SHORT2ANGLE(cg.snap->ps.delta_angles[YAW]);
|
|
AnglesToAxis(cg.refdef.viewangles, cg.refdef.viewaxis);
|
|
}
|
|
|
|
//Immersive cinematic sequence 6DoF
|
|
if ((in_camera && vr->immersive_cinematics) || vr->emplaced_gun || cg.renderingThirdPerson)
|
|
{
|
|
BG_ConvertFromVR(vr->hmdposition_offset, cg.refdef.vieworg, cg.refdef.vieworg);
|
|
}
|
|
|
|
// clear around the rendered view if sized down
|
|
CG_TileClear();
|
|
|
|
// offset vieworg appropriately if we're doing stereo separation
|
|
VectorCopy( cg.refdef.vieworg, baseOrg );
|
|
if ( separation != 0 && (!in_camera || vr->immersive_cinematics) && !in_misccamera && !usingScope ) {
|
|
VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg );
|
|
}
|
|
|
|
if ( cg.zoomMode == 3 && cg.snap->ps.batteryCharge ) // doing the Light amp goggles thing
|
|
{
|
|
cgi_R_LAGoggles();
|
|
}
|
|
|
|
if (!vr->emplaced_gun && !in_misccamera && !in_camera) {
|
|
//Vertical Positional Movement
|
|
cg.refdef.vieworg[2] -= DEFAULT_PLAYER_HEIGHT;
|
|
cg.refdef.vieworg[2] += (vr->hmdposition[1] + cg_heightAdjust.value) * cg_worldScale.value;
|
|
}
|
|
|
|
// draw 3D view
|
|
cgi_R_RenderScene( &cg.refdef );
|
|
|
|
// restore original viewpoint if running stereo
|
|
if ( separation != 0 ) {
|
|
VectorCopy( baseOrg, cg.refdef.vieworg );
|
|
}
|
|
|
|
// draw status bar and other floating elements
|
|
CG_Draw2D();
|
|
|
|
}
|
|
|