jkxr/Projects/Android/jni/OpenJK/codeJK2/cgame/cg_weapons.cpp
Simon 19ee9179e8 Many improvements
Added cvar g_saberAutoDeflect1stPerson so saber is only ever used to deflect lasers in first person
Made selector always draw using HMD YAW so it remains in front when turning i.r.l
Updated the hand model (@baggyg)
Updated the crawler text with the patron credits
Added @MuadDib's menu changes
Aligned all weapons
Scaled the saber hilt down a little bit

Co-Authored-By: Grant Bagwell <general@grantbagwell.co.uk>
2022-10-13 23:19:44 +01:00

3397 lines
88 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_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
#include "../game/wp_saber.h"
#include "../game/g_local.h"
#include "../game/anims.h"
#include <bg_local.h>
#include <JKVR/VrClientInfo.h>
extern void CG_LightningBolt( centity_t *cent, vec3_t origin );
#define PHASER_HOLDFRAME 2
int cgi_UI_GetMenuInfo(char *menuFile,int *x,int *y);
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 ( weaponNum >= WP_NUM_WEAPONS ) {
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 == NULL_HANDLE )
{
CG_Error( "Couldn't find weapon model %s\n", 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 (QDECL *)(struct centity_s *,const struct weaponInfo_s *))weaponData[weaponNum].func;
}
if (weaponData[weaponNum].altfunc)
{
weaponInfo->alt_missileTrailFunc = (void (QDECL *)(struct centity_s *,const struct weaponInfo_s *))weaponData[weaponNum].altfunc;
}
switch ( weaponNum ) //extra client only stuff
{
case WP_SABER:
//saber/force FX
theFxScheduler.RegisterEffect( "spark" );
theFxScheduler.RegisterEffect( "blood_sparks" );
theFxScheduler.RegisterEffect( "force_touch" );
theFxScheduler.RegisterEffect( "saber_block" );
theFxScheduler.RegisterEffect( "saber_cut" );
theFxScheduler.RegisterEffect( "blaster/smoke_bolton" );
theFxScheduler.RegisterEffect( "saber/fizz" );
theFxScheduler.RegisterEffect( "saber/boil" );
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" );
cgs.media.HUDSaberStyleFast = cgi_R_RegisterShader( "gfx/hud/saber_stylesFast" );
cgs.media.HUDSaberStyleMed = cgi_R_RegisterShader( "gfx/hud/saber_stylesMed" );
cgs.media.HUDSaberStyleStrong = cgi_R_RegisterShader( "gfx/hud/saber_stylesStrong" );
//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 ) );
}
//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.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" );
//saber graphics
cgs.media.saberBlurShader = cgi_R_RegisterShader("gfx/effects/sabers/saberBlur");
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" );
break;
case WP_BRYAR_PISTOL:
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" );
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_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" );
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/atst_health_frame" );
cgs.media.ladyLuckHealthShader = cgi_R_RegisterShaderNoMip( "gfx/hud/ladyluck_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:
//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" );
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_BLASTER_PISTOL: // enemy version
cgs.effects.bryarShotEffect = theFxScheduler.RegisterEffect( "bryar/shot" );
cgs.effects.bryarPowerupShotEffect = theFxScheduler.RegisterEffect( "bryar/crackleShot" );
cgs.effects.bryarWallImpactEffect = theFxScheduler.RegisterEffect( "bryar/wall_impact" );
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;
}
}
/*
=================
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.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_WEAPONREADY5:
case TORSO_WEAPONREADY6:
case TORSO_WEAPONREADY7:
case TORSO_WEAPONREADY8:
case TORSO_WEAPONREADY9:
case TORSO_WEAPONREADY10:
case TORSO_WEAPONREADY11:
case TORSO_WEAPONREADY12:
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 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);
vec3_t offset;
//Weapon offset debugging
float scale=1.0f;
if (strcmp(cgi_Cvar_Get("vr_control_scheme"), "99") == 0) {
scale = vr->test_scale;
//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, vr->test_angles, zero, 1.0);
Matrix4x4_Concat(m3, m1, m2);
Matrix4x4_ConvertToEntity(m3, angles, zero);
VectorCopy(vr->test_offset, offset);
int w = cgi_R_Font_StrLenPixels(vr->test_name, cgs.media.qhFontSmall, 1.0f);
int x = ( SCREEN_WIDTH - w ) / 2;
cgi_R_Font_DrawString(x, (SCREEN_HEIGHT / 2), vr->test_name, colorTable[CT_ICON_BLUE], cgs.media.qhFontSmall, -1, 1.0f);
} else {
if (ps->weapon != 0)
{
char cvar_name[64];
Com_sprintf(cvar_name, sizeof(cvar_name), "vr_weapon_adjustment_%i", ps->weapon);
char* weapon_adjustment = cgi_Cvar_Get(cvar_name);
if (strlen(weapon_adjustment) > 0) {
vec3_t adjust;
vec3_t temp_offset;
VectorClear(temp_offset);
VectorClear(adjust);
sscanf(weapon_adjustment, "%f,%f,%f,%f,%f,%f,%f", &scale,
&(temp_offset[0]), &(temp_offset[1]), &(temp_offset[2]),
&(adjust[PITCH]), &(adjust[YAW]), &(adjust[ROLL]));
VectorScale(temp_offset, scale, offset);
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 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 )
{
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 )
{
vec3_t up={0,0,1}, ax[3];
VectorCopy( dir, ax[0] );
CrossProduct( up, ax[0], ax[1] );
CrossProduct( ax[0], ax[1], ax[2] );
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 gun;
refEntity_t flash;
vec3_t angles;
const weaponInfo_t *weapon;
weaponData_t *wData;
centity_t *cent;
float fovOffset, leanOffset;
float cgFov = (cg_fovViewmodel.integer) ? cg_fovViewmodel.integer : cg_fov.integer;
int i;
if (cgFov < 1)
cgFov = 1;
else if (cgFov > 130)
cgFov = 130;
// 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 tAng, fxDir, temp;
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, fxAxis );
}
else
{//line
AngleVectors( tAng, fxDir, NULL, NULL );
theFxScheduler.PlayEffect( cgs.effects.forceLightning, temp, fxDir );
}
}
// 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;
}
// don't draw if testing a gun model
if ( cg.testGun )
{
return;
}
// drop gun lower at higher fov
float actualFOV;
gentity_t *player = &g_entities[0];
if ( (cg.snap->ps.forcePowersActive&(1<<FP_SPEED)) && player->client->ps.forcePowerDuration[FP_SPEED] )//cg.renderingThirdPerson &&
{
actualFOV = CG_ForceSpeedFOV(cg_fov.value);
actualFOV = (cg_fovViewmodel.integer) ? actualFOV + (cg_fovViewmodel.integer - cg_fov.integer) : actualFOV;
}
else
{
actualFOV = (cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value;
}
if ( cg_fovViewmodelAdjust.integer && actualFOV > 80 )
{
fovOffset = -0.1 * ( actualFOV - 80 );
}
else
{
fovOffset = 0;
}
if ( ps->leanofs != 0 )
{
//add leaning offset
leanOffset = ps->leanofs * 0.25f;
fovOffset += fabs((double)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 )
{
cgi_S_AddLoopingSound( cent->currentState.number,
cent->lerpOrigin,
vec3_origin,
weapon->firingSound );
}
if ( ps->weapon == WP_NONE )
{
return;
}
if (strcmp(cgi_Cvar_Get("vr_control_scheme"), "99") == 0) {
vec3_t origin;
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, 256, forward, endForward);
static vec3_t RED = {1.0f,0.0f,0.0f};
FX_AddLine( origin, endForward, 0.1f, 4.0f, 0.0f,
1.0f, 0.0f, 0.0f,
RED, RED, 0.0f,
120, cgi_R_RegisterShader( "gfx/effects/whiteline2" ),
FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
VectorMA(origin, 20, right, endRight);
vec3_t BLUE = {0.0f,0.0f,1.0f};
FX_AddLine( origin, endRight, 0.1f, 4.0f, 0.0f,
1.0f, 0.0f, 0.0f,
BLUE, BLUE, 0.0f,
120, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
VectorMA(origin, 20, up, endUp);
vec3_t GREEN = {0.0f,1.0f,0.0f};
FX_AddLine( origin, endUp, 0.1f, 4.0f, 0.0f,
1.0f, 0.0f, 0.0f,
GREEN, GREEN, 0.0f,
120, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
CG_CenterPrint(vr->test_name, 240);
}
// set up gun position
float scale = CG_CalculateWeaponPositionAndScale( ps, hand.origin, angles );
VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin );
VectorMA( hand.origin, (cg_gun_y.value+leanOffset), cg.refdef.viewaxis[1], hand.origin );
VectorMA( hand.origin, (cg_gun_z.value+fovOffset), 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( cgFov * ( M_PI/180 ) * 0.5f );
VectorScale( hand.axis[0], fracWeapFOV, hand.axis[0] );
}
*/
// map torso animations to weapon animations
if ( cg_gun_frame.integer )
{
// development tool
hand.frame = hand.oldframe = cg_gun_frame.integer;
hand.backlerp = 0;
}
else
{
// 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
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;
//---------
// 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];
CG_GetTagWorldPosition( &gun, "tag_flash", org_, axis_ );
if ( cent->gent->client->ps.saberActive && cent->gent->client->ps.saberLength < cent->gent->client->ps.saberLengthMax )
{
cent->gent->client->ps.saberLength += cg.frametime*0.03;
if ( cent->gent->client->ps.saberLength > cent->gent->client->ps.saberLengthMax )
{
cent->gent->client->ps.saberLength = cent->gent->client->ps.saberLengthMax;
}
}
// 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 );
VectorCopy( axis_[0], cent->gent->client->renderInfo.muzzleDir );
}
//---------
// CG_AddRefEntityWithPowerups( &gun, cent->currentState.powerups, cent->gent );
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 (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 )
{
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->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 )
{
// 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;
CGCam_Shake( 0.1f, 100 );
}
else
{
CGCam_Shake( val * val * 0.3f, 100 );
}
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 ( 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 || weaponIndex == WP_STUN_BATON )
{
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_DrawIconBackground
===================
*/
void CG_DrawIconBackground(void)
{
int height,xAdd,x2,y2,t;
int prongLeftX,prongRightX;
qhandle_t background;
if (( cg.zoomMode != 0 ) || !( cg_drawHUD.integer ))
{
return;
}
if ((cg.snap->ps.viewEntity>0&&cg.snap->ps.viewEntity<ENTITYNUM_WORLD))
{
return;
}
if (!cgi_UI_GetMenuInfo("iconbackground",&x2,&y2))
{
return;
}
prongLeftX =x2+37;
prongRightX =x2+544;
if (((cg.inventorySelectTime+WEAPON_SELECT_TIME)>cg.time) || (cgs.media.currentBackground == ICON_INVENTORY)) // Display inventory background?
{
background = cgs.media.inventoryIconBackground;
}
else if (((cg.weaponSelectTime+WEAPON_SELECT_TIME)>cg.time) || (cgs.media.currentBackground == ICON_WEAPONS)) // Display weapon background?
{
background = cgs.media.weaponIconBackground;
}
else // Display force background?
{
background = cgs.media.forceIconBackground;
}
if ((cg.iconSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
{
if (cg.iconHUDActive) // The time is up, but we still need to move the prongs back to their original position
{
t = cg.time - (cg.iconSelectTime+WEAPON_SELECT_TIME);
cg.iconHUDPercent = t/ 130.0f;
cg.iconHUDPercent = 1 - cg.iconHUDPercent;
if (cg.iconHUDPercent<0)
{
cg.iconHUDActive = qfalse;
cg.iconHUDPercent=0;
}
xAdd = (int) 8*cg.iconHUDPercent;
height = (int) (60.0f*cg.iconHUDPercent);
CG_DrawPic( x2+60, y2+30, 460, -height, background); // Top half
CG_DrawPic( x2+60, y2+30-2,460, height, background); // Bottom half
}
else
{
xAdd = 0;
}
cgi_R_SetColor( colorTable[CT_WHITE] );
CG_DrawPic( prongLeftX+xAdd, y2-10, 40, 80, cgs.media.weaponProngsOff);
CG_DrawPic( prongRightX-xAdd, y2-10, -40, 80, cgs.media.weaponProngsOff);
return;
}
prongLeftX =x2+37;
prongRightX =x2+544;
if (!cg.iconHUDActive)
{
t = cg.time - cg.iconSelectTime;
cg.iconHUDPercent = t/ 130.0f;
// Calc how far into opening sequence we are
if (cg.iconHUDPercent>1)
{
cg.iconHUDActive = qtrue;
cg.iconHUDPercent=1;
}
else if (cg.iconHUDPercent<0)
{
cg.iconHUDPercent=0;
}
}
else
{
cg.iconHUDPercent=1;
}
cgi_R_SetColor( colorTable[CT_WHITE] );
height = (int) (60.0f*cg.iconHUDPercent);
CG_DrawPic( x2+60, y2+30, 460, -height, background); // Top half
CG_DrawPic( x2+60, y2+30-2,460, height, background); // Bottom half
// And now for the prongs
if ((cg.inventorySelectTime+WEAPON_SELECT_TIME)>cg.time)
{
cgs.media.currentBackground = ICON_INVENTORY;
background = cgs.media.inventoryProngsOn;
}
else if ((cg.weaponSelectTime+WEAPON_SELECT_TIME)>cg.time)
{
cgs.media.currentBackground = ICON_WEAPONS;
background = cgs.media.weaponProngsOn;
}
else
{
cgs.media.currentBackground = ICON_FORCE;
background = cgs.media.forceProngsOn;
}
// Side Prongs
cgi_R_SetColor( colorTable[CT_WHITE]);
xAdd = (int) 8*cg.iconHUDPercent;
CG_DrawPic( prongLeftX+xAdd, y2-10, 40, 80, background);
CG_DrawPic( prongRightX-xAdd, y2-10, -40, 80, background);
}
int cgi_UI_GetItemText(char *menuFile,char *itemName, char *text);
char *weaponDesc[13] =
{
"SABER_DESC",
"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",
"STUN_BATON_DESC",
};
/*
===================
CG_DrawDataPadWeaponSelect
===================
*/
void CG_DrawDataPadWeaponSelect( void )
{
int i;
int bits;
int count;
int smallIconSize,bigIconSize;
int holdX,x,y,pad;
int sideLeftIconCnt,sideRightIconCnt;
int sideMax,holdCount,iconCnt;
vec4_t calcColor;
char text[1024]={0};
vec4_t textColor = { .875f, .718f, .121f, 1.0f };
// 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;
for ( i = 1 ; i < 16 ; i++ )
{
if ( bits & ( 1 << i ) )
{
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;
}
// This seems to be a problem is datapad comes up too early
if (cg.DataPadWeaponSelect<FIRST_WEAPON)
{
cg.DataPadWeaponSelect = FIRST_WEAPON;
}
else if (cg.DataPadWeaponSelect>MAX_PLAYER_WEAPONS)
{
cg.DataPadWeaponSelect = MAX_PLAYER_WEAPONS;
}
i = cg.DataPadWeaponSelect - 1;
if (i<1)
{
i = 13;
}
smallIconSize = 40;
bigIconSize = 80;
pad = 8;
x = 320;
y = 300;
// 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;
for (iconCnt=1;iconCnt<(sideLeftIconCnt+1);i--)
{
if (i<1)
{
i = 13;
}
if ( !(bits & ( 1 << i ))) // Does he have this weapon?
{
continue;
}
++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, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, y+10, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX -= (smallIconSize+pad);
}
}
// Current Center Icon
//height = bigIconSize * cg.iconHUDPercent;
cgi_R_SetColor(NULL);
// char buffer[256];
// cgi_UI_GetItemText("datapadWeaponsMenu",va("weapondesc%d",cg.DataPadWeaponSelect+1),buffer);
if (weaponData[cg.DataPadWeaponSelect].weaponIcon[0])
{
weaponInfo_t *weaponInfo;
CG_RegisterWeapon( cg.DataPadWeaponSelect );
weaponInfo = &cg_weapons[cg.DataPadWeaponSelect];
if (!CG_WeaponCheck(cg.DataPadWeaponSelect))
{
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2))+10, bigIconSize, bigIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2))+10, bigIconSize, bigIconSize, weaponInfo->weaponIcon );
}
}
i = cg.DataPadWeaponSelect + 1;
if (i> 13)
{
i = 1;
}
// Right side ICONS
// Work forwards from current icon
cgi_R_SetColor( calcColor);
holdX = x + (bigIconSize/2) + pad;
//height = smallIconSize * cg.iconHUDPercent;
for (iconCnt=1;iconCnt<(sideRightIconCnt+1);i++)
{
if (i>13)
{
i = 1;
}
if ( !(bits & ( 1 << i ))) // Does he have this weapon?
{
continue;
}
++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, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, y+10, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX += (smallIconSize+pad);
}
}
// draw the weapon description
x= 40;
y= 70;
cgi_SP_GetStringTextString( va("INGAME_%s",weaponDesc[cg.DataPadWeaponSelect-1]), text, sizeof(text) );
if (text[0])
{
CG_DisplayBoxedText(70,50,500,300,text,
cgs.media.qhFontSmall,
0.7f,
textColor
);
}
/* CG_DisplayBoxedText(70,50,500,300,weaponDesc[cg.DataPadWeaponSelect-1],
cgs.media.qhFontSmall,
0.7f,
colorTable[CT_WHITE]
);
*/
cgi_R_SetColor( NULL );
}
/*
===================
CG_DrawDataPadIconBackground
===================
*/
void CG_DrawDataPadIconBackground(int backgroundType)
{
int height,xAdd,x2,y2;
int prongLeftX,prongRightX;
qhandle_t background;
x2 = 0;
y2 = 295;
prongLeftX =x2+97;
prongRightX =x2+544;
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;
}
/* if ((cg.iconDataPadSelectTime+WEAPON_SELECT_TIME)<cg.time) // Time is up for the HUD to display
{
if (cg.iconDataPadHUDActive) // The time is up, but we still need to move the prongs back to their original position
{
t = cg.time - (cg.iconDataPadSelectTime+WEAPON_SELECT_TIME);
cg.iconDataPadHUDPercent = t/ 130.0f;
cg.iconDataPadHUDPercent = 1 - cg.iconDataPadHUDPercent;
if (cg.iconDataPadHUDPercent<0)
{
cg.iconDataPadHUDActive = qfalse;
cg.iconDataPadHUDPercent=0;
}
xAdd = (int) 8*cg.iconDataPadHUDPercent;
height = (int) (60.0f*cg.iconDataPadHUDPercent);
CG_DrawPic( x2+60, y2+30, 460, -height, background); // Top half
CG_DrawPic( x2+60, y2+30-2,460, height, background); // Bottom half
}
else
{
xAdd = 0;
}
cgi_R_SetColor( colorTable[CT_WHITE] );
CG_DrawPic( prongLeftX+xAdd, y2-10, 40, 80, cgs.media.weaponProngsOff);
CG_DrawPic( prongRightX-xAdd, y2-10, -40, 80, cgs.media.weaponProngsOff);
return;
}
*/
prongLeftX =x2+97;
prongRightX =x2+544;
/*
if (!cg.iconDataPadHUDActive)
{
t = cg.time - cg.iconDataPadSelectTime;
cg.iconDataPadHUDPercent = t/ 130.0f;
// Calc how far into opening sequence we are
if (cg.iconDataPadHUDPercent>1)
{
cg.iconDataPadHUDActive = qtrue;
cg.iconDataPadHUDPercent=1;
}
else if (cg.iconDataPadHUDPercent<0)
{
cg.iconDataPadHUDPercent=0;
}
}
else
{
cg.iconDataPadHUDPercent=1;
}
*/
cgi_R_SetColor( colorTable[CT_WHITE] );
// height = (int) (60.0f*cg.iconDataPadHUDPercent);
height = (int) 60.0f;
CG_DrawPic( x2+110, y2+30, 410, -height, background); // Top half
CG_DrawPic( x2+110, y2+30-2,410, height, background); // Bottom half
// And now for the prongs
if (backgroundType==ICON_INVENTORY)
{
cgs.media.currentDataPadIconBackground = ICON_INVENTORY;
background = cgs.media.inventoryProngsOn;
}
else if (backgroundType==ICON_WEAPONS)
{
cgs.media.currentDataPadIconBackground = ICON_WEAPONS;
background = cgs.media.weaponProngsOn;
}
else
{
cgs.media.currentDataPadIconBackground = ICON_FORCE;
background = cgs.media.forceProngsOn;
}
// Side Prongs
cgi_R_SetColor( colorTable[CT_WHITE]);
// xAdd = (int) 8*cg.iconDataPadHUDPercent;
xAdd = (int) 8;
CG_DrawPic( prongLeftX+xAdd, y2-10, 40, 80, background);
CG_DrawPic( prongRightX-xAdd, y2-10, -40, 80, background);
}
/*
===============
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
===================
*/
void CG_DrawWeaponSelect( void )
{
int i;
int bits;
int count;
int smallIconSize,bigIconSize;
int holdX,x,y,x2,y2,pad;
int sideLeftIconCnt,sideRightIconCnt;
int sideMax,holdCount,iconCnt;
//int height;
vec4_t calcColor;
vec4_t textColor = { .875f, .718f, .121f, 1.0f };
if (!cgi_UI_GetMenuInfo("weaponselecthud",&x2,&y2))
{
return;
}
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;
for ( i = 1 ; i < 16 ; i++ )
{
if ( bits & ( 1 << i ) )
{
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;
}
i = cg.weaponSelect - 1;
if (i<1)
{
i = 13;
}
smallIconSize = 40;
bigIconSize = 80;
pad = 12;
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;
for (iconCnt=1;iconCnt<(sideLeftIconCnt+1);i--)
{
if (i<1)
{
i = 13;
}
if ( !(bits & ( 1 << i ))) // Does he have this weapon?
{
continue;
}
++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, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, y+10, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX -= (smallIconSize+pad);
}
}
// 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, bigIconSize, bigIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( x-(bigIconSize/2), (y-((bigIconSize-smallIconSize)/2))+10, bigIconSize, bigIconSize, weaponInfo->weaponIcon );
}
}
i = cg.weaponSelect + 1;
if (i> 13)
{
i = 1;
}
// Right side ICONS
// Work forwards from current icon
cgi_R_SetColor( calcColor);
holdX = x + (bigIconSize/2) + pad;
//height = smallIconSize * cg.iconHUDPercent;
for (iconCnt=1;iconCnt<(sideRightIconCnt+1);i++)
{
if (i>13)
{
i = 1;
}
if ( !(bits & ( 1 << i ))) // Does he have this weapon?
{
continue;
}
++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, smallIconSize, smallIconSize, weaponInfo->weaponIconNoAmmo );
}
else
{
CG_DrawPic( holdX, y+10, smallIconSize, smallIconSize, weaponInfo->weaponIcon );
}
holdX += (smallIconSize+pad);
}
}
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("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);
CG_AdjustFrom640Int(&x, &y, NULL, NULL);
cgi_R_Font_DrawString(x, y, text, textColor, cgs.media.qhFontSmall, -1, 1.0f);
}
}
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 sqitch to lightsaber, have to stay there for at least half a second!
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;
for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ )
{
cg.weaponSelect++;
if ( cg.weaponSelect < FIRST_WEAPON || cg.weaponSelect > MAX_PLAYER_WEAPONS) {
cg.weaponSelect = FIRST_WEAPON;
}
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++ )
{
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++ )
{
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;
for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ ) {
cg.weaponSelect--;
if ( cg.weaponSelect < FIRST_WEAPON || 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->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->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
num = WP_STUN_BATON;
}
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].currentState.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].currentState.saberActive )
{
cg_entities[0].gent->client->ps.saberActive = qfalse;
if ( cg_entities[0].currentState.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, cgi_S_RegisterSound( "sound/weapons/saber/saberoff.wav" ) );
}
else
{
cgi_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/saber/saberoff.wav" ) );
}
}
else
{
cg_entities[0].gent->client->ps.saberActive = qtrue;
}
}
}
}
}
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.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) {
return; // don't have the weapon
}
SetWeaponSelectTime();
// cg.weaponSelectTime = cg.time;
cg.weaponSelect = num;
}
void Cmd_UseInventory_f(gentity_t *ent);
void CG_ToggleSaber_f( )
{
player->client->ps.saberActive = (qboolean)!player->client->ps.saberActive;
if (!player->client->ps.saberActive)
{
G_SoundOnEnt( player, CHAN_WEAPON, "sound/weapons/saber/saberoff.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 == -1)
{
cg.itemSelectorType = 0;
return;
}
if (cg.itemSelectorType == 0)
{
if (cg.weaponSelect == cg.itemSelectorSelection)
{
return;
}
cg.weaponSelectTime = cg.time;
cg.weaponSelect = cg.itemSelectorSelection;
}
else if (cg.itemSelectorType == 1)
{
if (cg.forcepowerSelect == cg.itemSelectorSelection)
{
return;
}
cg.forcepowerSelectTime = cg.time;
cg.forcepowerSelect = cg.itemSelectorSelection;
}
else if (cg.itemSelectorType == 2)
{
cg.inventorySelectTime = cg.time;
cg.inventorySelect = cg.itemSelectorSelection;
//Immediately use the selected inventory item
if (player)
{
Cmd_UseInventory_f(player);
}
}
//reset ready for next time
cg.itemSelectorSelection = -1;
}
void CG_ItemSelectorNext_f( void )
{
cg.itemSelectorType = (cg.itemSelectorType+1) % 3;
cg.itemSelectorTime = cg.time;
}
void CG_ItemSelectorPrev_f( void )
{
if (--cg.itemSelectorType < 0)
cg.itemSelectorType = 2;
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.itemSelectorTime == 0)
{
cg.itemSelectorTime = cg.time;
VectorCopy(vr->weaponangles, cg.itemSelectorAngles);
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;
BG_CalculateVRWeaponPosition(controllerOrigin, controllerAngles);
VectorSubtract(vr->weaponposition, cg.itemSelectorOrigin, controllerOffset);
vec3_t wheelAngles, wheelOrigin, beamOrigin, wheelForward, wheelRight, wheelUp;
cg.itemSelectorAngles[YAW] = vr->hmdorientation[YAW];
BG_CalculateVRPositionInWorld(cg.itemSelectorOrigin, cg.itemSelectorOffset, cg.itemSelectorAngles, 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);
{
vec3_t color = { 0, 0, 255 };
FX_AddLine( beamOrigin, selectorOrigin, 0.1f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
color, color, 0.0f,
60, cgi_R_RegisterShader( "gfx/misc/nav_line" ),
FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
int count;
switch (cg.itemSelectorType)
{
case 0: //weapons
count = WP_EMPLACED_GUN;
break;
case 1: // force powers
count = MAX_SHOWPOWERS;
break;
case 2: //gadgets
count = INV_GOODIE_KEY;
break;
}
qboolean selected = qfalse;
for (int index = 0; index < count; ++index)
{
int itemId = index;
if (cg.itemSelectorType == 0) {
itemId = index+1; // We need to ignore WP_NONE for weapons
if (itemId == count)
{
break;
}
#ifdef _DEMO
if (itemId == WP_SABER ||
itemId == WP_BRYAR_PISTOL ||
itemId == WP_BLASTER ||
itemId == WP_FLECHETTE ||
itemId == WP_REPEATER ||
itemId == WP_THERMAL) {
CG_RegisterWeapon(itemId);
} else {
continue;
}
#else
CG_RegisterWeapon(itemId);
#endif
}
{
bool selectable;
switch (cg.itemSelectorType)
{
case 0: //weapons
selectable = CG_WeaponSelectable(itemId, cg.weaponSelect, qfalse) && cg.snap->ps.ammo[weaponData[itemId].ammoIndex];
break;
case 1: // force powers
selectable = ForcePower_Valid(itemId);
break;
case 2: //gadgets
selectable = CG_InventorySelectable(itemId) && inv_icons[itemId];
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] =
(360 / count) * (itemId - 1);
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;
//trap_HapticEvent("selector_icon", 0, 0, 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 0: //weapons
sprite.customShader = cg_weapons[itemId].weaponIcon;
break;
case 1: // force powers
sprite.customShader = force_icons[showPowers[itemId]];
break;
case 2: //gadgets
sprite.customShader = inv_icons[itemId];
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 = -1;
}
}
/*
===================
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;
}
//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;
}
}
// 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 );
}
}
/*
=================
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:
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_THERMAL:
theFxScheduler.PlayEffect( "thermal/explosion", origin, dir );
theFxScheduler.PlayEffect( "thermal/shockwave", origin );
break;
case WP_EMPLACED_GUN:
FX_EmplacedHitWall( origin, dir );
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;
}
}
/*
-------------------------
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:
if ( altFire )
{
FX_BryarAltHitPlayer( origin, dir, humanoid );
}
else
{
FX_BryarHitPlayer( origin, dir, humanoid );
}
break;
case WP_BLASTER:
FX_BlasterWeaponHitPlayer( 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_THERMAL:
theFxScheduler.PlayEffect( "thermal/explosion", origin, dir );
theFxScheduler.PlayEffect( "thermal/shockwave", origin );
break;
case WP_EMPLACED_GUN:
FX_EmplacedHitWall( 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/flesh_impact", origin, dir );
break;
case WP_ATST_MAIN:
FX_EmplacedHitWall( 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;
}
}