jkxr/Projects/Android/jni/OpenJK/codeJK2/cgame/cg_weapons.cpp

3651 lines
96 KiB
C++
Raw Normal View History

/*
===========================================================================
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 <JKXR/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 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 )
{
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 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( 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" ),
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( 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" ),
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( 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" ),
FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
CG_CenterPrint(vr->weaponadjustment_info, 240);
}
// set up gun position
float scale = CG_CalculateWeaponPositionAndScale( ps, hand.origin, angles );
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, 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 | 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];
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;
}
2023-03-30 21:16:37 +00:00
if ( val < 0.0f )
{
val = 0.0f;
}
else if ( val > 1.0f )
{
val = 1.0f;
}
2023-04-20 18:55:48 +00:00
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 ( 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_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]);
}
}
/*
===================
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);
2023-03-07 22:52:55 +00:00
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 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);
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;
}
}
}
2022-10-12 22:03:17 +00:00
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 == ST_NONE)
{
return;
}
if (cg.itemSelectorType == ST_WEAPON) // weapons
{
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)
2022-10-16 15:50:32 +00:00
{
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 quik*\n");
CG_CenterPrint("Quick Saved", 240);
} else {
cgi_SendConsoleCommand("load quik\n");
}
}
//reset ready for next time
cg.itemSelectorSelection = ST_NONE;
}
void CG_ItemSelectorNext_f( void )
{
if (cg.itemSelectorType >= ST_FORCE_POWER)
2022-10-16 15:50:32 +00:00
{
cg.itemSelectorType = (cg.itemSelectorType == ST_FORCE_POWER) ? ST_QUICK_SAVE : ST_FORCE_POWER;
2022-10-16 15:50:32 +00:00
return;
}
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
//Only show the stance selection if using saber and in third person
int selectors = ((cent->gent->client->ps.forcePowersKnown & ( 1 << FP_SABER_OFFENSE )) &&
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)
2022-10-16 15:50:32 +00:00
{
cg.itemSelectorType = (cg.itemSelectorType == ST_FORCE_POWER) ? ST_QUICK_SAVE : ST_FORCE_POWER;
2022-10-16 15:50:32 +00:00
return;
}
centity_t *cent = &cg_entities[cg.snap->ps.clientNum];
//Only show the stance selection if using saber and in third person
int selectors = ((cent->gent->client->ps.forcePowersKnown & ( 1 << FP_SABER_OFFENSE )) &&
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;
2022-10-16 15:50:32 +00:00
if (vr->item_selector == 2)
{
cg.itemSelectorType = ST_FORCE_POWER;
VectorCopy(vr->offhandposition[0], cg.itemSelectorOrigin);
2022-10-16 15:50:32 +00:00
VectorCopy(vr->offhandoffset, cg.itemSelectorOffset);
}
else {
cg.itemSelectorType = ST_WEAPON;
2022-10-16 15:50:32 +00:00
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)
2022-10-16 15:50:32 +00:00
{
BG_CalculateVROffHandPosition(controllerOrigin, controllerAngles);
VectorSubtract(vr->offhandposition[0], cg.itemSelectorOrigin, controllerOffset);
2022-10-16 15:50:32 +00:00
}
else
{
BG_CalculateVRWeaponPosition(controllerOrigin, controllerAngles);
VectorSubtract(vr->weaponposition, cg.itemSelectorOrigin, controllerOffset);
}
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];
refEntity_t beam;
beam.shaderRGBA[3] = 0xff;
int count;
switch (cg.itemSelectorType)
{
case ST_WEAPON: //weapons
if (vr->in_vehicle)
count = 2;
else
count = WP_MELEE;
beam.shaderRGBA[0] = 0xff;
beam.shaderRGBA[1] = 0xae;
beam.shaderRGBA[2] = 0x40;
break;
case ST_GADGET: //gadgets
count = INV_GOODIE_KEY;
beam.shaderRGBA[0] = 0x00;
beam.shaderRGBA[1] = 0xff;
beam.shaderRGBA[2] = 0x00;
break;
case ST_FIGHTING_STYLE: //fighting style
count = 3;
beam.shaderRGBA[0] = 0xff;
beam.shaderRGBA[1] = 0xff;
beam.shaderRGBA[2] = 0xff;
break;
case ST_FORCE_POWER: // force powers
2022-10-16 15:50:32 +00:00
count = MAX_SHOWPOWERS;
beam.shaderRGBA[0] = 0x00;
beam.shaderRGBA[1] = 0x00;
beam.shaderRGBA[2] = 0xff;
2022-10-16 15:50:32 +00:00
break;
case ST_QUICK_SAVE:
count = 2;
beam.shaderRGBA[0] = 0xff;
beam.shaderRGBA[1] = 0xff;
beam.shaderRGBA[2] = 0xff;
break;
}
VectorCopy(beamOrigin, beam.oldorigin);
VectorCopy(selectorOrigin, beam.origin );
beam.customShader = cgi_R_RegisterShader( "gfx/misc/whiteline2" );
beam.reType = RT_LINE;
beam.radius = 0.3f;
cgi_R_AddRefEntityToScene( &beam );
if (cg.itemSelectorType == ST_WEAPON) // weapons
{
if (cg.weaponSelect != WP_NONE &&
cg.weaponSelect != WP_MELEE) {
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 == ST_FIGHTING_STYLE) // fighting style
{
//For the fighting style show the active one in the middle
int level = cent->gent->client->ps.saberAnimLevel;
if (cent->gent->client->ps.forcePowersKnown & (1 << FP_SABER_OFFENSE) &&
level > FORCE_LEVEL_0) {
refEntity_t sprite;
memset(&sprite, 0, sizeof(sprite));
VectorCopy(wheelOrigin, sprite.origin);
sprite.reType = RT_SPRITE;
switch (level) {
case FORCE_LEVEL_1:
sprite.customShader = cgs.media.HUDSaberStyleFast;
break;
case FORCE_LEVEL_2:
sprite.customShader = cgs.media.HUDSaberStyleMed;
break;
case FORCE_LEVEL_3:
sprite.customShader = cgs.media.HUDSaberStyleStrong;
break;
}
sprite.radius = 1.8f;
memset(sprite.shaderRGBA, 0xff, 4);
cgi_R_AddRefEntityToScene(&sprite);
}
}
else if (cg.itemSelectorType == ST_FORCE_POWER) // force powers
2022-10-16 15:50:32 +00:00
{
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);
}
2022-10-16 15:50:32 +00:00
}
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);
}
const char *info = CG_ConfigString( CS_SERVERINFO );
const char *mapname = Info_ValueForKey( info, "mapname" );
qboolean selected = qfalse;
for (int index = 0; index < count; ++index)
{
int itemId = index;
if (cg.itemSelectorType == 0) {
if (vr->in_vehicle)
{
itemId = WP_ATST_MAIN + index;
}
else
{
itemId = index + 1; // We need to ignore WP_NONE for weapons
if (itemId == count)
{
break;
}
}
}
{
bool selectable;
switch (cg.itemSelectorType)
{
case ST_WEAPON: //weapons
selectable = vr->in_vehicle || // both ATST weapons are always selectable
(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
{
if (cent->gent->client->ps.forcePowersKnown & ( 1 << FP_SABER_OFFENSE )) {
selectable = itemId < cent->gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE];
} else {
selectable = false;
}
}
break;
case ST_FORCE_POWER: // force powers
2022-10-16 15:50:32 +00:00
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 - ((cg.itemSelectorType == ST_WEAPON && !vr->in_vehicle) ? 1 : 0))) * 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 )
{
case 0://FORCE_LEVEL_1:
sprite.customShader = cgs.media.HUDSaberStyleFast;
break;
case 1://FORCE_LEVEL_2:
sprite.customShader = cgs.media.HUDSaberStyleMed;
break;
case 2://FORCE_LEVEL_3:
sprite.customShader = cgs.media.HUDSaberStyleStrong;
break;
}
break;
case ST_FORCE_POWER: // force powers
2022-10-16 15:50:32 +00:00
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;
}
//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)
{
2023-04-20 18:55:48 +00:00
/*
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 ? 4 : (vr->right_handed ? 2 : 1);
//Haptics
switch (ent->weapon) {
case WP_SABER:
2022-11-18 21:02:36 +00:00
cgi_HapticEvent("chainsaw_fire", position, 0, 40, 0, 0);
break;
case WP_BRYAR_PISTOL:
case WP_BOWCASTER:
case WP_BLASTER:
2023-01-29 08:36:46 +00:00
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:
2023-01-29 08:36:46 +00:00
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;
}
}
}
/*
=================
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;
}
}