jkxr/Projects/Android/jni/OpenJK/code/cgame/cg_weapons.cpp
2023-08-01 18:39:05 +01:00

3951 lines
107 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/>.
===========================================================================
*/
#include "cg_headers.h"
#include "cg_media.h"
#include "FxScheduler.h"
#include "../game/wp_saber.h"
#include "../game/g_vehicles.h"
#include "../game/anims.h"
#include <bg_local.h>
#include <VrClientInfo.h>
extern void CG_LightningBolt( centity_t *cent, vec3_t origin );
#define PHASER_HOLDFRAME 2
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
const char *CG_DisplayBoxedText(int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
const char *psText, int iFontHandle, float fScale,
const vec4_t v4Color);
/*
=================
CG_RegisterWeapon
The server says this item is used on this level
=================
*/
void CG_RegisterWeapon( int weaponNum ) {
weaponInfo_t *weaponInfo;
gitem_t *item, *ammo;
char path[MAX_QPATH];
vec3_t mins, maxs;
int i;
weaponInfo = &cg_weapons[weaponNum];
// error checking
if ( weaponNum == 0 ) {
return;
}
if ( weaponInfo->registered ) {
return;
}
// clear out the memory we use
memset( weaponInfo, 0, sizeof( *weaponInfo ) );
weaponInfo->registered = qtrue;
// find the weapon in the item list
for ( item = bg_itemlist + 1 ; item->classname ; item++ ) {
if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) {
weaponInfo->item = item;
break;
}
}
// if we couldn't find which weapon this is, give us an error
if ( !item->classname ) {
CG_Error( "Couldn't find item for weapon %s\nNeed to update Items.dat!", weaponData[weaponNum].classname);
}
CG_RegisterItemVisuals( item - bg_itemlist );
// set up in view weapon model
weaponInfo->weaponModel = cgi_R_RegisterModel( weaponData[weaponNum].weaponMdl );
{//in case the weaponmodel isn't _w, precache the _w.glm
char weaponModel[64];
Q_strncpyz (weaponModel, weaponData[weaponNum].weaponMdl, sizeof(weaponModel));
if (char *spot = strstr(weaponModel, ".md3") )
{
*spot = 0;
spot = strstr(weaponModel, "_w");//i'm using the in view weapon array instead of scanning the item list, so put the _w back on
if (!spot)
{
Q_strcat (weaponModel, sizeof(weaponModel), "_w");
}
Q_strcat (weaponModel, sizeof(weaponModel), ".glm"); //and change to ghoul2
}
gi.G2API_PrecacheGhoul2Model( weaponModel ); // correct way is item->world_model
}
if ( weaponInfo->weaponModel == 0 )
{
CG_Error( "Couldn't find weapon model %s for weapon %s\n", weaponData[weaponNum].weaponMdl, weaponData[weaponNum].classname);
return;
}
// calc midpoint for rotation
cgi_R_ModelBounds( weaponInfo->weaponModel, mins, maxs );
for ( i = 0 ; i < 3 ; i++ ) {
weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );
}
// setup the shader we will use for the icon
if (weaponData[weaponNum].weaponIcon[0])
{
weaponInfo->weaponIcon = cgi_R_RegisterShaderNoMip( weaponData[weaponNum].weaponIcon);
weaponInfo->weaponIconNoAmmo = cgi_R_RegisterShaderNoMip( va("%s_na",weaponData[weaponNum].weaponIcon));
}
for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) {
if ( ammo->giType == IT_AMMO && ammo->giTag == weaponData[weaponNum].ammoIndex) {
break;
}
}
if ( ammo->classname && ammo->world_model ) {
weaponInfo->ammoModel = cgi_R_RegisterModel( ammo->world_model );
}
for (i=0; i< weaponData[weaponNum].numBarrels; i++) {
Q_strncpyz( path, weaponData[weaponNum].weaponMdl, sizeof(path) );
COM_StripExtension( path, path, sizeof(path) );
if (i)
{
//char crap[50];
//Com_sprintf(crap, sizeof(crap), "_barrel%d.md3", i+1 );
//strcat ( path, crap );
Q_strcat( path, sizeof(path), va("_barrel%d.md3", i+1) );
}
else
Q_strcat( path, sizeof(path), "_barrel.md3" );
weaponInfo->barrelModel[i] = cgi_R_RegisterModel( path );
}
// set up the world model for the weapon
weaponInfo->weaponWorldModel = cgi_R_RegisterModel( item->world_model );
if ( !weaponInfo->weaponWorldModel) {
weaponInfo->weaponWorldModel = weaponInfo->weaponModel;
}
// set up the hand that holds the in view weapon - assuming we have one
Q_strncpyz( path, weaponData[weaponNum].weaponMdl, sizeof(path) );
COM_StripExtension( path, path, sizeof(path) );
Q_strcat( path, sizeof(path), "_hand.md3" );
weaponInfo->handsModel = cgi_R_RegisterModel( path );
if ( !weaponInfo->handsModel ) {
weaponInfo->handsModel = cgi_R_RegisterModel( "models/weapons2/briar_pistol/briar_pistol_hand.md3" );
}
// register the sounds for the weapon
if (weaponData[weaponNum].firingSnd[0]) {
weaponInfo->firingSound = cgi_S_RegisterSound( weaponData[weaponNum].firingSnd );
}
if (weaponData[weaponNum].altFiringSnd[0]) {
weaponInfo->altFiringSound = cgi_S_RegisterSound( weaponData[weaponNum].altFiringSnd );
}
if (weaponData[weaponNum].stopSnd[0]) {
weaponInfo->stopSound = cgi_S_RegisterSound( weaponData[weaponNum].stopSnd );
}
if (weaponData[weaponNum].chargeSnd[0]) {
weaponInfo->chargeSound = cgi_S_RegisterSound( weaponData[weaponNum].chargeSnd );
}
if (weaponData[weaponNum].altChargeSnd[0]) {
weaponInfo->altChargeSound = cgi_S_RegisterSound( weaponData[weaponNum].altChargeSnd );
}
if (weaponData[weaponNum].selectSnd[0]) {
weaponInfo->selectSound = cgi_S_RegisterSound( weaponData[weaponNum].selectSnd );
}
// give us missile models if we should
if (weaponData[weaponNum].missileMdl[0]) {
weaponInfo->missileModel = cgi_R_RegisterModel(weaponData[weaponNum].missileMdl );
}
if (weaponData[weaponNum].alt_missileMdl[0]) {
weaponInfo->alt_missileModel = cgi_R_RegisterModel(weaponData[weaponNum].alt_missileMdl );
}
if (weaponData[weaponNum].missileSound[0]) {
weaponInfo->missileSound = cgi_S_RegisterSound( weaponData[weaponNum].missileSound );
}
if (weaponData[weaponNum].alt_missileSound[0]) {
weaponInfo->alt_missileSound = cgi_S_RegisterSound( weaponData[weaponNum].alt_missileSound );
}
if (weaponData[weaponNum].missileHitSound[0]) {
weaponInfo->missileHitSound = cgi_S_RegisterSound( weaponData[weaponNum].missileHitSound );
}
if (weaponData[weaponNum].altmissileHitSound[0]) {
weaponInfo->altmissileHitSound = cgi_S_RegisterSound( weaponData[weaponNum].altmissileHitSound );
}
if ( weaponData[weaponNum].mMuzzleEffect[0] )
{
weaponData[weaponNum].mMuzzleEffectID = theFxScheduler.RegisterEffect( weaponData[weaponNum].mMuzzleEffect );
}
if ( weaponData[weaponNum].mAltMuzzleEffect[0] )
{
weaponData[weaponNum].mAltMuzzleEffectID = theFxScheduler.RegisterEffect( weaponData[weaponNum].mAltMuzzleEffect );
}
//fixme: don't really need to copy these, should just use directly
// give ourselves the functions if we can
if (weaponData[weaponNum].func)
{
weaponInfo->missileTrailFunc = (void (*)(struct centity_s *,const struct weaponInfo_s *))weaponData[weaponNum].func;
}
if (weaponData[weaponNum].altfunc)
{
weaponInfo->alt_missileTrailFunc = (void (*)(struct centity_s *,const struct weaponInfo_s *))weaponData[weaponNum].altfunc;
}
switch ( weaponNum ) //extra client only stuff
{
case WP_SABER:
//saber/force FX
theFxScheduler.RegisterEffect( "sparks/spark_nosnd" );//was "sparks/spark"
theFxScheduler.RegisterEffect( "sparks/blood_sparks2" );
theFxScheduler.RegisterEffect( "force/force_touch" );
theFxScheduler.RegisterEffect( "saber/saber_block" );
theFxScheduler.RegisterEffect( "saber/saber_cut" );
//theFxScheduler.RegisterEffect( "saber/limb_bolton" );
theFxScheduler.RegisterEffect( "saber/fizz" );
theFxScheduler.RegisterEffect( "saber/boil" );
//theFxScheduler.RegisterEffect( "saber/fire" );//was "sparks/spark"
cgs.effects.forceHeal = theFxScheduler.RegisterEffect( "force/heal" );
//cgs.effects.forceInvincibility = theFxScheduler.RegisterEffect( "force/invin" );
cgs.effects.forceConfusion = theFxScheduler.RegisterEffect( "force/confusion" );
cgs.effects.forceLightning = theFxScheduler.RegisterEffect( "force/lightning" );
cgs.effects.forceLightningWide = theFxScheduler.RegisterEffect( "force/lightningwide" );
//new Jedi Academy force power effects
cgs.effects.forceDrain = theFxScheduler.RegisterEffect( "mp/drain" );
cgs.effects.forceDrainWide = theFxScheduler.RegisterEffect( "mp/drainwide" );
//cgs.effects.forceDrained = theFxScheduler.RegisterEffect( "mp/drainhit");
//saber sounds
//cgi_S_RegisterSound( "sound/weapons/saber/saberon.wav" );
//cgi_S_RegisterSound( "sound/weapons/saber/enemy_saber_on.wav" );
cgi_S_RegisterSound( "sound/weapons/saber/saberonquick.wav" );
//cgi_S_RegisterSound( "sound/weapons/saber/saberoff.wav" );
//cgi_S_RegisterSound( "sound/weapons/saber/enemy_saber_off.wav" );
cgi_S_RegisterSound( "sound/weapons/saber/saberspinoff.wav" );
cgi_S_RegisterSound( "sound/weapons/saber/saberoffquick.wav" );
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberbounce%d.wav", i ) );
}
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberhit%d.wav", i ) );
}
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberhitwall%d.wav", i ) );
}
for ( i = 1; i < 10; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberblock%d.wav", i ) );
}
/*
for ( i = 1; i < 6; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberhum%d.wav", i ) );
}
*/
for ( i = 1; i < 10; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberhup%d.wav", i ) );
}
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/saberspin%d.wav", i ) );
}
cgi_S_RegisterSound( "sound/weapons/saber/saber_catch.wav" );
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/bounce%d.wav", i ) );
}
cgi_S_RegisterSound( "sound/weapons/saber/hitwater.wav" );
cgi_S_RegisterSound( "sound/weapons/saber/boiling.wav" );
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/saber/rainfizz%d.wav", i ) );
}
cgi_S_RegisterSound( "sound/movers/objects/saber_slam" );
//force sounds
cgi_S_RegisterSound( "sound/weapons/force/heal.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/speed.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/speedloop.mp3" );
for ( i = 1; i < 5; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/force/heal%d_m.mp3", i ) );
cgi_S_RegisterSound( va( "sound/weapons/force/heal%d_f.mp3", i ) );
}
cgi_S_RegisterSound( "sound/weapons/force/lightning.wav" );
cgi_S_RegisterSound( "sound/weapons/force/lightning2.wav" );
for ( i = 1; i < 4; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/force/lightninghit%d.wav", i ) );
}
cgi_S_RegisterSound( "sound/weapons/force/push.wav" );
cgi_S_RegisterSound( "sound/weapons/force/pull.wav" );
cgi_S_RegisterSound( "sound/weapons/force/jump.wav" );
cgi_S_RegisterSound( "sound/weapons/force/jumpbuild.wav" );
cgi_S_RegisterSound( "sound/weapons/force/grip.mp3" );
//new Jedi Academy force sounds
cgi_S_RegisterSound( "sound/weapons/force/absorb.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/absorbhit.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/absorbloop.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/protect.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/protecthit.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/protectloop.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/rage.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/ragehit.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/rageloop.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/see.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/seeloop.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/drain.mp3" );
cgi_S_RegisterSound( "sound/weapons/force/drained.mp3" );
//force graphics
cgs.media.playerShieldDamage = cgi_R_RegisterShader("gfx/misc/personalshield");
//cgs.media.forceSightBubble = cgi_R_RegisterShader("gfx/misc/sightbubble");
//cgs.media.forceShell = cgi_R_RegisterShader("powerups/forceshell");
cgs.media.forceShell = cgi_R_RegisterShader("gfx/misc/forceprotect");
cgs.media.sightShell = cgi_R_RegisterShader("powerups/sightshell");
cgi_R_RegisterShader( "gfx/2d/jsense" );
//force effects - FIXME: only if someone has these powers?
theFxScheduler.RegisterEffect( "force/rage2" );
//theFxScheduler.RegisterEffect( "force/heal_joint" );
theFxScheduler.RegisterEffect( "force/heal2" );
theFxScheduler.RegisterEffect( "force/drain_hand" );
//saber graphics
cgs.media.saberBlurShader = cgi_R_RegisterShader("gfx/effects/sabers/saberBlur");
cgs.media.swordTrailShader = cgi_R_RegisterShader("gfx/effects/sabers/swordTrail");
cgs.media.yellowDroppedSaberShader = cgi_R_RegisterShader("gfx/effects/yellow_glow");
cgi_R_RegisterShader( "gfx/effects/saberDamageGlow" );
cgi_R_RegisterShader( "gfx/effects/solidWhite_cull" );
cgi_R_RegisterShader( "gfx/effects/forcePush" );
cgi_R_RegisterShader( "gfx/effects/saberFlare" );
cgs.media.redSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/red_glow" );
cgs.media.redSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/red_line" );
cgs.media.orangeSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/orange_glow" );
cgs.media.orangeSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/orange_line" );
cgs.media.yellowSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/yellow_glow" );
cgs.media.yellowSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/yellow_line" );
cgs.media.greenSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/green_glow" );
cgs.media.greenSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/green_line" );
cgs.media.blueSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/blue_glow" );
cgs.media.blueSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/blue_line" );
cgs.media.purpleSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/purple_glow" );
cgs.media.purpleSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/purple_line" );
cgs.media.forceCoronaShader = cgi_R_RegisterShaderNoMip( "gfx/hud/force_swirl" );
//new Jedi Academy force graphics
cgs.media.drainShader = cgi_R_RegisterShader( "gfx/misc/redLine" );
//for grip slamming into walls
theFxScheduler.RegisterEffect( "env/impact_dustonly" );
cgi_S_RegisterSound( "sound/weapons/melee/punch1.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch2.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch3.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch4.mp3" );
//For kicks with saber staff...
theFxScheduler.RegisterEffect( "melee/kick_impact" );
//Kothos beam
cgi_R_RegisterShader( "gfx/misc/dr1" );
break;
case WP_BRYAR_PISTOL:
case WP_BLASTER_PISTOL: // enemy version
case WP_JAWA:
cgs.effects.bryarShotEffect = theFxScheduler.RegisterEffect( "bryar/shot" );
theFxScheduler.RegisterEffect( "bryar/NPCshot" );
cgs.effects.bryarPowerupShotEffect = theFxScheduler.RegisterEffect( "bryar/crackleShot" );
cgs.effects.bryarWallImpactEffect = theFxScheduler.RegisterEffect( "bryar/wall_impact" );
cgs.effects.bryarWallImpactEffect2 = theFxScheduler.RegisterEffect( "bryar/wall_impact2" );
cgs.effects.bryarWallImpactEffect3 = theFxScheduler.RegisterEffect( "bryar/wall_impact3" );
cgs.effects.bryarFleshImpactEffect = theFxScheduler.RegisterEffect( "bryar/flesh_impact" );
// Note....these are temp shared effects
theFxScheduler.RegisterEffect( "blaster/deflect" );
theFxScheduler.RegisterEffect( "blaster/smoke_bolton" ); // note: this will be called game side
break;
case WP_BLASTER:
cgs.effects.blasterShotEffect = theFxScheduler.RegisterEffect( "blaster/shot" );
theFxScheduler.RegisterEffect( "blaster/NPCshot" );
// cgs.effects.blasterOverchargeEffect = theFxScheduler.RegisterEffect( "blaster/overcharge" );
cgs.effects.blasterWallImpactEffect = theFxScheduler.RegisterEffect( "blaster/wall_impact" );
cgs.effects.blasterFleshImpactEffect = theFxScheduler.RegisterEffect( "blaster/flesh_impact" );
theFxScheduler.RegisterEffect( "blaster/deflect" );
theFxScheduler.RegisterEffect( "blaster/smoke_bolton" ); // note: this will be called game side
break;
case WP_DISRUPTOR:
theFxScheduler.RegisterEffect( "disruptor/wall_impact" );
theFxScheduler.RegisterEffect( "disruptor/flesh_impact" );
theFxScheduler.RegisterEffect( "disruptor/alt_miss" );
theFxScheduler.RegisterEffect( "disruptor/alt_hit" );
theFxScheduler.RegisterEffect( "disruptor/line_cap" );
theFxScheduler.RegisterEffect( "disruptor/death_smoke" );
cgi_R_RegisterShader( "gfx/effects/redLine" );
cgi_R_RegisterShader( "gfx/misc/whiteline2" );
cgi_R_RegisterShader( "gfx/effects/smokeTrail" );
cgi_R_RegisterShader( "gfx/effects/burn" );
cgi_R_RegisterShaderNoMip( "gfx/2d/crop_charge" );
// zoom sounds
cgi_S_RegisterSound( "sound/weapons/disruptor/zoomstart.wav" );
cgi_S_RegisterSound( "sound/weapons/disruptor/zoomend.wav" );
cgs.media.disruptorZoomLoop = cgi_S_RegisterSound( "sound/weapons/disruptor/zoomloop.wav" );
// Disruptor gun zoom interface
cgs.media.disruptorMask = cgi_R_RegisterShader( "gfx/2d/cropCircle2");
cgs.media.disruptorInsert = cgi_R_RegisterShader( "gfx/2d/cropCircle");
cgs.media.disruptorLight = cgi_R_RegisterShader( "gfx/2d/cropCircleGlow" );
cgs.media.disruptorInsertTick = cgi_R_RegisterShader( "gfx/2d/insertTick" );
break;
case WP_BOWCASTER:
cgs.effects.bowcasterShotEffect = theFxScheduler.RegisterEffect( "bowcaster/shot" );
cgs.effects.bowcasterBounceEffect = theFxScheduler.RegisterEffect( "bowcaster/bounce_wall" );
cgs.effects.bowcasterImpactEffect = theFxScheduler.RegisterEffect( "bowcaster/explosion" );
theFxScheduler.RegisterEffect( "bowcaster/deflect" );
break;
case WP_REPEATER:
theFxScheduler.RegisterEffect( "repeater/muzzle_smoke" );
theFxScheduler.RegisterEffect( "repeater/projectile" );
theFxScheduler.RegisterEffect( "repeater/alt_projectile" );
theFxScheduler.RegisterEffect( "repeater/wall_impact" );
// theFxScheduler.RegisterEffect( "repeater/alt_wall_impact2" );
// theFxScheduler.RegisterEffect( "repeater/flesh_impact" );
theFxScheduler.RegisterEffect( "repeater/concussion" );
break;
case WP_DEMP2:
theFxScheduler.RegisterEffect( "demp2/projectile" );
theFxScheduler.RegisterEffect( "demp2/wall_impact" );
theFxScheduler.RegisterEffect( "demp2/flesh_impact" );
theFxScheduler.RegisterEffect( "demp2/altDetonate" );
cgi_R_RegisterModel( "models/items/sphere.md3" );
cgi_R_RegisterShader( "gfx/effects/demp2shell" );
break;
case WP_ATST_MAIN:
theFxScheduler.RegisterEffect( "atst/shot" );
theFxScheduler.RegisterEffect( "atst/wall_impact" );
theFxScheduler.RegisterEffect( "atst/flesh_impact" );
theFxScheduler.RegisterEffect( "atst/droid_impact" );
break;
case WP_ATST_SIDE:
// For the ALT fire
theFxScheduler.RegisterEffect( "atst/side_alt_shot" );
theFxScheduler.RegisterEffect( "atst/side_alt_explosion" );
// For the regular fire
theFxScheduler.RegisterEffect( "atst/side_main_shot" );
theFxScheduler.RegisterEffect( "atst/side_main_impact" );
break;
case WP_FLECHETTE:
cgs.effects.flechetteShotEffect = theFxScheduler.RegisterEffect( "flechette/shot" );
cgs.effects.flechetteAltShotEffect = theFxScheduler.RegisterEffect( "flechette/alt_shot" );
cgs.effects.flechetteShotDeathEffect = theFxScheduler.RegisterEffect( "flechette/wall_impact" ); // shot death
cgs.effects.flechetteFleshImpactEffect = theFxScheduler.RegisterEffect( "flechette/flesh_impact" );
cgs.effects.flechetteRicochetEffect = theFxScheduler.RegisterEffect( "flechette/ricochet" );
// theFxScheduler.RegisterEffect( "flechette/explosion" );
theFxScheduler.RegisterEffect( "flechette/alt_blow" );
break;
case WP_ROCKET_LAUNCHER:
theFxScheduler.RegisterEffect( "rocket/shot" );
theFxScheduler.RegisterEffect( "rocket/explosion" );
cgi_R_RegisterShaderNoMip( "gfx/2d/wedge" );
cgi_R_RegisterShaderNoMip( "gfx/2d/lock" );
cgi_S_RegisterSound( "sound/weapons/rocket/lock.wav" );
cgi_S_RegisterSound( "sound/weapons/rocket/tick.wav" );
break;
case WP_CONCUSSION:
//Primary
theFxScheduler.RegisterEffect( "concussion/shot" );
theFxScheduler.RegisterEffect( "concussion/explosion" );
//Alt
theFxScheduler.RegisterEffect( "concussion/alt_miss" );
theFxScheduler.RegisterEffect( "concussion/alt_hit" );
theFxScheduler.RegisterEffect( "concussion/alt_ring" );
//not used (eventually)?
cgi_R_RegisterShader( "gfx/effects/blueLine" );
cgi_R_RegisterShader( "gfx/misc/whiteline2" );
break;
case WP_THERMAL:
cgs.media.grenadeBounce1 = cgi_S_RegisterSound( "sound/weapons/thermal/bounce1.wav" );
cgs.media.grenadeBounce2 = cgi_S_RegisterSound( "sound/weapons/thermal/bounce2.wav" );
cgi_S_RegisterSound( "sound/weapons/thermal/thermloop.wav" );
cgi_S_RegisterSound( "sound/weapons/thermal/warning.wav" );
theFxScheduler.RegisterEffect( "thermal/explosion" );
theFxScheduler.RegisterEffect( "thermal/shockwave" );
break;
case WP_TRIP_MINE:
theFxScheduler.RegisterEffect( "tripMine/explosion" );
theFxScheduler.RegisterEffect( "tripMine/laser" );
theFxScheduler.RegisterEffect( "tripMine/laserImpactGlow" );
theFxScheduler.RegisterEffect( "tripMine/glowBit" );
cgs.media.tripMineStickSound = cgi_S_RegisterSound( "sound/weapons/laser_trap/stick.wav" );
cgi_S_RegisterSound( "sound/weapons/laser_trap/warning.wav" );
cgi_S_RegisterSound( "sound/weapons/laser_trap/hum_loop.wav" );
break;
case WP_DET_PACK:
theFxScheduler.RegisterEffect( "detpack/explosion.efx" );
cgs.media.detPackStickSound = cgi_S_RegisterSound( "sound/weapons/detpack/stick.wav" );
cgi_R_RegisterModel( "models/weapons2/detpack/detpack.md3" );
cgi_S_RegisterSound( "sound/weapons/detpack/warning.wav" );
cgi_S_RegisterSound( "sound/weapons/explosions/explode5.wav" );
break;
case WP_EMPLACED_GUN:
theFxScheduler.RegisterEffect( "emplaced/shot" );
theFxScheduler.RegisterEffect( "emplaced/shotNPC" );
theFxScheduler.RegisterEffect( "emplaced/wall_impact" );
//E-Web, too, can't tell here which one you wanted, so...
theFxScheduler.RegisterEffect( "eweb/shot" );
theFxScheduler.RegisterEffect( "eweb/shotNPC" );
theFxScheduler.RegisterEffect( "eweb/wall_impact" );
theFxScheduler.RegisterEffect( "eweb/flesh_impact" );
cgi_R_RegisterShader( "models/map_objects/imp_mine/turret_chair_dmg" );
cgi_R_RegisterShader( "models/map_objects/imp_mine/turret_chair_on" );
cgs.media.emplacedHealthBarShader = cgi_R_RegisterShaderNoMip( "gfx/hud/health_frame" );
cgs.media.turretComputerOverlayShader = cgi_R_RegisterShaderNoMip( "gfx/hud/generic_target" );
cgs.media.turretCrossHairShader = cgi_R_RegisterShaderNoMip( "gfx/2d/panel_crosshair" );
break;
case WP_MELEE:
case WP_TUSKEN_STAFF:
//TEMP
theFxScheduler.RegisterEffect( "melee/punch_impact" );
theFxScheduler.RegisterEffect( "melee/kick_impact" );
cgi_S_RegisterSound( "sound/weapons/melee/punch1.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch2.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch3.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch4.mp3" );
break;
case WP_STUN_BATON:
cgi_R_RegisterShader( "gfx/effects/stunPass" );
theFxScheduler.RegisterEffect( "stunBaton/flesh_impact" );
//TEMP
cgi_S_RegisterSound( "sound/weapons/melee/punch1.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch2.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch3.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch4.mp3" );
cgi_S_RegisterSound( "sound/weapons/baton/fire" );
break;
case WP_TURRET:
theFxScheduler.RegisterEffect( "turret/shot" );
theFxScheduler.RegisterEffect( "turret/wall_impact" );
theFxScheduler.RegisterEffect( "turret/flesh_impact" );
break;
case WP_TUSKEN_RIFLE:
//melee
theFxScheduler.RegisterEffect( "melee/punch_impact" );
cgi_S_RegisterSound( "sound/weapons/melee/punch1.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch2.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch3.mp3" );
cgi_S_RegisterSound( "sound/weapons/melee/punch4.mp3" );
//fire
theFxScheduler.RegisterEffect( "tusken/shot" );
theFxScheduler.RegisterEffect( "tusken/hit" );
theFxScheduler.RegisterEffect( "tusken/hitwall" );
break;
case WP_SCEPTER:
//???
break;
case WP_NOGHRI_STICK:
//fire
theFxScheduler.RegisterEffect( "noghri_stick/shot" );
theFxScheduler.RegisterEffect( "noghri_stick/flesh_impact" );
//explosion
theFxScheduler.RegisterEffect( "noghri_stick/gas_cloud" );
//cgi_S_RegisterSound("sound/weapons/noghri/smoke.wav");
break;
case WP_TIE_FIGHTER:
theFxScheduler.RegisterEffect( "ships/imp_blastershot" );
break;
}
}
/*
=================
CG_RegisterItemVisuals
The server says this item is used on this level
=================
*/
void CG_RegisterItemVisuals( int itemNum ) {
itemInfo_t *itemInfo;
gitem_t *item;
itemInfo = &cg_items[ itemNum ];
if ( itemInfo->registered ) {
return;
}
item = &bg_itemlist[ itemNum ];
memset( itemInfo, 0, sizeof( *itemInfo ) );
itemInfo->registered = qtrue;
itemInfo->models = cgi_R_RegisterModel( item->world_model );
if ( item->icon && item->icon[0] )
{
itemInfo->icon = cgi_R_RegisterShaderNoMip( item->icon );
}
else
{
itemInfo->icon = -1;
}
if ( item->giType == IT_WEAPON )
{
CG_RegisterWeapon( item->giTag );
}
// some ammo types are actually the weapon, like in the case of explosives
if ( item->giType == IT_AMMO )
{
switch( item->giTag )
{
case AMMO_THERMAL:
CG_RegisterWeapon( WP_THERMAL );
break;
case AMMO_TRIPMINE:
CG_RegisterWeapon( WP_TRIP_MINE );
break;
case AMMO_DETPACK:
CG_RegisterWeapon( WP_DET_PACK );
break;
}
}
if ( item->giType == IT_HOLDABLE )
{
// This should be set up to actually work.
switch( item->giTag )
{
case INV_SEEKER:
cgi_S_RegisterSound("sound/chars/seeker/misc/fire.wav");
cgi_S_RegisterSound( "sound/chars/seeker/misc/hiss.wav");
theFxScheduler.RegisterEffect( "env/small_explode");
CG_RegisterWeapon( WP_BLASTER );
break;
case INV_SENTRY:
CG_RegisterWeapon( WP_TURRET );
cgi_S_RegisterSound( "sound/player/use_sentry" );
break;
case INV_ELECTROBINOCULARS:
// Binocular interface
cgs.media.binocularCircle = cgi_R_RegisterShader( "gfx/2d/binCircle" );
cgs.media.binocularMask = cgi_R_RegisterShader( "gfx/2d/binMask" );
cgs.media.binocularArrow = cgi_R_RegisterShader( "gfx/2d/binSideArrow" );
cgs.media.binocularTri = cgi_R_RegisterShader( "gfx/2d/binTopTri" );
cgs.media.binocularStatic = cgi_R_RegisterShader( "gfx/2d/binocularWindow" );
cgs.media.binocularOverlay = cgi_R_RegisterShader( "gfx/2d/binocularNumOverlay" );
break;
case INV_LIGHTAMP_GOGGLES:
// LA Goggles Shaders
cgs.media.laGogglesStatic = cgi_R_RegisterShader( "gfx/2d/lagogglesWindow" );
cgs.media.laGogglesMask = cgi_R_RegisterShader( "gfx/2d/amp_mask" );
cgs.media.laGogglesSideBit = cgi_R_RegisterShader( "gfx/2d/side_bit" );
cgs.media.laGogglesBracket = cgi_R_RegisterShader( "gfx/2d/bracket" );
cgs.media.laGogglesArrow = cgi_R_RegisterShader( "gfx/2d/bracket2" );
break;
case INV_BACTA_CANISTER:
for ( int i = 1; i < 5; i++ )
{
cgi_S_RegisterSound( va( "sound/weapons/force/heal%d_m.mp3", i ) );
cgi_S_RegisterSound( va( "sound/weapons/force/heal%d_f.mp3", i ) );
}
break;
}
}
}
/*
========================================================================================
VIEW WEAPON
========================================================================================
*/
/*
=================
CG_MapTorsoToWeaponFrame
animations MUST match the defined pattern!
the weapon hand animation has 3 anims,
6 frames of attack
4 frames of drop
5 frames of raise
if the torso anim does not match these lengths, it will not animate correctly!
=================
*/
extern qboolean ValidAnimFileIndex ( int index );
int CG_MapTorsoToWeaponFrame( const clientInfo_t *ci, int frame, int animNum, int weaponNum, int firing )
{
// we should use the animNum to map a weapon frame instead of relying on the torso frame
if ( !ValidAnimFileIndex( ci->animFileIndex ) )
{
return 0;
}
animation_t *animations = level.knownAnimFileSets[ci->animFileIndex].animations;
int ret=0;
switch( animNum )
{
case TORSO_WEAPONREADY1:
case TORSO_WEAPONREADY2:
case TORSO_WEAPONREADY3:
case TORSO_WEAPONREADY4:
case TORSO_WEAPONREADY10:
ret = 0;
break;
case TORSO_DROPWEAP1:
if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 5 )
{
ret = frame - animations[animNum].firstFrame + 6;
}
else
{
// assert(0);
}
break;
case TORSO_RAISEWEAP1:
if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 4 )
{
ret = frame - animations[animNum].firstFrame + 6 + 5;
}
else
{
// assert(0);
}
break;
case BOTH_ATTACK1:
case BOTH_ATTACK2:
case BOTH_ATTACK3:
case BOTH_ATTACK4:
if ( frame >= animations[animNum].firstFrame && frame < animations[animNum].firstFrame + 6 )
{
ret = 1 + ( frame - animations[animNum].firstFrame );
}
else
{
// assert(0);
}
break;
default:
break;
}
return ret;
}
/*
==============
CG_CalculateWeaponPosition
==============
*/
void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles )
{
float scale;
int delta;
float fracsin;
VectorCopy( cg.refdef.vieworg, origin );
VectorCopy( cg.refdefViewAngles, angles );
// on odd legs, invert some angles
if ( cg.bobcycle & 1 ) {
scale = -cg.xyspeed;
} else {
scale = cg.xyspeed;
}
// gun angles from bobbing
angles[ROLL] += scale * cg.bobfracsin * 0.0075;
angles[YAW] += scale * cg.bobfracsin * 0.01;
angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.0075;
// drop the weapon when landing
delta = cg.time - cg.landTime;
if ( delta < LAND_DEFLECT_TIME ) {
origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME;
} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
origin[2] += cg.landChange*0.25 *
(LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME;
}
#if 0
// drop the weapon when stair climbing
delta = cg.time - cg.stepTime;
if ( delta < STEP_TIME/2 ) {
origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2);
} else if ( delta < STEP_TIME ) {
origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2);
}
#endif
// idle drift
scale = /*cg.xyspeed + */40;
fracsin = sin( cg.time * 0.001 );
angles[ROLL] += scale * fracsin * 0.01;
angles[YAW] += scale * fracsin * 0.01;
angles[PITCH] += (scale * 0.5f ) * fracsin * 0.01;
}
static vr_weapon_adjustment_t* LoadWeaponAdjustment( int weapon ) {
char cvar_name[64];
vr_weapon_adjustment_t *adjustment = &vr->weaponadjustment[weapon];
if (!adjustment->loaded) {
Com_sprintf(cvar_name, sizeof(cvar_name), "vr_weapon_adjustment_%i", weapon);
char* weapon_adjustment = cgi_Cvar_Get(cvar_name);
if (strlen(weapon_adjustment) > 0) {
sscanf(weapon_adjustment, "%f,%f,%f,%f,%f,%f,%f",
&adjustment->scale,
&adjustment->offset[0],
&adjustment->offset[1],
&adjustment->offset[2],
&adjustment->angles[PITCH],
&adjustment->angles[YAW],
&adjustment->angles[ROLL]);
} else {
adjustment->scale = 1.0f;
adjustment->offset[0] = 0.0f;
adjustment->offset[1] = 0.0f;
adjustment->offset[2] = 0.0f;
adjustment->angles[PITCH] = 0.0f;
adjustment->angles[YAW] = 0.0f;
adjustment->angles[ROLL] = 0.0f;
}
adjustment->loaded = true;
}
return adjustment;
}
static float CG_CalculateWeaponPositionAndScale( playerState_t *ps, vec3_t origin, vec3_t angles ) {
if (cg.renderingThirdPerson)
{
CG_CalculateWeaponPosition(origin, angles);
return 1.0f;
}
BG_CalculateVRWeaponPosition(origin, angles);
vr_weapon_adjustment_t *adjustment = LoadWeaponAdjustment(ps->weapon);
vec3_t offset;
VectorScale(adjustment->offset, adjustment->scale, offset);
vec3_t adjust;
VectorCopy(adjustment->angles, adjust);
if (!vr->right_handed)
{
//yaw needs to go in the other direction as left handed model is reversed
adjust[YAW] *= -1.0f;
}
//Adjust angles for weapon models that aren't aligned very well
matrix4x4 m1, m2, m3;
vec3_t zero;
VectorClear(zero);
Matrix4x4_CreateFromEntity(m1, angles, zero, 1.0);
Matrix4x4_CreateFromEntity(m2, adjust, zero, 1.0);
Matrix4x4_Concat(m3, m1, m2);
Matrix4x4_ConvertToEntity(m3, angles, zero);
//Now move weapon closer to proper origin
vec3_t forward, right, up;
AngleVectors( angles, forward, right, up );
VectorMA( origin, offset[2], forward, origin );
VectorMA( origin, offset[1], up, origin );
if (vr->right_handed) {
VectorMA(origin, offset[0], right, origin);
} else {
VectorMA(origin, -offset[0], right, origin);
}
return adjustment->scale;
}
/*
======================
CG_MachinegunSpinAngle
======================
*/
/*
#define SPIN_SPEED 0.9
#define COAST_TIME 1000
static float CG_MachinegunSpinAngle( centity_t *cent ) {
int delta;
float angle;
float speed;
delta = cg.time - cent->pe.barrelTime;
if ( cent->pe.barrelSpinning ) {
angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
} else {
if ( delta > COAST_TIME ) {
delta = COAST_TIME;
}
speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
angle = cent->pe.barrelAngle + delta * speed;
}
if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) {
cent->pe.barrelTime = cg.time;
cent->pe.barrelAngle = AngleNormalize360( angle );
cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING);
}
return angle;
}
*/
/*
Ghoul2 Insert Start
*/
// set up the appropriate ghoul2 info to a refent
void CG_SetGhoul2InfoRef( refEntity_t *ent, refEntity_t *s1)
{
ent->ghoul2 = s1->ghoul2;
VectorCopy( s1->modelScale, ent->modelScale);
ent->radius = s1->radius;
VectorCopy( s1->angles, ent->angles);
}
//--------------------------------------------------------------------------
static void CG_DoMuzzleFlash( centity_t *cent, vec3_t org, vec3_t dir, weaponData_t *wData )
{
// Handle muzzle flashes, really this could just be a qboolean instead of a time.......
if ( cent->muzzleFlashTime > 0 )
{
if (cg.stereoView == STEREO_RIGHT) {
cent->muzzleFlashTime = 0;
}
const char *effect = NULL;
// CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash");
// Try and get a default muzzle so we have one to fall back on
if ( wData->mMuzzleEffect[0] )
{
effect = &wData->mMuzzleEffect[0];
}
if ( cent->altFire )
{
// We're alt-firing, so see if we need to override with a custom alt-fire effect
if ( wData->mAltMuzzleEffect[0] )
{
effect = &wData->mAltMuzzleEffect[0];
}
}
if (/*( cent->currentState.eFlags & EF_FIRING || cent->currentState.eFlags & EF_ALT_FIRING ) &&*/ effect )
{
if (( cent->gent && cent->gent->NPC ) || cg.renderingThirdPerson )
{
theFxScheduler.PlayEffect( effect, org, dir );
}
else
{
// We got an effect and we're firing, so let 'er rip.
theFxScheduler.PlayEffect( effect, cent->currentState.clientNum );
}
}
}
else
{
// CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash", NULL);
}
}
/*
Ghoul2 Insert End
*/
/*
==============
CG_AddViewWeapon
Add the weapon, and flash for the player's view
==============
*/
extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame );
extern float CG_ForceSpeedFOV( float infov );
void CG_AddViewWeapon( playerState_t *ps )
{
refEntity_t hand;
refEntity_t flash;
vec3_t angles;
const weaponInfo_t *weapon;
weaponData_t *wData;
centity_t *cent;
float fovOffset, leanOffset;
// no gun if in third person view
if ( cg.renderingThirdPerson )
return;
if ( ps->pm_type == PM_INTERMISSION )
return;
cent = &cg_entities[ps->clientNum];
if ( ps->eFlags & EF_LOCKED_TO_WEAPON )
{
return;
}
if ( cent->gent && cent->gent->client && cent->gent->client->ps.forcePowersActive&(1<<FP_LIGHTNING) )
{//doing the electrocuting
vec3_t temp;//tAng, fxDir,
//VectorSet( tAng, cent->pe.torso.pitchAngle, cent->pe.torso.yawAngle, 0 );
VectorCopy( cent->gent->client->renderInfo.handLPoint, temp );
VectorMA( temp, -5, cg.refdef.viewaxis[0], temp );
if ( cent->gent->client->ps.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 )
{//arc
//vec3_t fxAxis[3];
//AnglesToAxis( tAng, fxAxis );
theFxScheduler.PlayEffect( cgs.effects.forceLightningWide, temp, cg.refdef.viewaxis );
}
else
{//line
//AngleVectors( tAng, fxDir, NULL, NULL );
theFxScheduler.PlayEffect( cgs.effects.forceLightning, temp, cg.refdef.viewaxis[0] );
}
}
if ( cent->gent && cent->gent->client && cent->gent->client->ps.forcePowersActive&(1<<FP_DRAIN) )
{//doing the draining
vec3_t temp;//tAng, fxDir,
//VectorSet( tAng, cent->pe.torso.pitchAngle, cent->pe.torso.yawAngle, 0 );
VectorCopy( cent->gent->client->renderInfo.handLPoint, temp );
VectorMA( temp, -5, cg.refdef.viewaxis[0], temp );
if ( cent->gent->client->ps.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_2 )
{//arc
//vec3_t fxAxis[3];
//AnglesToAxis( tAng, fxAxis );
theFxScheduler.PlayEffect( cgs.effects.forceDrainWide, temp, cg.refdef.viewaxis );
}
else
{//line
//AngleVectors( tAng, fxDir, NULL, NULL );
theFxScheduler.PlayEffect( cgs.effects.forceDrain, temp, cg.refdef.viewaxis[0] );
}
}
// allow the gun to be completely removed
if ( !cg_drawGun.integer || cg.zoomMode )
{
vec3_t origin;
// special hack for lightning guns...
VectorCopy( cg.refdef.vieworg, origin );
VectorMA( origin, -10, cg.refdef.viewaxis[2], origin );
VectorMA( origin, 16, cg.refdef.viewaxis[0], origin );
// Doesn't look like we'll have lightning style guns. Clean this crap up when we are sure about this.
// CG_LightningBolt( cent, origin );
// We should still do muzzle flashes though...
CG_RegisterWeapon( ps->weapon );
weapon = &cg_weapons[ps->weapon];
wData = &weaponData[ps->weapon];
CG_DoMuzzleFlash( cent, origin, cg.refdef.viewaxis[0], wData );
// If we don't update this, the muzzle flash point won't even be updated properly
VectorCopy( origin, cent->gent->client->renderInfo.muzzlePoint );
VectorCopy( cg.refdef.viewaxis[0], cent->gent->client->renderInfo.muzzleDir );
cent->gent->client->renderInfo.mPCalcTime = cg.time;
return;
}
// drop gun lower at higher fov
float actualFOV;
if ( (cg.snap->ps.forcePowersActive&(1<<FP_SPEED)) && player->client->ps.forcePowerDuration[FP_SPEED] )//cg.renderingThirdPerson &&
{
actualFOV = CG_ForceSpeedFOV(cg_fov.value);
}
else
{
if ( cg.overrides.active & CG_OVERRIDE_FOV )
actualFOV = cg.overrides.fov;
else {
actualFOV = cg_fovViewmodel.integer ? cg_fovViewmodel.value : cg_fov.value;
}
}
if ( cg_fovViewmodelAdjust.integer && actualFOV > 90 )
fovOffset = -0.1 * ( actualFOV - 80 );
else
fovOffset = 0;
if ( ps->leanofs != 0 )
{ //add leaning offset
leanOffset = ps->leanofs * 0.25f;
fovOffset += abs(ps->leanofs) * -0.1f;
}
else
{
leanOffset = 0;
}
CG_RegisterWeapon( ps->weapon );
weapon = &cg_weapons[ps->weapon];
wData = &weaponData[ps->weapon];
memset (&hand, 0, sizeof(hand));
if ( ps->weapon == WP_STUN_BATON || ps->weapon == WP_CONCUSSION )
{
cgi_S_AddLoopingSound( cent->currentState.number,
cent->lerpOrigin,
vec3_origin,
weapon->firingSound );
}
if (strcmp(cgi_Cvar_Get("vr_control_scheme"), "99") == 0) {
vec3_t origin;
vec3_t startForward, startRight, startUp;
vec3_t endForward, endRight, endUp;
vec3_t _angles;
BG_CalculateVRWeaponPosition( origin, _angles );
vec3_t forward, right, up;
AngleVectors(_angles, forward, right, up);
trace_t trace;
VectorMA(origin, -10, forward, startForward);
VectorMA(origin, 256, forward, endForward);
static vec3_t RED = {1.0f,0.0f,0.0f};
FX_AddLine( -1, startForward, endForward, 0.1f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
RED, RED, 0.0f,
120, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
VectorMA(origin, -10, right, startRight);
VectorMA(origin, 10, right, endRight);
vec3_t BLUE = {0.0f,0.0f,1.0f};
FX_AddLine( -1, startRight, endRight, 0.1f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
BLUE, BLUE, 0.0f,
120, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
VectorMA(origin, -10, up, startUp);
VectorMA(origin, 10, up, endUp);
vec3_t GREEN = {0.0f,1.0f,0.0f};
FX_AddLine( -1, startUp, endUp, 0.1f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
GREEN, GREEN, 0.0f,
120, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
CG_CenterPrint(vr->weaponadjustment_info, 240);
}
// set up gun position
float scale = CG_CalculateWeaponPositionAndScale( ps, hand.origin, angles );
vec3_t extraOffset;
extraOffset[0] = extraOffset[1] = extraOffset[2] = 0.0f;
if( ps->weapon == WP_TUSKEN_RIFLE || ps->weapon == WP_NOGHRI_STICK || ps->weapon == WP_TUSKEN_STAFF )
{
extraOffset[0] = 2;
extraOffset[1] = -3;
extraOffset[2] = -6;
}
if (vr->in_vehicle)
{
//Shunt the origin up if we are in a vehicle to avoid blinding the player with a muzzle flash
VectorMA( hand.origin, 2.0f * cg_worldScale.value, cg.refdef.viewaxis[2], hand.origin );
}
//VectorMA( hand.origin, cg_gun_x.value+extraOffset[0], cg.refdef.viewaxis[0], hand.origin );
//VectorMA( hand.origin, (cg_gun_y.value+leanOffset+extraOffset[1]), cg.refdef.viewaxis[1], hand.origin );
//VectorMA( hand.origin, (cg_gun_z.value+fovOffset+extraOffset[2]), cg.refdef.viewaxis[2], hand.origin );
AnglesToAxis(angles, hand.axis);
//scale the whole model (hand and weapon)
for ( int i = 0; i < 3; i++ ) {
VectorScale( hand.axis[i], (vr->right_handed || i != 1 ) ? scale : -scale, hand.axis[i] );
}
//Gotta move this forward but test for now
VectorCopy( hand.origin, hand.lightingOrigin );
/* if ( cg_fovViewmodel.integer ) {
float fracDistFOV = tanf( cg.refdef.fov_x * ( M_PI/180 ) * 0.5f );
float fracWeapFOV = (1.0f / fracDistFOV) * tanf( actualFOV * (M_PI / 180) * 0.5f );
VectorScale( hand.axis[0], fracWeapFOV, hand.axis[0] );
}
*/
// map torso animations to weapon animations
#ifndef FINAL_BUILD
if ( cg_gun_frame.integer )
{
// development tool
hand.frame = hand.oldframe = cg_gun_frame.integer;
hand.backlerp = 0;
}
else
#endif
{
// get clientinfo for animation map
const clientInfo_t *ci = &cent->gent->client->clientInfo;
int torsoAnim = cent->gent->client->ps.torsoAnim;//pe.torso.animationNumber;
float currentFrame;
int startFrame,endFrame,flags;
float animSpeed;
if (cent->gent->lowerLumbarBone>=0&& gi.G2API_GetBoneAnimIndex(&cent->gent->ghoul2[cent->gent->playerModel], cent->gent->lowerLumbarBone, cg.time, &currentFrame, &startFrame, &endFrame, &flags, &animSpeed,0) )
{
hand.oldframe = CG_MapTorsoToWeaponFrame( ci,floor(currentFrame), torsoAnim, cent->currentState.weapon, ( cent->currentState.eFlags & EF_FIRING ) );
hand.frame = CG_MapTorsoToWeaponFrame( ci,ceil(currentFrame), torsoAnim, cent->currentState.weapon, ( cent->currentState.eFlags & EF_FIRING ) );
hand.backlerp=1.0f-(currentFrame-floor(currentFrame));
if ( cg_debugAnim.integer == 1 && cent->currentState.clientNum == 0 )
{
Com_Printf( "Torso frame %d to %d makes Weapon frame %d to %d\n", cent->pe.torso.oldFrame, cent->pe.torso.frame, hand.oldframe, hand.frame );
}
}
else
{
// assert(0); // no idea what to do here
hand.oldframe=0;
hand.frame=0;
hand.backlerp=0.0f;
}
}
// add the weapon(s) - FIXME: allow for 2 weapons generically, not just 2 sabers?
int numSabers = 1;
if ( cent->gent->client->ps.dualSabers )
{
numSabers = 2;
}
for ( int saberNum = 0; saberNum < numSabers; saberNum++ )
{
refEntity_t gun;
memset (&gun, 0, sizeof(gun));
gun.hModel = weapon->weaponModel;
if (!gun.hModel)
{
return;
}
AnglesToAxis( angles, gun.axis );
CG_PositionEntityOnTag( &gun, &hand, weapon->handsModel, "tag_weapon");
gun.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_VRVIEWMODEL;
//---------
// OK, we are making an assumption here that if we have the phaser that it is always on....
//FIXME: if saberInFlight, need to draw empty hand guiding it
if ( cent->gent && cent->gent->client && cent->currentState.weapon == WP_SABER )
{
vec3_t org_, axis_[3];
for ( int bladeNum = 0; bladeNum < cent->gent->client->ps.saber[saberNum].numBlades; bladeNum++ )
{
//FIXME: need to get from tag_flash2 for saberstaff's second blade?
CG_GetTagWorldPosition( &gun, "tag_flash", org_, axis_ );
//loop this and do for both blades
if ( cent->gent->client->ps.saber[0].blade[0].active && cent->gent->client->ps.saber[0].blade[0].length < cent->gent->client->ps.saber[0].blade[0].lengthMax )
{
cent->gent->client->ps.saber[0].blade[0].length += cg.frametime*0.03;
if ( cent->gent->client->ps.saber[0].blade[0].length > cent->gent->client->ps.saber[0].blade[0].lengthMax )
{
cent->gent->client->ps.saber[0].blade[0].length = cent->gent->client->ps.saber[0].blade[0].lengthMax;
}
}
// FX_Saber( org_, axis_[0], cent->gent->client->ps.saberLength, 2.0 + Q_flrand(-1.0f, 1.0f) * 0.2f, cent->gent->client->ps.saberColor );
if ( saberNum == 0 && bladeNum == 0 )
{
VectorCopy( axis_[0], cent->gent->client->renderInfo.muzzleDir );
}
else
{//need these points stored here when in 1st person saber
VectorCopy(org_, cent->gent->client->ps.saber[saberNum].blade[bladeNum].muzzlePoint);
}
VectorCopy( axis_[0], cent->gent->client->ps.saber[saberNum].blade[bladeNum].muzzleDir );
}
}
//---------
CG_AddRefEntityWithPowerups( &gun, cent->currentState.powerups, cent, true );
// cgi_R_AddRefEntityToScene( &gun );
/* if ( ps->weapon == WP_STUN_BATON )
{
gun.shaderRGBA[0] = gun.shaderRGBA[1] = gun.shaderRGBA[2] = 25;
gun.customShader = cgi_R_RegisterShader( "gfx/effects/stunPass" );
gun.renderfx = RF_RGB_TINT | RF_FIRST_PERSON | RF_DEPTHHACK;
cgi_R_AddRefEntityToScene( &gun );
}
*/
// add the spinning barrel[s]
for (int i = 0; (i < wData->numBarrels); i++)
{
refEntity_t barrel;
memset( &barrel, 0, sizeof( barrel ) );
barrel.hModel = weapon->barrelModel[i];
//VectorCopy( parent->lightingOrigin, barrel.lightingOrigin );
//barrel.shadowPlane = parent->shadowPlane;
barrel.renderfx = gun.renderfx;
angles[YAW] = 0;
angles[PITCH] = 0;
// if ( ps->weapon == WP_TETRION_DISRUPTOR) {
// angles[ROLL] = CG_MachinegunSpinAngle( cent );
// } else {
angles[ROLL] = 0;//CG_MachinegunSpinAngle( cent );
// }
AnglesToAxis( angles, barrel.axis );
if (!i)
{
CG_PositionRotatedEntityOnTag( &barrel, &hand, weapon->handsModel, "tag_barrel", NULL );
} else
{
CG_PositionRotatedEntityOnTag( &barrel, &hand, weapon->handsModel, va("tag_barrel%d",i+1), NULL );
}
cgi_R_AddRefEntityToScene( &barrel );
}
memset (&flash, 0, sizeof(flash));
// Seems like we should always do this in case we have an animating muzzle flash....that way we can always store the correct muzzle dir, etc.
CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash");
CG_DoMuzzleFlash( cent, flash.origin, flash.axis[0], wData );
if ( cent->gent && cent->gent->client )
{
if ( saberNum == 0 )
{
VectorCopy(flash.origin, cent->gent->client->renderInfo.muzzlePoint);
VectorCopy(flash.axis[0], cent->gent->client->renderInfo.muzzleDir);
}
// VectorNormalize( cent->gent->client->renderInfo.muzzleDir );
cent->gent->client->renderInfo.mPCalcTime = cg.time;
//CG_LightningBolt( cent, flash.origin );
}
}
// Do special charge bits
//-----------------------
if (( ps->weaponstate == WEAPON_CHARGING_ALT && ps->weapon == WP_BRYAR_PISTOL )
|| ( ps->weaponstate == WEAPON_CHARGING_ALT && ps->weapon == WP_BLASTER_PISTOL )
|| ( ps->weapon == WP_BOWCASTER && ps->weaponstate == WEAPON_CHARGING )
|| ( ps->weapon == WP_DEMP2 && ps->weaponstate == WEAPON_CHARGING_ALT ))
{
int shader = 0;
float val = 0.0f, scale = 1.0f;
vec3_t WHITE = {1.0f,1.0f,1.0f};
if ( ps->weapon == WP_BRYAR_PISTOL
|| ps->weapon == WP_BLASTER_PISTOL )
{
// Hardcoded max charge time of 1 second
val = ( cg.time - ps->weaponChargeTime ) * 0.001f;
shader = cgi_R_RegisterShader( "gfx/effects/bryarFrontFlash" );
}
else if ( ps->weapon == WP_BOWCASTER )
{
// Hardcoded max charge time of 1 second
val = ( cg.time - ps->weaponChargeTime ) * 0.001f;
shader = cgi_R_RegisterShader( "gfx/effects/greenFrontFlash" );
}
else if ( ps->weapon == WP_DEMP2 )
{
// Hardcoded max charge time of 1 second
val = ( cg.time - ps->weaponChargeTime ) * 0.001f;
shader = cgi_R_RegisterShader( "gfx/misc/lightningFlash" );
scale = 1.75f;
}
if ( val < 0.0f )
{
val = 0.0f;
}
else if ( val > 1.0f )
{
val = 1.0f;
}
int position = vr->weapon_stabilised ? 4 : (vr->right_handed ? 2 : 1);
cgi_HapticEvent("RTCWQuest:fire_tesla", position, 0, 60 * val, 0, 0);
val += Q_flrand(0.0f, 1.0f) * 0.5f;
FX_AddSprite( flash.origin, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, Q_flrand(0.0f, 1.0f) * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA | FX_DEPTH_HACK );
}
// Check if the heavy repeater is finishing up a sustained burst
//-------------------------------
if ( ps->weapon == WP_REPEATER && ps->weaponstate == WEAPON_FIRING )
{
if ( cent->gent && cent->gent->client && cent->gent->client->ps.weaponstate != WEAPON_FIRING )
{
int ct = 0;
// the more continuous shots we've got, the more smoke we spawn
if ( cent->gent->client->ps.weaponShotCount > 60 ) {
ct = 5;
}
else if ( cent->gent->client->ps.weaponShotCount > 35 ) {
ct = 3;
}
else if ( cent->gent->client->ps.weaponShotCount > 15 ) {
ct = 1;
}
for ( int i = 0; i < ct; i++ )
{
theFxScheduler.PlayEffect( "repeater/muzzle_smoke", cent->currentState.clientNum );
}
cent->gent->client->ps.weaponShotCount = 0;
}
}
}
/*
==============================================================================
WEAPON SELECTION
==============================================================================
*/
/*
===================
CG_WeaponCheck
===================
*/
int CG_WeaponCheck( int weaponIndex )
{
int value;
if ( weaponIndex == WP_SABER)
{
return qtrue;
}
value = weaponData[weaponIndex].energyPerShot < weaponData[weaponIndex].altEnergyPerShot
? weaponData[weaponIndex].energyPerShot
: weaponData[weaponIndex].altEnergyPerShot;
if( !cg.snap )
{
return qfalse;
}
// check how much energy(ammo) it takes to fire this weapon against how much ammo we have
if ( value > cg.snap->ps.ammo[weaponData[weaponIndex].ammoIndex] )
{
value = qfalse;
}
else
{
value = qtrue;
}
return value;
}
/*
===================
CG_DrawMoveSpeedIcon
===================
*/
void CG_DrawMoveSpeedIcon(void) {
if ((cg.zoomMode != 0) || !(cg_drawHUD.integer)) {
return;
}
if ((cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD)) {
return;
}
if (cg.moveSpeedSelect != vr->move_speed)
{
cg.moveSpeedSelect = vr->move_speed;
cg.moveSpeedSelectTime = cg.time;
}
if (((cg.moveSpeedSelectTime+WEAPON_SELECT_TIME)>cg.time)) {
cgi_R_SetColor(colorTable[CT_WHITE]);
CG_DrawPic(96, 64, 48, 64, cgs.media.iconMoveSpeed[cg.moveSpeedSelect]);
}
}
int cgi_UI_GetItemText(char *menuFile,char *itemName, char *text);
const char *weaponDesc[13] =
{
"SABER_DESC",
"NEW_BLASTER_PISTOL_DESC",
"BLASTER_RIFLE_DESC",
"DISRUPTOR_RIFLE_DESC",
"BOWCASTER_DESC",
"HEAVYREPEATER_DESC",
"DEMP2_DESC",
"FLECHETTE_DESC",
"MERR_SONN_DESC",
"THERMAL_DETONATOR_DESC",
"TRIP_MINE_DESC",
"DET_PACK_DESC",
"CONCUSSION_DESC",
};
/*
===================
CG_DrawDataPadWeaponSelect
Allows user to cycle through the various weapons currently owned and view the description
===================
*/
void CG_DrawDataPadWeaponSelect( void )
{
int i;
int weaponBitFlag,weaponCount,weaponSelectI;
int holdX;
int sideLeftIconCnt,sideRightIconCnt;
int holdCount,iconCnt;
char text[1024]={0};
qboolean drewConc = qfalse;
// showing weapon select clears pickup item display, but not the blend blob
cg.itemPickupTime = 0;
weaponBitFlag = cg.snap->ps.stats[ STAT_WEAPONS ];
// count the number of weapons owned
weaponCount = 0;
for ( i = 1 ; i < 16 ; i++ )
{
if ( weaponBitFlag & ( 1 << i ) )
{
weaponCount++;
}
}
if (weaponCount == 0) // If no weapons, don't display
{
return;
}
const short sideMax = 3; // Max number of icons on the side
// Calculate how many icons will appear to either side of the center one
holdCount = weaponCount - 1; // -1 for the center icon
if (holdCount == 0) // No icons to either side
{
sideLeftIconCnt = 0;
sideRightIconCnt = 0;
}
else if (weaponCount > (2*sideMax)) // Go to the max on each side
{
sideLeftIconCnt = sideMax;
sideRightIconCnt = sideMax;
}
else // Less than max, so do the calc
{
sideLeftIconCnt = holdCount/2;
sideRightIconCnt = holdCount - sideLeftIconCnt;
}
// This seems to be a problem if datapad comes up too early
if (cg.DataPadWeaponSelect<FIRST_WEAPON)
{
cg.DataPadWeaponSelect = FIRST_WEAPON;
}
else if (cg.DataPadWeaponSelect>13)
{
cg.DataPadWeaponSelect = 13;
}
// What weapon does the player currently have selected
if ( cg.DataPadWeaponSelect == WP_CONCUSSION )
{
weaponSelectI = WP_FLECHETTE;
}
else
{
weaponSelectI = cg.DataPadWeaponSelect - 1;
}
if (weaponSelectI<1)
{
weaponSelectI = 13;
}
const int smallIconSize = 40;
const int bigIconSize = 80;
const int bigPad = 64;
const int pad = 32;
const int centerXPos = 320;
const int graphicYPos = 340;
// Left side ICONS
// Work backwards from current icon
holdX = centerXPos - ((bigIconSize/2) + bigPad + smallIconSize);
cgi_R_SetColor( colorTable[CT_WHITE] );
for (iconCnt=1;iconCnt<(sideLeftIconCnt+1);weaponSelectI--)
{
if ( weaponSelectI == WP_CONCUSSION )
{
weaponSelectI--;
}
else if ( weaponSelectI == WP_FLECHETTE && !drewConc && cg.DataPadWeaponSelect != WP_CONCUSSION )
{
weaponSelectI = WP_CONCUSSION;
}
if (weaponSelectI<1)
{
weaponSelectI = 13;
}
if ( !(weaponBitFlag & ( 1 << weaponSelectI ))) // Does he have this weapon?
{
if ( weaponSelectI == WP_CONCUSSION )
{
drewConc = qtrue;
weaponSelectI = WP_ROCKET_LAUNCHER;
}
continue;
}
++iconCnt; // Good icon
if (weaponData[weaponSelectI].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( weaponSelectI );
weaponInfo = &cg_weapons[weaponSelectI];
if (!CG_WeaponCheck(weaponSelectI))
{
CG_DrawPic( holdX, graphicYPos, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, graphicYPos, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX -= (smallIconSize+pad);
}
if ( weaponSelectI == WP_CONCUSSION )
{
drewConc = qtrue;
weaponSelectI = WP_ROCKET_LAUNCHER;
}
}
// Current Center Icon
cgi_R_SetColor(colorTable[CT_WHITE]);
if (weaponData[cg.DataPadWeaponSelect].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( cg.DataPadWeaponSelect );
weaponInfo = &cg_weapons[cg.DataPadWeaponSelect];
// Draw graphic to show weapon has ammo or no ammo
if (!CG_WeaponCheck(cg.DataPadWeaponSelect))
{
CG_DrawPic( centerXPos-(bigIconSize/2), (graphicYPos-((bigIconSize-smallIconSize)/2))+10, bigIconSize, bigIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( centerXPos-(bigIconSize/2), (graphicYPos-((bigIconSize-smallIconSize)/2))+10, bigIconSize, bigIconSize, weaponInfo->weaponIcon );
}
}
if ( cg.DataPadWeaponSelect == WP_CONCUSSION )
{
weaponSelectI = WP_ROCKET_LAUNCHER;
}
else
{
weaponSelectI = cg.DataPadWeaponSelect + 1;
}
if (weaponSelectI> 13)
{
weaponSelectI = 1;
}
// Right side ICONS
// Work forwards from current icon
cgi_R_SetColor(colorTable[CT_WHITE]);
holdX = centerXPos + (bigIconSize/2) + bigPad;
for (iconCnt=1;iconCnt<(sideRightIconCnt+1);weaponSelectI++)
{
if ( weaponSelectI == WP_CONCUSSION )
{
weaponSelectI++;
}
else if ( weaponSelectI == WP_ROCKET_LAUNCHER && !drewConc && cg.DataPadWeaponSelect != WP_CONCUSSION )
{
weaponSelectI = WP_CONCUSSION;
}
if (weaponSelectI>13)
{
weaponSelectI = 1;
}
if ( !(weaponBitFlag & ( 1 << weaponSelectI ))) // Does he have this weapon?
{
if ( weaponSelectI == WP_CONCUSSION )
{
drewConc = qtrue;
weaponSelectI = WP_FLECHETTE;
}
continue;
}
++iconCnt; // Good icon
if (weaponData[weaponSelectI].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( weaponSelectI );
weaponInfo = &cg_weapons[weaponSelectI];
// Draw graphic to show weapon has ammo or no ammo
if (!CG_WeaponCheck(i))
{
CG_DrawPic( holdX, graphicYPos, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, graphicYPos, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX += (smallIconSize+pad);
}
if ( weaponSelectI == WP_CONCUSSION )
{
drewConc = qtrue;
weaponSelectI = WP_FLECHETTE;
}
}
// Print the weapon description
cgi_SP_GetStringTextString( va("SP_INGAME_%s",weaponDesc[cg.DataPadWeaponSelect-1]), text, sizeof(text) );
if (text[0])
{
const short textboxXPos = 40;
const short textboxYPos = 60;
const int textboxWidth = 560;
const int textboxHeight = 300;
const float textScale = 1.0f;
CG_DisplayBoxedText(
textboxXPos, textboxYPos,
textboxWidth, textboxHeight,
text,
4,
textScale,
colorTable[CT_WHITE]
);
}
cgi_R_SetColor( NULL );
}
/*
===================
CG_DrawDataPadIconBackground
Draw the proper background graphic for the icons being displayed on the datapad
===================
*/
void CG_DrawDataPadIconBackground(const int backgroundType)
{
// const int graphicXPos = 40;
// const int graphicYPos = 340;
// const short graphicHeight = 60;
// const short graphicWidth = 560;
// qhandle_t background;
/*
if (backgroundType == ICON_INVENTORY) // Display inventory background?
{
background = cgs.media.inventoryIconBackground;
}
else if (backgroundType == ICON_WEAPONS) // Display weapon background?
{
background = cgs.media.weaponIconBackground;
}
else // Display force background?
{
background = cgs.media.forceIconBackground;
}
cgi_R_SetColor( colorTable[CT_WHITE] ); // Let the graphic set the color
CG_DrawPic( graphicXPos,
graphicYPos+(graphicHeight/2),
graphicWidth,
-graphicHeight,
background); // Top half
CG_DrawPic( graphicXPos,
graphicYPos+(graphicHeight/2),
graphicWidth,
graphicHeight,
background); // Bottom half
*/
}
/*
===============
SetWeaponSelectTime
===============
*/
void SetWeaponSelectTime(void)
{
if (((cg.inventorySelectTime + WEAPON_SELECT_TIME) > cg.time) || // The Inventory HUD was currently active to just swap it out with Force HUD
((cg.forcepowerSelectTime + WEAPON_SELECT_TIME) > cg.time)) // The Force HUD was currently active to just swap it out with Force HUD
{
cg.inventorySelectTime = 0;
cg.forcepowerSelectTime = 0;
cg.weaponSelectTime = cg.time + 130.0f;
}
else
{
cg.weaponSelectTime = cg.time;
}
}
/*
===================
CG_DrawWeaponSelect
===================
*/
extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
extern bool G_IsRidingTurboVehicle( gentity_t *ent );
void CG_DrawWeaponSelect( void )
{
int i;
int bits;
int count;
int smallIconSize,bigIconSize;
int holdX,x,y,x2,y2,w2,h2,pad;
int sideLeftIconCnt,sideRightIconCnt;
int sideMax,holdCount,iconCnt;
//int height;
vec4_t calcColor;
vec4_t textColor = { .875f, .718f, .121f, 1.0f };
int yOffset = 0;
bool isOnVeh = false;
qboolean drewConc = qfalse;
if ((cg.weaponSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
{
return;
}
// don't display if dead
if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 )
{
return;
}
cg.iconSelectTime = cg.weaponSelectTime;
// showing weapon select clears pickup item display, but not the blend blob
//cg.itemPickupTime = 0;
bits = cg.snap->ps.stats[ STAT_WEAPONS ];
// count the number of weapons owned
count = 0;
isOnVeh = (G_IsRidingVehicle(cg_entities[0].gent)!=0);
for ( i = 1 ; i < MAX_PLAYER_WEAPONS ; i++ )
{
if ((bits & ( 1 << i )) &&
(!isOnVeh || i==WP_NONE || i==WP_SABER || i==WP_BLASTER))
{
count++;
}
}
if (count == 0) // If no weapons, don't display
{
return;
}
sideMax = 3; // Max number of icons on the side
// Calculate how many icons will appear to either side of the center one
holdCount = count - 1; // -1 for the center icon
if (holdCount == 0) // No icons to either side
{
sideLeftIconCnt = 0;
sideRightIconCnt = 0;
}
else if (count > (2*sideMax)) // Go to the max on each side
{
sideLeftIconCnt = sideMax;
sideRightIconCnt = sideMax;
}
else // Less than max, so do the calc
{
sideLeftIconCnt = holdCount/2;
sideRightIconCnt = holdCount - sideLeftIconCnt;
}
if ( cg.weaponSelect == WP_CONCUSSION )
{
i = WP_FLECHETTE;
}
else
{
i = cg.weaponSelect - 1;
}
if (i<1)
{
i = MAX_PLAYER_WEAPONS;
}
smallIconSize = 40;
bigIconSize = 80;
pad = 12;
if (!cgi_UI_GetMenuInfo("weaponselecthud",&x2,&y2,&w2,&h2))
{
return;
}
x = 320;
y = 410;
// Background
memcpy(calcColor, colorTable[CT_WHITE], sizeof(vec4_t));
calcColor[3] = .60f;
cgi_R_SetColor( calcColor);
// Left side ICONS
cgi_R_SetColor( calcColor);
// Work backwards from current icon
holdX = x - ((bigIconSize/2) + pad + smallIconSize);
//height = smallIconSize * cg.iconHUDPercent;
drewConc = qfalse;
for (iconCnt=1;iconCnt<(sideLeftIconCnt+1);i--)
{
if ( i == WP_CONCUSSION )
{
i--;
}
else if ( i == WP_FLECHETTE && !drewConc && cg.weaponSelect != WP_CONCUSSION )
{
i = WP_CONCUSSION;
}
if (i<1)
{
i = MAX_PLAYER_WEAPONS;
}
if ( !(bits & ( 1 << i ))) // Does he have this weapon?
{
if ( i == WP_CONCUSSION )
{
drewConc = qtrue;
i = WP_ROCKET_LAUNCHER;
}
continue;
}
if (isOnVeh)
{
if ( i != WP_NONE && i!=WP_SABER && i!=WP_BLASTER )
{
if ( i == WP_CONCUSSION )
{
drewConc = qtrue;
i = WP_ROCKET_LAUNCHER;
}
continue; // Don't draw anything else if on a vehicle
}
}
++iconCnt; // Good icon
if (weaponData[i].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( i );
weaponInfo = &cg_weapons[i];
if (!CG_WeaponCheck(i))
{
CG_DrawPic( holdX, y+10+yOffset, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, y+10+yOffset, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX -= (smallIconSize+pad);
}
if ( i == WP_CONCUSSION )
{
drewConc = qtrue;
i = WP_ROCKET_LAUNCHER;
}
}
// Current Center Icon
//height = bigIconSize * cg.iconHUDPercent;
cgi_R_SetColor(NULL);
if (weaponData[cg.weaponSelect].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( cg.weaponSelect );
weaponInfo = &cg_weapons[cg.weaponSelect];
if (!CG_WeaponCheck(cg.weaponSelect))
{
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2))+10+yOffset, bigIconSize, bigIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2))+10+yOffset, bigIconSize, bigIconSize, weaponInfo->weaponIcon );
}
}
if ( cg.weaponSelect == WP_CONCUSSION )
{
i = WP_ROCKET_LAUNCHER;
}
else
{
i = cg.weaponSelect + 1;
}
if (i> MAX_PLAYER_WEAPONS)
{
i = 1;
}
// Right side ICONS
// Work forwards from current icon
cgi_R_SetColor( calcColor);
holdX = x + (bigIconSize/2) + pad;
//height = smallIconSize * cg.iconHUDPercent;
drewConc = qfalse;
for (iconCnt=1;iconCnt<(sideRightIconCnt+1);i++)
{
if ( i == WP_CONCUSSION )
{
i++;
}
else if ( i == WP_ROCKET_LAUNCHER && !drewConc && cg.weaponSelect != WP_CONCUSSION )
{
i = WP_CONCUSSION;
}
if (i>MAX_PLAYER_WEAPONS)
{
i = 1;
}
if ( !(bits & ( 1 << i ))) // Does he have this weapon?
{
if ( i == WP_CONCUSSION )
{
drewConc = qtrue;
i = WP_FLECHETTE;
}
continue;
}
if (isOnVeh)
{
if ( i != WP_NONE && i!=WP_SABER && i!=WP_BLASTER )
{
if ( i == WP_CONCUSSION )
{
drewConc = qtrue;
i = WP_FLECHETTE;
}
continue; // Don't draw anything else if on a vehicle
}
}
++iconCnt; // Good icon
if (weaponData[i].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( i );
weaponInfo = &cg_weapons[i];
// No ammo for this weapon?
if (!CG_WeaponCheck(i))
{
CG_DrawPic( holdX, y+10+yOffset, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, y+10+yOffset, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX += (smallIconSize+pad);
}
if ( i == WP_CONCUSSION )
{
drewConc = qtrue;
i = WP_FLECHETTE;
}
}
gitem_t *item = cg_weapons[ cg.weaponSelect ].item;
// draw the selected name
if ( item && item->classname && item->classname[0] )
{
char text[1024];
if ( cgi_SP_GetStringTextString( va("SP_INGAME_%s",item->classname), text, sizeof( text )))
{
int w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 1.0f);
int x = ( SCREEN_WIDTH - w ) / 2;
int y = (SCREEN_HEIGHT - 24);
cgi_R_Font_DrawString(x, y, text, textColor, cgs.media.qhFontSmall, -1, FONT_SCALE);
}
}
cgi_R_SetColor( NULL );
}
/*
===============
CG_WeaponSelectable
===============
*/
qboolean CG_WeaponSelectable( int i, int original, qboolean dpMode )
{
int usage_for_weap;
if (i > MAX_PLAYER_WEAPONS)
{
#ifndef FINAL_BUILD
Com_Printf("CG_WeaponSelectable() passed illegal index of %d!\n",i);
#endif
return qfalse;
}
if ( cg.weaponSelectTime + 200 > cg.time )
{//TEMP standard weapon cycle debounce for E3 because G2 can't keep up with fast weapon changes
return qfalse;
}
//FIXME: this doesn't work below, can still cycle too fast!
if ( original == WP_SABER && cg.weaponSelectTime + 500 > cg.time )
{//when switch to lightsaber, have to stay there for at least half a second!
return qfalse;
}
if ( G_IsRidingVehicle(cg_entities[0].gent) )
{
if (G_IsRidingTurboVehicle(cg_entities[0].gent) || (i!=WP_NONE && i!=WP_SABER && i!=WP_BLASTER) )
{
return qfalse;
}
}
if (( weaponData[i].ammoIndex != AMMO_NONE ) && !dpMode )
{//weapon uses ammo, see if we have any
usage_for_weap = weaponData[i].energyPerShot < weaponData[i].altEnergyPerShot
? weaponData[i].energyPerShot
: weaponData[i].altEnergyPerShot;
if ( cg.snap->ps.ammo[weaponData[i].ammoIndex] - usage_for_weap < 0 )
{
if ( i != WP_DET_PACK ) // detpack can be switched to...should possibly check if there are any stuck to a wall somewhere?
{
// This weapon doesn't have enough ammo to shoot either the main or the alt-fire
return qfalse;
}
}
}
if (!(cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i )))
{
// Don't have this weapon to start with.
return qfalse;
}
return qtrue;
}
void CG_ToggleATSTWeapon( void )
{
if ( cg.weaponSelect == WP_ATST_MAIN )
{
cg.weaponSelect = WP_ATST_SIDE;
}
else
{
cg.weaponSelect = WP_ATST_MAIN;
}
// cg.weaponSelectTime = cg.time;
SetWeaponSelectTime();
}
void CG_PlayerLockedWeaponSpeech( int jumping )
{
extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType );
static int speechDebounceTime = 0;
if ( !in_camera )
{//not in a cinematic
if ( speechDebounceTime < cg.time )
{//spoke more than 3 seconds ago
if ( !Q3_TaskIDPending( &g_entities[0], TID_CHAN_VOICE ) )
{//not waiting on a scripted sound to finish
if( !jumping )
{
if( Q_flrand(0.0f, 1.0f) > 0.5 )
{
G_SoundOnEnt( player, CHAN_VOICE, va( "sound/chars/kyle/09kyk015.wav" ));
}
else
{
G_SoundOnEnt( player, CHAN_VOICE, va( "sound/chars/kyle/09kyk016.wav" ));
}
}
else
{
G_SoundOnEnt( player, CHAN_VOICE, va( "sound/chars/kyle/16kyk007.wav" ));
}
speechDebounceTime = cg.time + 3000;
}
}
}
}
/*
===============
CG_NextWeapon_f
===============
*/
void CG_NextWeapon_f( void ) {
int i;
int original;
if ( !cg.snap ) {
return;
}
/*
if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
return;
}
*/
if( g_entities[0].flags & FL_LOCK_PLAYER_WEAPONS )
{
CG_PlayerLockedWeaponSpeech( qfalse );
return;
}
if( g_entities[0].client && g_entities[0].client->NPC_class == CLASS_ATST )
{
CG_ToggleATSTWeapon();
return;
}
if ( cg.snap->ps.eFlags & EF_LOCKED_TO_WEAPON )
{
// can't do any sort of weapon switching when in the emplaced gun
return;
}
if ( cg.snap->ps.viewEntity )
{
// yeah, probably need a better check here
if ( g_entities[cg.snap->ps.viewEntity].client && ( g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_R5D2
|| g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_R2D2
|| g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_MOUSE ))
{
return;
}
}
original = cg.weaponSelect;
int firstWeapon = FIRST_WEAPON;
if (G_IsRidingVehicle(&g_entities[cg.snap->ps.viewEntity]))
{
firstWeapon = 0; // include WP_NONE here
}
for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ )
{
//*SIGH*... Hack to put concussion rifle before rocketlauncher
if ( cg.weaponSelect == WP_FLECHETTE )
{
cg.weaponSelect = WP_CONCUSSION;
}
else if ( cg.weaponSelect == WP_CONCUSSION )
{
cg.weaponSelect = WP_ROCKET_LAUNCHER;
}
else if ( cg.weaponSelect == WP_DET_PACK )
{
cg.weaponSelect = firstWeapon;
}
else
{
cg.weaponSelect++;
}
if ( cg.weaponSelect < firstWeapon || cg.weaponSelect > MAX_PLAYER_WEAPONS) {
cg.weaponSelect = firstWeapon;
}
if ( CG_WeaponSelectable( cg.weaponSelect, original, qfalse ) )
{
// cg.weaponSelectTime = cg.time;
SetWeaponSelectTime();
return;
}
}
cg.weaponSelect = original;
}
/*
===============
CG_DPNextWeapon_f
===============
*/
void CG_DPNextWeapon_f( void ) {
int i;
int original;
if ( !cg.snap ) {
return;
}
/*
if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
return;
}
*/
original = cg.DataPadWeaponSelect;
for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ )
{
//*SIGH*... Hack to put concussion rifle before rocketlauncher
if ( cg.DataPadWeaponSelect == WP_FLECHETTE )
{
cg.DataPadWeaponSelect = WP_CONCUSSION;
}
else if ( cg.DataPadWeaponSelect == WP_CONCUSSION )
{
cg.DataPadWeaponSelect = WP_ROCKET_LAUNCHER;
}
else if ( cg.DataPadWeaponSelect == WP_DET_PACK )
{
cg.DataPadWeaponSelect = FIRST_WEAPON;
}
else
{
cg.DataPadWeaponSelect++;
}
if ( cg.DataPadWeaponSelect < FIRST_WEAPON || cg.DataPadWeaponSelect > MAX_PLAYER_WEAPONS) {
cg.DataPadWeaponSelect = FIRST_WEAPON;
}
if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original, qtrue ) )
{
return;
}
}
cg.DataPadWeaponSelect = original;
}
/*
===============
CG_DPPrevWeapon_f
===============
*/
void CG_DPPrevWeapon_f( void )
{
int i;
int original;
if ( !cg.snap )
{
return;
}
/*
if ( cg.snap->ps.pm_flags & PMF_FOLLOW )
{
return;
}
*/
original = cg.DataPadWeaponSelect;
for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ )
{
//*SIGH*... Hack to put concussion rifle before rocketlauncher
if ( cg.DataPadWeaponSelect == WP_ROCKET_LAUNCHER )
{
cg.DataPadWeaponSelect = WP_CONCUSSION;
}
else if ( cg.DataPadWeaponSelect == WP_CONCUSSION )
{
cg.DataPadWeaponSelect = WP_FLECHETTE;
}
else if ( cg.DataPadWeaponSelect == WP_MELEE )
{
cg.DataPadWeaponSelect = WP_DET_PACK;
}
else
{
cg.DataPadWeaponSelect--;
}
if ( cg.DataPadWeaponSelect < FIRST_WEAPON || cg.DataPadWeaponSelect > MAX_PLAYER_WEAPONS)
{
cg.DataPadWeaponSelect = MAX_PLAYER_WEAPONS;
}
if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original, qtrue ) )
{
return;
}
}
cg.DataPadWeaponSelect = original;
}
/*
===============
CG_PrevWeapon_f
===============
*/
void CG_PrevWeapon_f( void ) {
int i;
int original;
if ( !cg.snap ) {
return;
}
/*
if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
return;
}
*/
if( g_entities[0].flags & FL_LOCK_PLAYER_WEAPONS )
{
CG_PlayerLockedWeaponSpeech( qfalse );
return;
}
if( g_entities[0].client && g_entities[0].client->NPC_class == CLASS_ATST )
{
CG_ToggleATSTWeapon();
return;
}
if ( cg.snap->ps.eFlags & EF_LOCKED_TO_WEAPON )
{
// can't do any sort of weapon switching when in the emplaced gun
return;
}
if ( cg.snap->ps.viewEntity )
{
// yeah, probably need a better check here
if ( g_entities[cg.snap->ps.viewEntity].client && ( g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_R5D2
|| g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_R2D2
|| g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_MOUSE ))
{
return;
}
}
original = cg.weaponSelect;
int firstWeapon = FIRST_WEAPON;
if (G_IsRidingVehicle(&g_entities[cg.snap->ps.viewEntity]))
{
firstWeapon = 0; // include WP_NONE here
}
for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ ) {
//*SIGH*... Hack to put concussion rifle before rocketlauncher
if ( cg.weaponSelect == WP_ROCKET_LAUNCHER )
{
cg.weaponSelect = WP_CONCUSSION;
}
else if ( cg.weaponSelect == WP_CONCUSSION )
{
cg.weaponSelect = WP_FLECHETTE;
}
else if ( cg.weaponSelect == WP_MELEE )
{
cg.weaponSelect = WP_DET_PACK;
}
else
{
cg.weaponSelect--;
}
if ( cg.weaponSelect < firstWeapon || cg.weaponSelect > MAX_PLAYER_WEAPONS) {
cg.weaponSelect = MAX_PLAYER_WEAPONS;
}
if ( CG_WeaponSelectable( cg.weaponSelect, original, qfalse ) )
{
SetWeaponSelectTime();
// cg.weaponSelectTime = cg.time;
return;
}
}
cg.weaponSelect = original;
}
/*
void CG_ChangeWeapon( int num )
Meant to be called from the normal game, so checks the game-side weapon inventory data
*/
void CG_ChangeWeapon( int num )
{
gentity_t *player = &g_entities[0];
if ( num < WP_NONE || num >= WP_NUM_WEAPONS )
{
return;
}
if( player->flags & FL_LOCK_PLAYER_WEAPONS )
{
CG_PlayerLockedWeaponSpeech( qfalse );
return;
}
if ( player->client != NULL && !(player->client->ps.stats[STAT_WEAPONS] & ( 1 << num )) )
{
return; // don't have the weapon
}
// because we don't have an empty hand model for the thermal, don't allow selecting that weapon if it has no ammo
if ( num == WP_THERMAL )
{
if ( cg.snap && cg.snap->ps.ammo[AMMO_THERMAL] <= 0 )
{
return;
}
}
// because we don't have an empty hand model for the thermal, don't allow selecting that weapon if it has no ammo
if ( num == WP_TRIP_MINE )
{
if ( cg.snap && cg.snap->ps.ammo[AMMO_TRIPMINE] <= 0 )
{
return;
}
}
SetWeaponSelectTime();
// cg.weaponSelectTime = cg.time;
cg.weaponSelect = num;
}
/*
===============
CG_Weapon_f
===============
*/
void CG_Weapon_f( void )
{
int num;
if ( cg.weaponSelectTime + 200 > cg.time )
{
return;
}
if ( !cg.snap ) {
return;
}
/*
if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
return;
}
*/
if( g_entities[0].flags & FL_LOCK_PLAYER_WEAPONS )
{
CG_PlayerLockedWeaponSpeech( qfalse );
return;
}
if( g_entities[0].client && g_entities[0].client->NPC_class == CLASS_ATST )
{
CG_ToggleATSTWeapon();
return;
}
if ( cg.snap->ps.eFlags & EF_LOCKED_TO_WEAPON )
{
// can't do any sort of weapon switching when in the emplaced gun
return;
}
if ( cg.snap->ps.viewEntity )
{
// yeah, probably need a better check here
if ( g_entities[cg.snap->ps.viewEntity].client && ( g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_R5D2
|| g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_R2D2
|| g_entities[cg.snap->ps.viewEntity].client->NPC_class == CLASS_MOUSE ))
{
return;
}
}
num = atoi( CG_Argv( 1 ) );
if ( num < WP_NONE || num >= WP_NUM_WEAPONS ) {
return;
}
if ( num == WP_SABER )
{//lightsaber
if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) )
{//don't have saber, try stun baton
if ( ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << WP_STUN_BATON ) ) )
{
num = WP_STUN_BATON;
}
else
{//don't have stun baton, use fists
num = WP_MELEE;
}
}
else if ( num == cg.snap->ps.weapon )
{//already have it up, let's try to toggle it
if ( !in_camera )
{//player can't activate/deactivate saber when in a cinematic
//can't toggle it if not holding it and not controlling it or dead
if ( cg.predicted_player_state.stats[STAT_HEALTH] > 0 && (!cg_entities[0].gent->client->ps.saberInFlight || (&g_entities[cg_entities[0].gent->client->ps.saberEntityNum] != NULL && g_entities[cg_entities[0].gent->client->ps.saberEntityNum].s.pos.trType == TR_LINEAR) ) )
{//it's either in-hand or it's under telekinetic control
if ( cg_entities[0].gent->client->ps.SaberActive() )
{//a saber is on
if ( cg_entities[0].gent->client->ps.dualSabers
&& cg_entities[0].gent->client->ps.saber[1].Active() )
{//2nd saber is on, turn it off, too
cg_entities[0].gent->client->ps.saber[1].Deactivate();
}
cg_entities[0].gent->client->ps.saber[0].Deactivate();
if ( cg_entities[0].gent->client->ps.saberInFlight )
{//play it on the saber
cgi_S_UpdateEntityPosition( cg_entities[0].gent->client->ps.saberEntityNum, g_entities[cg_entities[0].gent->client->ps.saberEntityNum].currentOrigin );
cgi_S_StartSound (NULL, cg_entities[0].gent->client->ps.saberEntityNum, CHAN_AUTO, cgs.sound_precache[cg_entities[0].gent->client->ps.saber[0].soundOff] );
}
else
{
cgi_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.sound_precache[cg_entities[0].gent->client->ps.saber[0].soundOff] );
}
}
else
{//turn them both on
cg_entities[0].gent->client->ps.SaberActivate();
}
}
}
}
}
else if ( num >= WP_THERMAL && num <= WP_DET_PACK ) // these weapons cycle
{
int weap, i = 0;
if ( cg.snap->ps.weapon >= WP_THERMAL && cg.snap->ps.weapon <= WP_DET_PACK )
{
// already in cycle range so start with next cycle item
weap = cg.snap->ps.weapon + 1;
}
else
{
// not in cycle range, so start with thermal detonator
weap = WP_THERMAL;
}
// prevent an endless loop
while ( i <= 4 )
{
if ( weap > WP_DET_PACK )
{
weap = WP_THERMAL;
}
if ( cg.snap->ps.ammo[weaponData[weap].ammoIndex] > 0 || weap == WP_DET_PACK )
{
if ( CG_WeaponSelectable( weap, cg.snap->ps.weapon, qfalse ) )
{
num = weap;
break;
}
}
weap++;
i++;
}
}
if (!CG_WeaponSelectable(num, cg.snap->ps.weapon, qfalse))
{
return;
}
SetWeaponSelectTime();
// cg.weaponSelectTime = cg.time;
cg.weaponSelect = num;
}
void Cmd_UseInventory_f(gentity_t *ent);
extern float cg_zoomFov; //from cg_view.cpp
void CG_ExitScope_f( )
{
if ( cg.zoomMode )
{
G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomend.wav" );
// already zooming, so must be wanting to turn it off
cg.zoomMode = 0;
cg.zoomTime = cg.time;
cg.zoomLocked = qfalse;
}
}
void CG_EnterScope_f( )
{
if ( cg.zoomMode == 0 || cg.zoomMode == 3 )
{
G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomstart.wav" );
// not already zooming, so do it now
if (cg.weaponSelect == WP_DISRUPTOR) {
cg.zoomMode = 2;
cg_zoomFov = 80.0f;
cg.zoomLocked = qfalse;
} else {
//Our specially created E11 Blaster scope
cg.zoomMode = 4;
cg_zoomFov = 30.0f;
cg.zoomLocked = qtrue;
}
}
}
void CG_ToggleSaber_f( )
{
bool deactivated = false;
if (player->client->ps.saber[0].numBlades > 1)
{
if (player->client->ps.saber[0].blade[0].active && player->client->ps.saber[0].blade[1].active)
{
player->client->ps.SaberBladeActivate( 0, 1, qfalse );
deactivated = true;
}
else if (player->client->ps.saber[0].blade[0].active)
{
player->client->ps.SaberBladeActivate( 0, 0, qfalse );
deactivated = true;
}
else
{
player->client->ps.saber[0].Activate();
}
}
else if (player->client->ps.saber->Active())
{
player->client->ps.saber[0].Deactivate();
if (player->client->ps.dualSabers)
{
player->client->ps.saber[1].Deactivate();
}
deactivated = true;
}
else
{
player->client->ps.saber[0].Activate();
if (player->client->ps.dualSabers)
{
player->client->ps.saber[1].Activate();
}
}
if (deactivated)
{
G_SoundOnEnt( player, CHAN_WEAPON, "sound/weapons/saber/saberoffquick.wav" );
}
}
//Selects the currently selected thing (if one _is_ selected)
void CG_ItemSelectorSelect_f( void )
{
cg.itemSelectorTime = 0;
cgi_Cvar_Set("timescale", "1.0");
if (cg.itemSelectorSelection == ST_NONE)
{
return;
}
if (cg.itemSelectorType == ST_WEAPON) // weapons
{
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
if (vr->in_vehicle
&& vr->vehicle_type != VH_WALKER
&& cent->currentState.weapon == WP_SABER)
{
//If holding saber, put it away
CG_NextWeapon_f();
}
else
{
if (cg.weaponSelect == cg.itemSelectorSelection)
{
return;
}
cg.weaponSelectTime = cg.time;
cg.weaponSelect = cg.itemSelectorSelection;
}
}
else if (cg.itemSelectorType == ST_GADGET) // gadgets
{
cg.inventorySelectTime = cg.time;
cg.inventorySelect = cg.itemSelectorSelection;
//Immediately use the selected inventory item
if (player)
{
Cmd_UseInventory_f(player);
}
}
else if (cg.itemSelectorType == ST_FIGHTING_STYLE) //fighting style
{
cgi_SendConsoleCommand(va( "setSaberLevel %i\n", cg.itemSelectorSelection + 1));
}
else if (cg.itemSelectorType == ST_FORCE_POWER)
{
if (cg.forcepowerSelect == cg.itemSelectorSelection)
{
return;
}
cg.forcepowerSelectTime = cg.time;
cg.forcepowerSelect = cg.itemSelectorSelection;
}
else if (cg.itemSelectorType == ST_QUICK_SAVE) {
if (cg.itemSelectorSelection == 0) {
cgi_SendConsoleCommand("save quick\n");
CG_CenterPrint("Quick Saved", 240);
} else {
cgi_SendConsoleCommand("load quick\n");
}
}
//reset ready for next time
cg.itemSelectorSelection = ST_NONE;
}
void CG_ItemSelectorNext_f( void )
{
if (cg.itemSelectorType >= ST_FORCE_POWER)
{
cg.itemSelectorType = (cg.itemSelectorType == ST_FORCE_POWER) ? ST_QUICK_SAVE : ST_FORCE_POWER;
return;
}
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
//Only show the stance selection if using saber and in third person and not using dual/staff saber
int selectors = (cent->gent->client->ps.saberStylesKnown != SS_NONE &&
cent->currentState.weapon == WP_SABER && cg_thirdPerson.integer) ? 3 : 2;
cg.itemSelectorType = (cg.itemSelectorType+1) % selectors;
cg.itemSelectorTime = cg.time;
}
void CG_ItemSelectorPrev_f( void )
{
if (cg.itemSelectorType >= ST_FORCE_POWER)
{
cg.itemSelectorType = (cg.itemSelectorType == ST_FORCE_POWER) ? ST_QUICK_SAVE : ST_FORCE_POWER;
return;
}
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
//Only show the stance selection if using saber and in third person and not using dual/staff saber
int selectors = (cent->gent->client->ps.saberStylesKnown != SS_NONE &&
cent->currentState.weapon == WP_SABER && cg_thirdPerson.integer) ? 3 : 2;
if (--cg.itemSelectorType < 0)
cg.itemSelectorType = selectors-1;
cg.itemSelectorTime = cg.time;
}
extern int force_icons[NUM_FORCE_POWERS];
extern int inv_icons[INV_MAX];
qboolean CG_InventorySelectable( int index);
qboolean ForcePower_Valid(int index);
void CG_DrawItemSelector( void )
{
if (cg.predicted_player_state.stats[STAT_HEALTH] <= 0)
{
return;
}
if (cg.itemSelectorTime == 0)
{
cg.itemSelectorTime = cg.time;
if (vr->item_selector == 2)
{
cg.itemSelectorType = ST_FORCE_POWER;
VectorCopy(vr->offhandposition[0], cg.itemSelectorOrigin);
VectorCopy(vr->offhandoffset, cg.itemSelectorOffset);
}
else {
cg.itemSelectorType = ST_WEAPON;
VectorCopy(vr->weaponposition, cg.itemSelectorOrigin);
VectorCopy(vr->weaponoffset, cg.itemSelectorOffset);
}
}
float dist = 10.0f;
float radius = 4.4f;
float scale = 0.05f;
float frac = (cg.time - cg.itemSelectorTime) / 20.0f;
if (frac > 1.0f)
{
frac = 1.0f;
}
cgi_Cvar_Set("timescale", "0.22");
vec3_t controllerOrigin, controllerAngles, controllerOffset, selectorOrigin;
if (cg.itemSelectorType >= ST_FORCE_POWER)
{
BG_CalculateVROffHandPosition(controllerOrigin, controllerAngles);
VectorSubtract(vr->offhandposition[0], cg.itemSelectorOrigin, controllerOffset);
}
else
{
BG_CalculateVRWeaponPosition(controllerOrigin, controllerAngles);
VectorSubtract(vr->weaponposition, cg.itemSelectorOrigin, controllerOffset);
}
if (vr->in_vehicle)
{
BG_ConvertFromVR(vr->hmdposition_offset, controllerOrigin, controllerOrigin);
}
vec3_t wheelAngles, wheelOrigin, beamOrigin, wheelForward, wheelRight, wheelUp;
vec3_t angles;
VectorClear(angles);
angles[YAW] = vr->hmdorientation[YAW];
BG_CalculateVRPositionInWorld(cg.itemSelectorOrigin, cg.itemSelectorOffset, angles, wheelOrigin, wheelAngles);
AngleVectors(wheelAngles, wheelForward, wheelRight, wheelUp);
VectorCopy(controllerOrigin, wheelOrigin);
VectorCopy(wheelOrigin, beamOrigin);
VectorMA(wheelOrigin, (dist * frac), wheelForward, wheelOrigin);
VectorCopy(wheelOrigin, selectorOrigin);
vec3_t pos;
memset(&pos, 0, sizeof pos);
{
pos[0] = (sinf(DEG2RAD(wheelAngles[YAW] - controllerAngles[YAW])) / sinf(DEG2RAD(22.5f)));
pos[1] = ((wheelAngles[PITCH] - controllerAngles[PITCH]) / 22.5f);
float len = VectorLength(pos);
if (len > 1.0f)
{
pos[0] *= (1.0f / len);
pos[1] *= (1.0f / len);
}
}
VectorMA(selectorOrigin, radius * pos[0], wheelRight, selectorOrigin);
VectorMA(selectorOrigin, radius * pos[1], wheelUp, selectorOrigin);
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
vec3_t sRGB;
int count;
switch (cg.itemSelectorType)
{
case ST_WEAPON: //weapons
if (vr->in_vehicle)
count = 2;
else
count = WP_MELEE;
sRGB[0] = 1.0f;
sRGB[1] = 0.8f;
sRGB[2] = 0.2f;
break;
case ST_GADGET: //gadgets
count = INV_GOODIE_KEY;
sRGB[0] = 0.0f;
sRGB[1] = 1.0f;
sRGB[2] = 0.0f;
break;
case ST_FIGHTING_STYLE: //fighting style
count = 3;
sRGB[0] = 0.0f;
sRGB[1] = 1.0f;
sRGB[2] = 1.0f;
break;
case ST_FORCE_POWER: // force powers
count = MAX_SHOWPOWERS;
sRGB[0] = 0.0f;
sRGB[1] = 0.0f;
sRGB[2] = 1.0f;
break;
case ST_QUICK_SAVE:
count = 2;
sRGB[0] = 1.0f;
sRGB[1] = 1.0f;
sRGB[2] = 1.0f;
break;
}
//cgi_R_AddRefEntityToScene( &beam );
FX_AddLine(beamOrigin, selectorOrigin, 1.0f, 0.1f, 0.0f,
0.5f, 0.5f,
sRGB, sRGB,
10, cgi_R_RegisterShader("gfx/misc/whiteline2"),
FX_SIZE_LINEAR | FX_ALPHA_LINEAR);
if (cg.itemSelectorType == ST_WEAPON) // weapons
{
if (cg.weaponSelect != WP_NONE) {
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
VectorCopy(wheelOrigin, sprite.origin);
sprite.reType = RT_SPRITE;
sprite.customShader = cg_weapons[cg.weaponSelect].weaponIcon;
sprite.radius = 1.8f;
memset(sprite.shaderRGBA, 0xff, 4);
cgi_R_AddRefEntityToScene(&sprite);
}
}
else if (cg.itemSelectorType == 2) // fighting style
{
//For the fighting style show the active one in the middle
if (cent->gent->client->ps.saberStylesKnown != SS_NONE) {
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
VectorCopy(wheelOrigin, sprite.origin);
sprite.reType = RT_SPRITE;
switch (cent->gent->client->ps.saberAnimLevel) {
case SS_FAST:
sprite.customShader = otherHUDBits[OHB_SABERSTYLE_FAST].background;
break;
case SS_MEDIUM:
case SS_DUAL:
case SS_STAFF:
sprite.customShader = otherHUDBits[OHB_SABERSTYLE_MEDIUM].background;
break;
default:
sprite.customShader = otherHUDBits[OHB_SABERSTYLE_STRONG].background;
break;
}
sprite.radius = 1.8f;
memset(sprite.shaderRGBA, 0xff, 4);
cgi_R_AddRefEntityToScene(&sprite);
}
}
else if (cg.itemSelectorType == ST_FORCE_POWER) // force powers
{
if (cent->gent->client->ps.forcePowersKnown != 0) {
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
VectorCopy(wheelOrigin, sprite.origin);
sprite.reType = RT_SPRITE;
sprite.customShader = force_icons[showPowers[cg.forcepowerSelect]];
sprite.radius = 1.8f;
memset(sprite.shaderRGBA, 0xff, 4);
cgi_R_AddRefEntityToScene(&sprite);
}
}
for (int s = -1; s < 2; s += 2) {
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
vec3_t right;
AngleVectors(wheelAngles, NULL, right, NULL);
float offset = ((float) s * 6.0f) + (((float) s * 0.3f) *
sinf(DEG2RAD(AngleNormalize360(cg.time - cg.itemSelectorTime))));
VectorMA(wheelOrigin, offset, right, sprite.origin);
sprite.reType = RT_SPRITE;
sprite.customShader = cgs.media.binocularArrow;
sprite.radius = 0.6f;
sprite.rotation = 180.0f * ((s - 1.0f) / 2.0f);
memset(sprite.shaderRGBA, 0xff, 4);
cgi_R_AddRefEntityToScene(&sprite);
}
qboolean selected = qfalse;
for (int index = 0; index < count; ++index)
{
int itemId = index;
if (cg.itemSelectorType == ST_WEAPON) {
if (vr->in_vehicle)
{
if (vr->vehicle_type == VH_WALKER)
{
itemId = WP_ATST_MAIN + index;
}
else
{
//Only choice on a speeder/animal is the saber
itemId = WP_SABER;
}
}
else
{
if (itemId == 0)
{
itemId = WP_MELEE;
}
CG_RegisterWeapon(itemId);
}
}
{
bool selectable;
switch (cg.itemSelectorType)
{
case ST_WEAPON: //weapons
selectable = vr->in_vehicle ||
(CG_WeaponSelectable(itemId, cg.weaponSelect, qfalse) && cg.snap->ps.ammo[weaponData[itemId].ammoIndex]);
break;
case ST_GADGET: //gadgets
selectable = CG_InventorySelectable(itemId) && inv_icons[itemId];
break;
case ST_FIGHTING_STYLE: //fighting style
selectable = cent->gent->client->ps.saberAnimLevel <= SS_STRONG &&
cent->gent->client->ps.saberStylesKnown & (1<<(itemId+1));
break;
case ST_FORCE_POWER: // force powers
selectable = ForcePower_Valid(itemId);
break;
case ST_QUICK_SAVE:
selectable = true;
break;
}
if (selectable) {
//first calculate wheel slot position
vec3_t angles, iconOrigin, iconBackground, iconForeground;
VectorClear(angles);
angles[YAW] = wheelAngles[YAW];
angles[PITCH] = wheelAngles[PITCH];
angles[ROLL] = (float)(360 / count) * index;
vec3_t forward, up;
AngleVectors(angles, forward, NULL, up);
VectorMA(wheelOrigin, (radius * frac), up, iconOrigin);
VectorMA(iconOrigin, 0.2f, forward, iconBackground);
VectorMA(iconOrigin, -0.2f, forward, iconForeground);
{
vec3_t diff;
VectorSubtract(selectorOrigin, iconOrigin, diff);
float length = VectorLength(diff);
if (length <= 1.0f &&
frac == 1.0f &&
selectable) {
if (cg.itemSelectorSelection != itemId) {
cg.itemSelectorSelection = itemId;
cgi_HapticEvent("selector_icon", 0, vr->right_handed ?
((cg.itemSelectorType >= ST_FORCE_POWER) ? 2 : 1) : ((cg.itemSelectorType >= ST_FORCE_POWER) ? 1 : 2), 100, 0, 0);
}
selected = qtrue;
}
}
if (cg.itemSelectorSelection == itemId) {
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
VectorCopy(iconOrigin, sprite.origin);
sprite.origin[2] += 2.5f + (0.5f * sinf(DEG2RAD(
AngleNormalize360(cg.time - cg.itemSelectorTime))));
sprite.reType = RT_SPRITE;
sprite.customShader = cgs.media.binocularArrow;
sprite.radius = 0.6f;
sprite.rotation = -90.0f;
sprite.shaderRGBA[0] = 255;
sprite.shaderRGBA[1] = 255;
sprite.shaderRGBA[2] = 255;
sprite.shaderRGBA[3] = 255;
cgi_R_AddRefEntityToScene(&sprite);
}
{
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
float sRadius = 1.3f;
VectorCopy(iconOrigin, sprite.origin);
sprite.reType = RT_SPRITE;
switch (cg.itemSelectorType)
{
case ST_WEAPON: //weapons
sprite.customShader = cg_weapons[itemId].weaponIcon;
break;
case ST_GADGET: //gadgets
sprite.customShader = inv_icons[itemId];
break;
case ST_FIGHTING_STYLE: //fighting style
switch ( itemId+1 )
{
case SS_FAST:
sprite.customShader = otherHUDBits[OHB_SABERSTYLE_FAST].background;
break;
case SS_MEDIUM:
sprite.customShader = otherHUDBits[OHB_SABERSTYLE_MEDIUM].background;
break;
case SS_STRONG:
sprite.customShader = otherHUDBits[OHB_SABERSTYLE_STRONG].background;
break;
}
break;
case ST_FORCE_POWER: // force powers
sprite.customShader = force_icons[showPowers[itemId]];
break;
case ST_QUICK_SAVE:
sprite.customShader = itemId == 0 ? cgs.media.iconSave : cgs.media.iconLoad;
break;
}
sprite.radius =
sRadius * (cg.itemSelectorSelection == itemId ? 1.3f : 0.6f);
sprite.shaderRGBA[0] = 255;
sprite.shaderRGBA[1] = 255;
sprite.shaderRGBA[2] = 255;
sprite.shaderRGBA[3] = 255;
cgi_R_AddRefEntityToScene(&sprite);
}
}
}
}
if (!selected)
{
cg.itemSelectorSelection = ST_NONE;
}
}
/*
===================
CG_OutOfAmmoChange
The current weapon has just run out of ammo
===================
*/
void CG_OutOfAmmoChange( void ) {
int i;
int original;
if ( cg.weaponSelectTime + 200 > cg.time )
return;
if( g_entities[0].client && g_entities[0].client->NPC_class == CLASS_ATST )
{
CG_ToggleATSTWeapon();
return;
}
original = cg.weaponSelect;
for ( i = WP_ROCKET_LAUNCHER; i > 0 ; i-- )
{
// We don't want the emplaced, melee, or explosive devices here
if ( original != i && CG_WeaponSelectable( i, original, qfalse ) )
{
SetWeaponSelectTime();
cg.weaponSelect = i;
break;
}
}
if ( cg_autoswitch.integer != 1 )
{
// didn't have that, so try these. Start with thermal...
for ( i = WP_THERMAL; i <= WP_DET_PACK; i++ )
{
// We don't want the emplaced, or melee here
if ( original != i && CG_WeaponSelectable( i, original, qfalse ) )
{
if ( i == WP_DET_PACK && cg.snap->ps.ammo[weaponData[i].ammoIndex] <= 0 )
{
// crap, no point in switching to this
}
else
{
SetWeaponSelectTime();
cg.weaponSelect = i;
break;
}
}
}
}
// try stun baton as a last ditch effort
if ( CG_WeaponSelectable( WP_STUN_BATON, original, qfalse ))
{
SetWeaponSelectTime();
cg.weaponSelect = WP_STUN_BATON;
}
}
/*
===================================================================================================
WEAPON EVENTS
===================================================================================================
*/
/*
================
CG_FireWeapon
Caused by an EV_FIRE_WEAPON event
================
*/
void CG_FireWeapon( centity_t *cent, qboolean alt_fire )
{
entityState_t *ent;
//weaponInfo_t *weap;
ent = &cent->currentState;
if ( ent->weapon == WP_NONE ) {
return;
}
if ( ent->weapon >= WP_NUM_WEAPONS ) {
CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" );
return;
}
if ( ent->weapon == WP_TUSKEN_RIFLE && cent->gent->client)
{
if (cent->gent->client->ps.torsoAnim==BOTH_TUSKENATTACK1 ||
cent->gent->client->ps.torsoAnim==BOTH_TUSKENATTACK2 ||
cent->gent->client->ps.torsoAnim==BOTH_TUSKENATTACK3 ||
cent->gent->client->ps.torsoAnim==BOTH_TUSKENLUNGE1)
{
return;
}
}
//weap = &cg_weapons[ ent->weapon ];
// mark the entity as muzzle flashing, so when it is added it will
// append the flash to the weapon model
cent->muzzleFlashTime = cg.time;
cent->altFire = alt_fire;
// lightning type guns only does this this on initial press
if ( ent->weapon == WP_SABER )
{
if ( cent->pe.lightningFiring )
{
/* if ( ent->weapon == WP_DREADNOUGHT )
{
cgi_FF_EnsureFX( fffx_Laser3 );
}
*/
return;
}
}
//Are we the player?
if (cent->gent->client->ps.clientNum == 0)
{
/*
These are specific to external haptics vest/arms/face combinations
position values:
0 - Will play on vest and both arms if pattern files present for both
1 - Will play on (left) vest and on left arm only if pattern files present for left
2 - Will play on (right) vest and on right arm only if pattern files present for right
3 - Will play on head only (if present)
4 - Will play on all devices (that have a pattern defined for them)
*/
int position = vr->weapon_stabilised ? 0 : (vr->right_handed ? 2 : 1);
//Haptics
switch (ent->weapon) {
case WP_SABER:
case WP_MELEE:
if (cent->gent->client->ps.dualSabers ||
ent->weapon == WP_MELEE)
{
if (vr->primaryVelocityTriggeredAttack && vr->secondaryVelocityTriggeredAttack)
{
position = 4;
}
else if (vr->primaryVelocityTriggeredAttack)
{
position = (vr->right_handed ? 2 : 1);
}
else if (vr->secondaryVelocityTriggeredAttack) // secondary triggered
{
position = (vr->right_handed ? 1 : 2);
}
else
{
position = -1;
}
}
cgi_HapticEvent( "chainsaw_fire", position, 0, 40, 0, 0);
break;
case WP_BRYAR_PISTOL:
case WP_BOWCASTER:
case WP_BLASTER:
case WP_ATST_MAIN:
cgi_HapticEvent("machinegun_fire", position, 0, (ent->weapon == WP_BRYAR_PISTOL) ? 60 : 100, 0, 0);
break;
case WP_BLASTER_PISTOL:
cgi_HapticEvent("shotgun_fire", position, 0, 100, 0, 0);
break;
case WP_THERMAL:
case WP_DET_PACK:
case WP_TRIP_MINE:
cgi_HapticEvent("handgrenade_fire", position, 0, 80, 0, 0);
break;
case WP_ROCKET_LAUNCHER:
case WP_ATST_SIDE:
cgi_HapticEvent("rocket_fire", position, 0, 100, 0, 0);
break;
case WP_DISRUPTOR:
cgi_HapticEvent("RTCWQuest:fire_sniper", position, 0, 100, 0, 0);
break;
case WP_FLECHETTE:
case WP_REPEATER:
cgi_HapticEvent("plasmagun_fire", position, 0, 100, 0, 0);
break;
case WP_DEMP2:
case WP_EMPLACED_GUN:
cgi_HapticEvent("bfg_fire", position, 0, 100, 0, 0);
break;
}
}
// Do overcharge sound that get's added to the top
/* if (( ent->powerups & ( 1<<PW_WEAPON_OVERCHARGE )))
{
if ( alt_fire )
{
switch( ent->weapon )
{
case WP_THERMAL:
case WP_DET_PACK:
case WP_TRIP_MINE:
case WP_ROCKET_LAUNCHER:
case WP_FLECHETTE:
// these weapon fires don't overcharge
break;
case WP_BLASTER:
cgi_S_StartSound( NULL, ent->number, CHAN_AUTO, cgs.media.overchargeFastSound );
break;
default:
cgi_S_StartSound( NULL, ent->number, CHAN_AUTO, cgs.media.overchargeSlowSound );
break;
}
}
else
{
switch( ent->weapon )
{
case WP_THERMAL:
case WP_DET_PACK:
case WP_TRIP_MINE:
case WP_ROCKET_LAUNCHER:
// these weapon fires don't overcharge
break;
case WP_REPEATER:
cgi_S_StartSound( NULL, ent->number, CHAN_AUTO, cgs.media.overchargeFastSound );
break;
default:
cgi_S_StartSound( NULL, ent->number, CHAN_AUTO, cgs.media.overchargeSlowSound );
break;
}
}
}*/
}
/*
=================
CG_BounceEffect
Caused by an EV_BOUNCE | EV_BOUNCE_HALF event
=================
*/
void CG_BounceEffect( centity_t *cent, int weapon, vec3_t origin, vec3_t normal )
{
switch( weapon )
{
case WP_THERMAL:
if ( rand() & 1 ) {
cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.grenadeBounce1 );
} else {
cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.grenadeBounce2 );
}
break;
case WP_BOWCASTER:
theFxScheduler.PlayEffect( cgs.effects.bowcasterBounceEffect, origin, normal );
break;
case WP_FLECHETTE:
theFxScheduler.PlayEffect( "flechette/ricochet", origin, normal );
break;
default:
if ( rand() & 1 ) {
cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.grenadeBounce1 );
} else {
cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.grenadeBounce2 );
}
break;
}
}
//----------------------------------------------------------------------
void CG_MissileStick( centity_t *cent, int weapon, vec3_t position )
//----------------------------------------------------------------------
{
sfxHandle_t snd = 0;
switch( weapon )
{
case WP_FLECHETTE:
snd = cgs.media.flechetteStickSound;
break;
case WP_DET_PACK:
snd = cgs.media.detPackStickSound;
break;
case WP_TRIP_MINE:
snd = cgs.media.tripMineStickSound;
break;
}
if ( snd )
{
cgi_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, snd );
}
}
qboolean CG_VehicleWeaponImpact( centity_t *cent )
{//see if this is a missile entity that's owned by a vehicle and should do a special, overridden impact effect
if (cent->currentState.otherEntityNum2
&& g_vehWeaponInfo[cent->currentState.otherEntityNum2].iImpactFX)
{//missile is from a special vehWeapon
CG_PlayEffectID(g_vehWeaponInfo[cent->currentState.otherEntityNum2].iImpactFX, cent->lerpOrigin, cent->gent->pos1);
return qtrue;
}
return qfalse;
}
/*
=================
CG_MissileHitWall
Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing
=================
*/
void CG_MissileHitWall( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, qboolean altFire )
{
int parm;
switch( weapon )
{
case WP_BRYAR_PISTOL:
case WP_BLASTER_PISTOL:
case WP_JAWA:
if ( altFire )
{
parm = 0;
if ( cent->gent )
{
parm += cent->gent->count;
}
FX_BryarAltHitWall( origin, dir, parm );
}
else
{
FX_BryarHitWall( origin, dir );
}
break;
case WP_BLASTER:
FX_BlasterWeaponHitWall( origin, dir );
break;
case WP_BOWCASTER:
FX_BowcasterHitWall( origin, dir );
break;
case WP_REPEATER:
if ( altFire )
{
FX_RepeaterAltHitWall( origin, dir );
}
else
{
FX_RepeaterHitWall( origin, dir );
}
break;
case WP_DEMP2:
if ( altFire )
{
}
else
{
FX_DEMP2_HitWall( origin, dir );
}
break;
case WP_FLECHETTE:
if ( altFire )
{
theFxScheduler.PlayEffect( "flechette/alt_blow", origin, dir );
}
else
{
FX_FlechetteWeaponHitWall( origin, dir );
}
break;
case WP_ROCKET_LAUNCHER:
FX_RocketHitWall( origin, dir );
break;
case WP_CONCUSSION:
FX_ConcHitWall( origin, dir );
break;
case WP_THERMAL:
theFxScheduler.PlayEffect( "thermal/explosion", origin, dir );
theFxScheduler.PlayEffect( "thermal/shockwave", origin );
break;
case WP_EMPLACED_GUN:
FX_EmplacedHitWall( origin, dir, (qboolean)(cent->gent&&cent->gent->alt_fire) );
break;
case WP_ATST_MAIN:
FX_ATSTMainHitWall( origin, dir );
break;
case WP_ATST_SIDE:
if ( altFire )
{
theFxScheduler.PlayEffect( "atst/side_alt_explosion", origin, dir );
}
else
{
theFxScheduler.PlayEffect( "atst/side_main_impact", origin, dir );
}
break;
case WP_TRIP_MINE:
theFxScheduler.PlayEffect( "tripmine/explosion", origin, dir );
break;
case WP_DET_PACK:
theFxScheduler.PlayEffect( "detpack/explosion", origin, dir );
break;
case WP_TURRET:
theFxScheduler.PlayEffect( "turret/wall_impact", origin, dir );
break;
case WP_TUSKEN_RIFLE:
FX_TuskenShotWeaponHitWall( origin, dir );
break;
case WP_NOGHRI_STICK:
FX_NoghriShotWeaponHitWall( origin, dir );
break;
}
}
/*
-------------------------
CG_MissileHitPlayer
-------------------------
*/
void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, qboolean altFire )
{
gentity_t *other = NULL;
qboolean humanoid = qtrue;
if ( cent->gent )
{
other = &g_entities[cent->gent->s.otherEntityNum];
if( other->client )
{
class_t npc_class = other->client->NPC_class;
// check for all droids, maybe check for certain monsters if they're considered non-humanoid..?
if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 ||
npc_class == CLASS_PROTOCOL || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 ||
npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY )
{
humanoid = qfalse;
}
}
}
switch( weapon )
{
case WP_BRYAR_PISTOL:
case WP_BLASTER_PISTOL:
case WP_JAWA:
if ( altFire )
{
FX_BryarAltHitPlayer( origin, dir, humanoid );
}
else
{
FX_BryarHitPlayer( origin, dir, humanoid );
}
break;
case WP_BLASTER:
FX_BlasterWeaponHitPlayer( other, origin, dir, humanoid );
break;
case WP_BOWCASTER:
FX_BowcasterHitPlayer( origin, dir, humanoid );
break;
case WP_REPEATER:
if ( altFire )
{
FX_RepeaterAltHitPlayer( origin, dir, humanoid );
}
else
{
FX_RepeaterHitPlayer( origin, dir, humanoid );
}
break;
case WP_DEMP2:
if ( !altFire )
{
FX_DEMP2_HitPlayer( origin, dir, humanoid );
}
// Do a full body effect here for some more feedback
if ( other && other->client )
{
other->s.powerups |= ( 1 << PW_SHOCKED );
other->client->ps.powerups[PW_SHOCKED] = cg.time + 1000;
}
break;
case WP_FLECHETTE:
if ( altFire )
{
theFxScheduler.PlayEffect( "flechette/alt_blow", origin, dir );
}
else
{
FX_FlechetteWeaponHitPlayer( origin, dir, humanoid );
}
break;
case WP_ROCKET_LAUNCHER:
FX_RocketHitPlayer( origin, dir, humanoid );
break;
case WP_CONCUSSION:
FX_ConcHitPlayer( origin, dir, humanoid );
break;
case WP_THERMAL:
theFxScheduler.PlayEffect( "thermal/explosion", origin, dir );
theFxScheduler.PlayEffect( "thermal/shockwave", origin );
break;
case WP_EMPLACED_GUN:
FX_EmplacedHitPlayer( origin, dir, (qboolean)(cent->gent&&cent->gent->alt_fire) );
break;
case WP_TRIP_MINE:
theFxScheduler.PlayEffect( "tripmine/explosion", origin, dir );
break;
case WP_DET_PACK:
theFxScheduler.PlayEffect( "detpack/explosion", origin, dir );
break;
case WP_TURRET:
theFxScheduler.PlayEffect( "turret/flesh_impact", origin, dir );
break;
case WP_ATST_MAIN:
FX_EmplacedHitWall( origin, dir, qfalse );
break;
case WP_ATST_SIDE:
if ( altFire )
{
theFxScheduler.PlayEffect( "atst/side_alt_explosion", origin, dir );
}
else
{
theFxScheduler.PlayEffect( "atst/side_main_impact", origin, dir );
}
break;
case WP_TUSKEN_RIFLE:
FX_TuskenShotWeaponHitPlayer( other, origin, dir, humanoid );
break;
case WP_NOGHRI_STICK:
FX_NoghriShotWeaponHitPlayer( other, origin, dir, humanoid );
break;
}
}