2013-04-19 02:52:48 +00:00
# include "g_local.h"
# include "bg_local.h"
# include "w_saber.h"
# include "ai_main.h"
# include "../ghoul2/G2.h"
# define SABER_BOX_SIZE 16.0f
extern bot_state_t * botstates [ MAX_CLIENTS ] ;
extern qboolean InFront ( vec3_t spot , vec3_t from , vec3_t fromAngles , float threshHold ) ;
extern void G_TestLine ( vec3_t start , vec3_t end , int color , int time ) ;
extern vmCvar_t g_saberRealisticCombat ;
extern vmCvar_t d_saberSPStyleDamage ;
extern vmCvar_t g_debugSaberLocks ;
// nmckenzie: SABER_DAMAGE_WALLS
extern vmCvar_t g_saberWallDamageScale ;
int saberSpinSound = 0 ;
//would be cleaner if these were renamed to BG_ and proto'd in a header.
# include "../namespace_begin.h"
qboolean PM_SaberInTransition ( int move ) ;
qboolean PM_SaberInDeflect ( int move ) ;
qboolean PM_SaberInBrokenParry ( int move ) ;
qboolean PM_SaberInBounce ( int move ) ;
qboolean BG_SaberInReturn ( int move ) ;
qboolean BG_InKnockDownOnGround ( playerState_t * ps ) ;
qboolean BG_StabDownAnim ( int anim ) ;
qboolean BG_SabersOff ( playerState_t * ps ) ;
qboolean BG_SaberInTransitionAny ( int move ) ;
qboolean BG_SaberInAttackPure ( int move ) ;
qboolean WP_SaberBladeUseSecondBladeStyle ( saberInfo_t * saber , int bladeNum ) ;
qboolean WP_SaberBladeDoTransitionDamage ( saberInfo_t * saber , int bladeNum ) ;
# include "../namespace_end.h"
void WP_SaberAddG2Model ( gentity_t * saberent , const char * saberModel , qhandle_t saberSkin ) ;
void WP_SaberRemoveG2Model ( gentity_t * saberent ) ;
float RandFloat ( float min , float max ) {
// return ((rand() * (max - min)) / 32768.0F) + min;
//for linux:
return ( ( rand ( ) * ( max - min ) ) / ( float ) RAND_MAX ) + min ;
}
# ifdef DEBUG_SABER_BOX
void G_DebugBoxLines ( vec3_t mins , vec3_t maxs , int duration )
{
vec3_t start ;
vec3_t end ;
float x = maxs [ 0 ] - mins [ 0 ] ;
float y = maxs [ 1 ] - mins [ 1 ] ;
// top of box
VectorCopy ( maxs , start ) ;
VectorCopy ( maxs , end ) ;
start [ 0 ] - = x ;
G_TestLine ( start , end , 0x00000ff , duration ) ;
end [ 0 ] = start [ 0 ] ;
end [ 1 ] - = y ;
G_TestLine ( start , end , 0x00000ff , duration ) ;
start [ 1 ] = end [ 1 ] ;
start [ 0 ] + = x ;
G_TestLine ( start , end , 0x00000ff , duration ) ;
G_TestLine ( start , maxs , 0x00000ff , duration ) ;
// bottom of box
VectorCopy ( mins , start ) ;
VectorCopy ( mins , end ) ;
start [ 0 ] + = x ;
G_TestLine ( start , end , 0x00000ff , duration ) ;
end [ 0 ] = start [ 0 ] ;
end [ 1 ] + = y ;
G_TestLine ( start , end , 0x00000ff , duration ) ;
start [ 1 ] = end [ 1 ] ;
start [ 0 ] - = x ;
G_TestLine ( start , end , 0x00000ff , duration ) ;
G_TestLine ( start , mins , 0x00000ff , duration ) ;
}
# endif
//general check for performing certain attacks against others
qboolean G_CanBeEnemy ( gentity_t * self , gentity_t * enemy )
{
if ( ! self - > inuse | | ! enemy - > inuse | | ! self - > client | | ! enemy - > client )
{
return qfalse ;
}
if ( self - > client - > ps . duelInProgress & & self - > client - > ps . duelIndex ! = enemy - > s . number )
{ //dueling but not with this person
return qfalse ;
}
if ( enemy - > client - > ps . duelInProgress & & enemy - > client - > ps . duelIndex ! = self - > s . number )
{ //other guy dueling but not with me
return qfalse ;
}
if ( g_gametype . integer < GT_TEAM )
{ //ok, sure
return qtrue ;
}
if ( g_friendlyFire . integer )
{ //if ff on then can inflict damage normally on teammates
return qtrue ;
}
if ( OnSameTeam ( self , enemy ) )
{ //ff not on, don't hurt teammates
return qfalse ;
}
return qtrue ;
}
//This function gets the attack power which is used to decide broken parries,
//knockaways, and numerous other things. It is not directly related to the
//actual amount of damage done, however. -rww
static GAME_INLINE int G_SaberAttackPower ( gentity_t * ent , qboolean attacking )
{
int baseLevel ;
assert ( ent & & ent - > client ) ;
baseLevel = ent - > client - > ps . fd . saberAnimLevel ;
//Give "medium" strength for the two special stances.
if ( baseLevel = = SS_DUAL )
{
baseLevel = 2 ;
}
else if ( baseLevel = = SS_STAFF )
{
baseLevel = 2 ;
}
if ( attacking )
{ //the attacker gets a boost to help penetrate defense.
//General boost up so the individual levels make a bigger difference.
baseLevel * = 2 ;
baseLevel + + ;
//Get the "speed" of the swing, roughly, and add more power
//to the attack based on it.
if ( ent - > client - > lastSaberStorageTime > = ( level . time - 50 ) & &
ent - > client - > olderIsValid )
{
vec3_t vSub ;
int swingDist ;
int toleranceAmt ;
//We want different "tolerance" levels for adding in the distance of the last swing
//to the base power level depending on which stance we are using. Otherwise fast
//would have more advantage than it should since the animations are all much faster.
switch ( ent - > client - > ps . fd . saberAnimLevel )
{
case SS_STRONG :
toleranceAmt = 8 ;
break ;
case SS_MEDIUM :
toleranceAmt = 16 ;
break ;
case SS_FAST :
toleranceAmt = 24 ;
break ;
default : //dual, staff, etc.
toleranceAmt = 16 ;
break ;
}
VectorSubtract ( ent - > client - > lastSaberBase_Always , ent - > client - > olderSaberBase , vSub ) ;
swingDist = ( int ) VectorLength ( vSub ) ;
while ( swingDist > 0 )
{ //I would like to do something more clever. But I suppose this works, at least for now.
baseLevel + + ;
swingDist - = toleranceAmt ;
}
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer > 1 )
{
Com_Printf ( " Client %i: ATT STR: %i \n " , ent - > s . number , baseLevel ) ;
}
# endif
}
if ( ( ent - > client - > ps . brokenLimbs & ( 1 < < BROKENLIMB_RARM ) ) | |
( ent - > client - > ps . brokenLimbs & ( 1 < < BROKENLIMB_LARM ) ) )
{ //We're very weak when one of our arms is broken
baseLevel * = 0.3 ;
}
//Cap at reasonable values now.
if ( baseLevel < 1 )
{
baseLevel = 1 ;
}
else if ( baseLevel > 16 )
{
baseLevel = 16 ;
}
if ( g_gametype . integer = = GT_POWERDUEL & &
ent - > client - > sess . duelTeam = = DUELTEAM_LONE )
{ //get more power then
return baseLevel * 2 ;
}
else if ( attacking & & g_gametype . integer = = GT_SIEGE )
{ //in siege, saber battles should be quicker and more biased toward the attacker
return baseLevel * 3 ;
}
return baseLevel ;
}
void WP_DeactivateSaber ( gentity_t * self , qboolean clearLength )
{
if ( ! self | | ! self - > client )
{
return ;
}
//keep my saber off!
if ( ! self - > client - > ps . saberHolstered )
{
self - > client - > ps . saberHolstered = 2 ;
/*
if ( clearLength )
{
self - > client - > ps . SetSaberLength ( 0 ) ;
}
*/
//Doens't matter ATM
if ( self - > client - > saber [ 0 ] . soundOff )
{
G_Sound ( self , CHAN_WEAPON , self - > client - > saber [ 0 ] . soundOff ) ;
}
if ( self - > client - > saber [ 1 ] . soundOff & &
self - > client - > saber [ 1 ] . model [ 0 ] )
{
G_Sound ( self , CHAN_WEAPON , self - > client - > saber [ 1 ] . soundOff ) ;
}
}
}
void WP_ActivateSaber ( gentity_t * self )
{
if ( ! self | | ! self - > client )
{
return ;
}
if ( self - > NPC & &
self - > client - > ps . forceHandExtend = = HANDEXTEND_JEDITAUNT & &
( self - > client - > ps . forceHandExtendTime - level . time ) > 200 )
{ //if we're an NPC and in the middle of a taunt then stop it
self - > client - > ps . forceHandExtend = HANDEXTEND_NONE ;
self - > client - > ps . forceHandExtendTime = 0 ;
}
else if ( self - > client - > ps . fd . forceGripCripple )
{ //can't activate saber while being gripped
return ;
}
if ( self - > client - > ps . saberHolstered )
{
self - > client - > ps . saberHolstered = 0 ;
if ( self - > client - > saber [ 0 ] . soundOn )
{
G_Sound ( self , CHAN_WEAPON , self - > client - > saber [ 0 ] . soundOn ) ;
}
if ( self - > client - > saber [ 1 ] . soundOn )
{
G_Sound ( self , CHAN_WEAPON , self - > client - > saber [ 1 ] . soundOn ) ;
}
}
}
# define PROPER_THROWN_VALUE 999 //Ah, well..
void SaberUpdateSelf ( gentity_t * ent )
{
if ( ent - > r . ownerNum = = ENTITYNUM_NONE )
{
ent - > think = G_FreeEntity ;
ent - > nextthink = level . time ;
return ;
}
if ( ! g_entities [ ent - > r . ownerNum ] . inuse | |
! g_entities [ ent - > r . ownerNum ] . client /* ||
g_entities [ ent - > r . ownerNum ] . client - > sess . sessionTeam = = TEAM_SPECTATOR */ )
{
ent - > think = G_FreeEntity ;
ent - > nextthink = level . time ;
return ;
}
if ( g_entities [ ent - > r . ownerNum ] . client - > ps . saberInFlight & & g_entities [ ent - > r . ownerNum ] . health > 0 )
{ //let The Master take care of us now (we'll get treated like a missile until we return)
ent - > nextthink = level . time ;
ent - > genericValue5 = PROPER_THROWN_VALUE ;
return ;
}
ent - > genericValue5 = 0 ;
if ( g_entities [ ent - > r . ownerNum ] . client - > ps . weapon ! = WP_SABER | |
( g_entities [ ent - > r . ownerNum ] . client - > ps . pm_flags & PMF_FOLLOW ) | |
//RWW ADDED 7-19-03 BEGIN
g_entities [ ent - > r . ownerNum ] . client - > sess . sessionTeam = = TEAM_SPECTATOR | |
g_entities [ ent - > r . ownerNum ] . client - > tempSpectate > = level . time | |
//RWW ADDED 7-19-03 END
g_entities [ ent - > r . ownerNum ] . health < 1 | |
BG_SabersOff ( & g_entities [ ent - > r . ownerNum ] . client - > ps ) | |
( ! g_entities [ ent - > r . ownerNum ] . client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] & & g_entities [ ent - > r . ownerNum ] . s . eType ! = ET_NPC ) )
{ //owner is not using saber, spectating, dead, saber holstered, or has no attack level
ent - > r . contents = 0 ;
ent - > clipmask = 0 ;
}
else
{ //Standard contents (saber is active)
# ifdef DEBUG_SABER_BOX
if ( g_saberDebugBox . integer = = 1 | | g_saberDebugBox . integer = = 4 )
{
vec3_t dbgMins ;
vec3_t dbgMaxs ;
VectorAdd ( ent - > r . currentOrigin , ent - > r . mins , dbgMins ) ;
VectorAdd ( ent - > r . currentOrigin , ent - > r . maxs , dbgMaxs ) ;
G_DebugBoxLines ( dbgMins , dbgMaxs , ( 10.0f / ( float ) g_svfps . integer ) * 100 ) ;
}
# endif
if ( ent - > r . contents ! = CONTENTS_LIGHTSABER )
{
if ( ( level . time - g_entities [ ent - > r . ownerNum ] . client - > lastSaberStorageTime ) < = 200 )
{ //Only go back to solid once we're sure our owner has updated recently
ent - > r . contents = CONTENTS_LIGHTSABER ;
ent - > clipmask = MASK_PLAYERSOLID | CONTENTS_LIGHTSABER ;
}
}
else
{
ent - > r . contents = CONTENTS_LIGHTSABER ;
ent - > clipmask = MASK_PLAYERSOLID | CONTENTS_LIGHTSABER ;
}
}
trap_LinkEntity ( ent ) ;
ent - > nextthink = level . time ;
}
void SaberGotHit ( gentity_t * self , gentity_t * other , trace_t * trace )
{
gentity_t * own = & g_entities [ self - > r . ownerNum ] ;
if ( ! own | | ! own - > client )
{
return ;
}
//Do something here..? Was handling projectiles here, but instead they're now handled in their own functions.
}
# include "../namespace_begin.h"
qboolean BG_SuperBreakLoseAnim ( int anim ) ;
# include "../namespace_end.h"
static GAME_INLINE void SetSaberBoxSize ( gentity_t * saberent )
{
gentity_t * owner = NULL ;
vec3_t saberOrg , saberTip ;
int i ;
int j = 0 ;
int k = 0 ;
qboolean dualSabers = qfalse ;
qboolean alwaysBlock [ MAX_SABERS ] [ MAX_BLADES ] ;
qboolean forceBlock = qfalse ;
assert ( saberent & & saberent - > inuse ) ;
if ( saberent - > r . ownerNum < MAX_CLIENTS & & saberent - > r . ownerNum > = 0 )
{
owner = & g_entities [ saberent - > r . ownerNum ] ;
}
else if ( saberent - > r . ownerNum > = 0 & & saberent - > r . ownerNum < ENTITYNUM_WORLD & &
g_entities [ saberent - > r . ownerNum ] . s . eType = = ET_NPC )
{
owner = & g_entities [ saberent - > r . ownerNum ] ;
}
if ( ! owner | | ! owner - > inuse | | ! owner - > client )
{
assert ( ! " Saber with no owner? " ) ;
return ;
}
if ( owner - > client - > saber [ 1 ] . model
& & owner - > client - > saber [ 1 ] . model [ 0 ] )
{
dualSabers = qtrue ;
}
if ( PM_SaberInBrokenParry ( owner - > client - > ps . saberMove )
| | BG_SuperBreakLoseAnim ( owner - > client - > ps . torsoAnim ) )
{ //let swings go right through when we're in this state
for ( i = 0 ; i < MAX_SABERS ; i + + )
{
if ( i > 0 & & ! dualSabers )
{ //not using a second saber, set it to not blocking
for ( j = 0 ; j < MAX_BLADES ; j + + )
{
alwaysBlock [ i ] [ j ] = qfalse ;
}
}
else
{
if ( ( owner - > client - > saber [ i ] . saberFlags2 & SFL2_ALWAYS_BLOCK ) )
{
for ( j = 0 ; j < owner - > client - > saber [ i ] . numBlades ; j + + )
{
alwaysBlock [ i ] [ j ] = qtrue ;
forceBlock = qtrue ;
}
}
if ( owner - > client - > saber [ i ] . bladeStyle2Start > 0 )
{
for ( j = owner - > client - > saber [ i ] . bladeStyle2Start ; j < owner - > client - > saber [ i ] . numBlades ; j + + )
{
if ( ( owner - > client - > saber [ i ] . saberFlags2 & SFL2_ALWAYS_BLOCK2 ) )
{
alwaysBlock [ i ] [ j ] = qtrue ;
forceBlock = qtrue ;
}
else
{
alwaysBlock [ i ] [ j ] = qfalse ;
}
}
}
}
}
if ( ! forceBlock )
{ //no sabers/blades to FORCE to be on, so turn off blocking altogether
VectorSet ( saberent - > r . mins , 0 , 0 , 0 ) ;
VectorSet ( saberent - > r . maxs , 0 , 0 , 0 ) ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer > 1 )
{
Com_Printf ( " Client %i in broken parry, saber box 0 \n " , owner - > s . number ) ;
}
# endif
return ;
}
}
if ( ( level . time - owner - > client - > lastSaberStorageTime ) > 200 | |
( level . time - owner - > client - > saber [ j ] . blade [ k ] . storageTime ) > 100 )
{ //it's been too long since we got a reliable point storage, so use the defaults and leave.
VectorSet ( saberent - > r . mins , - SABER_BOX_SIZE , - SABER_BOX_SIZE , - SABER_BOX_SIZE ) ;
VectorSet ( saberent - > r . maxs , SABER_BOX_SIZE , SABER_BOX_SIZE , SABER_BOX_SIZE ) ;
return ;
}
if ( dualSabers
| | owner - > client - > saber [ 0 ] . numBlades > 1 )
{ //dual sabers or multi-blade saber
if ( owner - > client - > ps . saberHolstered > 1 )
{ //entirely off
//no blocking at all
VectorSet ( saberent - > r . mins , 0 , 0 , 0 ) ;
VectorSet ( saberent - > r . maxs , 0 , 0 , 0 ) ;
return ;
}
}
else
{ //single saber
if ( owner - > client - > ps . saberHolstered )
{ //off
//no blocking at all
VectorSet ( saberent - > r . mins , 0 , 0 , 0 ) ;
VectorSet ( saberent - > r . maxs , 0 , 0 , 0 ) ;
return ;
}
}
//Start out at the saber origin, then go through all the blades and push out the extents
//for each blade, then set the box relative to the origin.
VectorCopy ( saberent - > r . currentOrigin , saberent - > r . mins ) ;
VectorCopy ( saberent - > r . currentOrigin , saberent - > r . maxs ) ;
for ( i = 0 ; i < 3 ; i + + )
{
for ( j = 0 ; j < MAX_SABERS ; j + + )
{
if ( ! owner - > client - > saber [ j ] . model [ 0 ] )
{
break ;
}
if ( dualSabers
& & owner - > client - > ps . saberHolstered = = 1
& & j = = 1 )
{ //this mother is holstered, get outta here.
j + + ;
continue ;
}
for ( k = 0 ; k < owner - > client - > saber [ j ] . numBlades ; k + + )
{
if ( k > 0 )
{ //not the first blade
if ( ! dualSabers )
{ //using a single saber
if ( owner - > client - > saber [ j ] . numBlades > 1 )
{ //with multiple blades
if ( owner - > client - > ps . saberHolstered = = 1 )
{ //all blades after the first one are off
break ;
}
}
}
}
if ( forceBlock )
{ //only do blocking with blades that are marked to block
if ( ! alwaysBlock [ j ] [ k ] )
{ //this blade shouldn't be blocking
continue ;
}
}
//VectorMA(owner->client->saber[j].blade[k].muzzlePoint, owner->client->saber[j].blade[k].lengthMax*0.5f, owner->client->saber[j].blade[k].muzzleDir, saberOrg);
VectorCopy ( owner - > client - > saber [ j ] . blade [ k ] . muzzlePoint , saberOrg ) ;
VectorMA ( owner - > client - > saber [ j ] . blade [ k ] . muzzlePoint , owner - > client - > saber [ j ] . blade [ k ] . lengthMax , owner - > client - > saber [ j ] . blade [ k ] . muzzleDir , saberTip ) ;
if ( saberOrg [ i ] < saberent - > r . mins [ i ] )
{
saberent - > r . mins [ i ] = saberOrg [ i ] ;
}
if ( saberTip [ i ] < saberent - > r . mins [ i ] )
{
saberent - > r . mins [ i ] = saberTip [ i ] ;
}
if ( saberOrg [ i ] > saberent - > r . maxs [ i ] )
{
saberent - > r . maxs [ i ] = saberOrg [ i ] ;
}
if ( saberTip [ i ] > saberent - > r . maxs [ i ] )
{
saberent - > r . maxs [ i ] = saberTip [ i ] ;
}
//G_TestLine(saberOrg, saberTip, 0x0000ff, 50);
}
}
}
VectorSubtract ( saberent - > r . mins , saberent - > r . currentOrigin , saberent - > r . mins ) ;
VectorSubtract ( saberent - > r . maxs , saberent - > r . currentOrigin , saberent - > r . maxs ) ;
}
void WP_SaberInitBladeData ( gentity_t * ent )
{
gentity_t * saberent = NULL ;
gentity_t * checkEnt ;
int i = 0 ;
while ( i < level . num_entities )
{ //make sure there are no other saber entities floating around that think they belong to this client.
checkEnt = & g_entities [ i ] ;
if ( checkEnt - > inuse & & checkEnt - > neverFree & &
checkEnt - > r . ownerNum = = ent - > s . number & &
checkEnt - > classname & & checkEnt - > classname [ 0 ] & &
! Q_stricmp ( checkEnt - > classname , " lightsaber " ) )
{
if ( saberent )
{ //already have one
checkEnt - > neverFree = qfalse ;
checkEnt - > think = G_FreeEntity ;
checkEnt - > nextthink = level . time ;
}
else
{ //hmm.. well then, take it as my own.
//free the bitch but don't issue a kg2 to avoid overflowing clients.
checkEnt - > s . modelGhoul2 = 0 ;
G_FreeEntity ( checkEnt ) ;
//now init it manually and reuse this ent slot.
G_InitGentity ( checkEnt ) ;
saberent = checkEnt ;
}
}
i + + ;
}
//We do not want the client to have any real knowledge of the entity whatsoever. It will only
//ever be used on the server.
if ( ! saberent )
{ //ok, make one then
saberent = G_Spawn ( ) ;
}
ent - > client - > ps . saberEntityNum = ent - > client - > saberStoredIndex = saberent - > s . number ;
saberent - > classname = " lightsaber " ;
saberent - > neverFree = qtrue ; //the saber being removed would be a terrible thing.
saberent - > r . svFlags = SVF_USE_CURRENT_ORIGIN ;
saberent - > r . ownerNum = ent - > s . number ;
saberent - > clipmask = MASK_PLAYERSOLID | CONTENTS_LIGHTSABER ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
SetSaberBoxSize ( saberent ) ;
saberent - > mass = 10 ;
saberent - > s . eFlags | = EF_NODRAW ;
saberent - > r . svFlags | = SVF_NOCLIENT ;
saberent - > s . modelGhoul2 = 1 ;
//should we happen to be removed (we belong to an NPC and he is removed) then
//we want to attempt to remove our g2 instance on the client in case we had one.
saberent - > touch = SaberGotHit ;
saberent - > think = SaberUpdateSelf ;
saberent - > genericValue5 = 0 ;
saberent - > nextthink = level . time + 50 ;
saberSpinSound = G_SoundIndex ( " sound/weapons/saber/saberspin.wav " ) ;
}
# define LOOK_DEFAULT_SPEED 0.15f
# define LOOK_TALKING_SPEED 0.15f
static GAME_INLINE qboolean G_CheckLookTarget ( gentity_t * ent , vec3_t lookAngles , float * lookingSpeed )
{
//FIXME: also clamp the lookAngles based on the clamp + the existing difference between
// headAngles and torsoAngles? But often the tag_torso is straight but the torso itself
// is deformed to not face straight... sigh...
if ( ent - > s . eType = = ET_NPC & &
ent - > s . m_iVehicleNum & &
ent - > s . NPC_class ! = CLASS_VEHICLE )
{ //an NPC bolted to a vehicle should just look around randomly
if ( TIMER_Done ( ent , " lookAround " ) )
{
ent - > NPC - > shootAngles [ YAW ] = flrand ( 0 , 360 ) ;
TIMER_Set ( ent , " lookAround " , Q_irand ( 500 , 3000 ) ) ;
}
VectorSet ( lookAngles , 0 , ent - > NPC - > shootAngles [ YAW ] , 0 ) ;
return qtrue ;
}
//Now calc head angle to lookTarget, if any
if ( ent - > client - > renderInfo . lookTarget > = 0 & & ent - > client - > renderInfo . lookTarget < ENTITYNUM_WORLD )
{
vec3_t lookDir , lookOrg , eyeOrg ;
int i ;
if ( ent - > client - > renderInfo . lookMode = = LM_ENT )
{
gentity_t * lookCent = & g_entities [ ent - > client - > renderInfo . lookTarget ] ;
if ( lookCent )
{
if ( lookCent ! = ent - > enemy )
{ //We turn heads faster than headbob speed, but not as fast as if watching an enemy
* lookingSpeed = LOOK_DEFAULT_SPEED ;
}
//FIXME: Ignore small deltas from current angles so we don't bob our head in synch with theirs?
/*
if ( ent - > client - > renderInfo . lookTarget = = 0 & & ! cg . renderingThirdPerson ) //!cg_thirdPerson.integer )
{ //Special case- use cg.refdef.vieworg if looking at player and not in third person view
VectorCopy ( cg . refdef . vieworg , lookOrg ) ;
}
*/ //No no no!
if ( lookCent - > client )
{
VectorCopy ( lookCent - > client - > renderInfo . eyePoint , lookOrg ) ;
}
else if ( lookCent - > inuse & & ! VectorCompare ( lookCent - > r . currentOrigin , vec3_origin ) )
{
VectorCopy ( lookCent - > r . currentOrigin , lookOrg ) ;
}
else
{ //at origin of world
return qfalse ;
}
//Look in dir of lookTarget
}
}
else if ( ent - > client - > renderInfo . lookMode = = LM_INTEREST & & ent - > client - > renderInfo . lookTarget > - 1 & & ent - > client - > renderInfo . lookTarget < MAX_INTEREST_POINTS )
{
VectorCopy ( level . interestPoints [ ent - > client - > renderInfo . lookTarget ] . origin , lookOrg ) ;
}
else
{
return qfalse ;
}
VectorCopy ( ent - > client - > renderInfo . eyePoint , eyeOrg ) ;
VectorSubtract ( lookOrg , eyeOrg , lookDir ) ;
vectoangles ( lookDir , lookAngles ) ;
for ( i = 0 ; i < 3 ; i + + )
{
lookAngles [ i ] = AngleNormalize180 ( lookAngles [ i ] ) ;
ent - > client - > renderInfo . eyeAngles [ i ] = AngleNormalize180 ( ent - > client - > renderInfo . eyeAngles [ i ] ) ;
}
AnglesSubtract ( lookAngles , ent - > client - > renderInfo . eyeAngles , lookAngles ) ;
return qtrue ;
}
return qfalse ;
}
//rww - attempted "port" of the SP version which is completely client-side and
//uses illegal gentity access. I am trying to keep this from being too
//bandwidth-intensive.
//This is primarily droid stuff I guess, I'm going to try to handle all humanoid
//NPC stuff in with the actual player stuff if possible.
void NPC_SetBoneAngles ( gentity_t * ent , char * bone , vec3_t angles ) ;
static GAME_INLINE void G_G2NPCAngles ( gentity_t * ent , vec3_t legs [ 3 ] , vec3_t angles )
{
char * craniumBone = " cranium " ;
char * thoracicBone = " thoracic " ; //only used by atst so doesn't need a case
qboolean looking = qfalse ;
vec3_t viewAngles ;
vec3_t lookAngles ;
if ( ent - > client )
{
if ( ( ent - > client - > NPC_class = = CLASS_PROBE )
| | ( ent - > client - > NPC_class = = CLASS_R2D2 )
| | ( ent - > client - > NPC_class = = CLASS_R5D2 )
| | ( ent - > client - > NPC_class = = CLASS_ATST ) )
{
vec3_t trailingLegsAngles ;
if ( ent - > s . eType = = ET_NPC & &
ent - > s . m_iVehicleNum & &
ent - > s . NPC_class ! = CLASS_VEHICLE )
{ //an NPC bolted to a vehicle should use the full angles
VectorCopy ( ent - > r . currentAngles , angles ) ;
}
else
{
VectorCopy ( ent - > client - > ps . viewangles , angles ) ;
angles [ PITCH ] = 0 ;
}
//FIXME: use actual swing/clamp tolerances?
/*
if ( ent - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE )
{ //on the ground
CG_PlayerLegsYawFromMovement ( cent , ent - > client - > ps . velocity , & angles [ YAW ] , cent - > lerpAngles [ YAW ] , - 60 , 60 , qtrue ) ;
}
else
{ //face legs to front
CG_PlayerLegsYawFromMovement ( cent , vec3_origin , & angles [ YAW ] , cent - > lerpAngles [ YAW ] , - 60 , 60 , qtrue ) ;
}
*/
VectorCopy ( ent - > client - > ps . viewangles , viewAngles ) ;
// viewAngles[YAW] = viewAngles[ROLL] = 0;
viewAngles [ PITCH ] * = 0.5 ;
VectorCopy ( viewAngles , lookAngles ) ;
lookAngles [ 1 ] = 0 ;
if ( ent - > client - > NPC_class = = CLASS_ATST )
{ //body pitch
NPC_SetBoneAngles ( ent , thoracicBone , lookAngles ) ;
//BG_G2SetBoneAngles( cent, ent, ent->thoracicBone, lookAngles, BONE_ANGLES_POSTMULT,POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
}
VectorCopy ( viewAngles , lookAngles ) ;
if ( ent & & ent - > client & & ent - > client - > NPC_class = = CLASS_ATST )
{
//CG_ATSTLegsYaw( cent, trailingLegsAngles );
AnglesToAxis ( trailingLegsAngles , legs ) ;
}
else
{
//FIXME: this needs to properly set the legs.yawing field so we don't erroneously play the turning anim, but we do play it when turning in place
/*
if ( angles [ YAW ] = = cent - > pe . legs . yawAngle )
{
cent - > pe . legs . yawing = qfalse ;
}
else
{
cent - > pe . legs . yawing = qtrue ;
}
cent - > pe . legs . yawAngle = angles [ YAW ] ;
if ( ent - > client )
{
ent - > client - > renderInfo . legsYaw = angles [ YAW ] ;
}
AnglesToAxis ( angles , legs ) ;
*/
}
// if ( ent && ent->client && ent->client->NPC_class == CLASS_ATST )
// {
// looking = qfalse;
// }
// else
{ //look at lookTarget!
//FIXME: snaps to side when lets go of lookTarget... ?
float lookingSpeed = 0.3f ;
looking = G_CheckLookTarget ( ent , lookAngles , & lookingSpeed ) ;
lookAngles [ PITCH ] = lookAngles [ ROLL ] = 0 ; //droids can't pitch or roll their heads
if ( looking )
{ //want to keep doing this lerp behavior for a full second after stopped looking (so don't snap)
ent - > client - > renderInfo . lookingDebounceTime = level . time + 1000 ;
}
}
if ( ent - > client - > renderInfo . lookingDebounceTime > level . time )
{ //adjust for current body orientation
vec3_t oldLookAngles ;
lookAngles [ YAW ] - = 0 ; //ent->client->ps.viewangles[YAW];//cent->pe.torso.yawAngle;
//lookAngles[YAW] -= cent->pe.legs.yawAngle;
//normalize
lookAngles [ YAW ] = AngleNormalize180 ( lookAngles [ YAW ] ) ;
//slowly lerp to this new value
//Remember last headAngles
VectorCopy ( ent - > client - > renderInfo . lastHeadAngles , oldLookAngles ) ;
if ( VectorCompare ( oldLookAngles , lookAngles ) = = qfalse )
{
//FIXME: This clamp goes off viewAngles,
//but really should go off the tag_torso's axis[0] angles, no?
lookAngles [ YAW ] = oldLookAngles [ YAW ] + ( lookAngles [ YAW ] - oldLookAngles [ YAW ] ) * 0.4f ;
}
//Remember current lookAngles next time
VectorCopy ( lookAngles , ent - > client - > renderInfo . lastHeadAngles ) ;
}
else
{ //Remember current lookAngles next time
VectorCopy ( lookAngles , ent - > client - > renderInfo . lastHeadAngles ) ;
}
if ( ent - > client - > NPC_class = = CLASS_ATST )
{
VectorCopy ( ent - > client - > ps . viewangles , lookAngles ) ;
lookAngles [ 0 ] = lookAngles [ 2 ] = 0 ;
lookAngles [ YAW ] - = trailingLegsAngles [ YAW ] ;
}
else
{
lookAngles [ PITCH ] = lookAngles [ ROLL ] = 0 ;
lookAngles [ YAW ] - = ent - > client - > ps . viewangles [ YAW ] ;
}
NPC_SetBoneAngles ( ent , craniumBone , lookAngles ) ;
//BG_G2SetBoneAngles( cent, ent, ent->craniumBone, lookAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
//return;
}
else //if ( (ent->client->NPC_class == CLASS_GONK ) || (ent->client->NPC_class == CLASS_INTERROGATOR) || (ent->client->NPC_class == CLASS_SENTRY) )
{
// VectorCopy( ent->client->ps.viewangles, angles );
// AnglesToAxis( angles, legs );
//return;
}
}
}
static GAME_INLINE void G_G2PlayerAngles ( gentity_t * ent , vec3_t legs [ 3 ] , vec3_t legsAngles )
{
qboolean tPitching = qfalse ,
tYawing = qfalse ,
lYawing = qfalse ;
float tYawAngle = ent - > client - > ps . viewangles [ YAW ] ,
tPitchAngle = 0 ,
lYawAngle = ent - > client - > ps . viewangles [ YAW ] ;
int ciLegs = ent - > client - > ps . legsAnim ;
int ciTorso = ent - > client - > ps . torsoAnim ;
vec3_t turAngles ;
vec3_t lerpOrg , lerpAng ;
if ( ent - > s . eType = = ET_NPC & & ent - > client )
{ //sort of hacky, but it saves a pretty big load off the server
int i = 0 ;
gentity_t * clEnt ;
//If no real clients are in the same PVS then don't do any of this stuff, no one can see him anyway!
while ( i < MAX_CLIENTS )
{
clEnt = & g_entities [ i ] ;
if ( clEnt & & clEnt - > inuse & & clEnt - > client & &
trap_InPVS ( clEnt - > client - > ps . origin , ent - > client - > ps . origin ) )
{ //this client can see him
break ;
}
i + + ;
}
if ( i = = MAX_CLIENTS )
{ //no one can see him, just return
return ;
}
}
VectorCopy ( ent - > client - > ps . origin , lerpOrg ) ;
VectorCopy ( ent - > client - > ps . viewangles , lerpAng ) ;
if ( ent - > localAnimIndex < = 1 )
{ //don't do these things on non-humanoids
vec3_t lookAngles ;
entityState_t * emplaced = NULL ;
if ( ent - > client - > ps . hasLookTarget )
{
VectorSubtract ( g_entities [ ent - > client - > ps . lookTarget ] . r . currentOrigin , ent - > client - > ps . origin , lookAngles ) ;
vectoangles ( lookAngles , lookAngles ) ;
ent - > client - > lookTime = level . time + 1000 ;
}
else
{
VectorCopy ( ent - > client - > ps . origin , lookAngles ) ;
}
lookAngles [ PITCH ] = 0 ;
if ( ent - > client - > ps . emplacedIndex )
{
emplaced = & g_entities [ ent - > client - > ps . emplacedIndex ] . s ;
}
BG_G2PlayerAngles ( ent - > ghoul2 , ent - > client - > renderInfo . motionBolt , & ent - > s , level . time , lerpOrg , lerpAng , legs ,
legsAngles , & tYawing , & tPitching , & lYawing , & tYawAngle , & tPitchAngle , & lYawAngle , FRAMETIME , turAngles ,
ent - > modelScale , ciLegs , ciTorso , & ent - > client - > corrTime , lookAngles , ent - > client - > lastHeadAngles ,
ent - > client - > lookTime , emplaced , NULL ) ;
if ( ent - > client - > ps . heldByClient & & ent - > client - > ps . heldByClient < = MAX_CLIENTS )
{ //then put our arm in this client's hand
//is index+1 because index 0 is valid.
int heldByIndex = ent - > client - > ps . heldByClient - 1 ;
gentity_t * other = & g_entities [ heldByIndex ] ;
int lHandBolt = 0 ;
if ( other & & other - > inuse & & other - > client & & other - > ghoul2 )
{
lHandBolt = trap_G2API_AddBolt ( other - > ghoul2 , 0 , " *l_hand " ) ;
}
else
{ //they left the game, perhaps?
ent - > client - > ps . heldByClient = 0 ;
return ;
}
if ( lHandBolt )
{
mdxaBone_t boltMatrix ;
vec3_t boltOrg ;
vec3_t tAngles ;
VectorCopy ( other - > client - > ps . viewangles , tAngles ) ;
tAngles [ PITCH ] = tAngles [ ROLL ] = 0 ;
trap_G2API_GetBoltMatrix ( other - > ghoul2 , 0 , lHandBolt , & boltMatrix , tAngles , other - > client - > ps . origin , level . time , 0 , other - > modelScale ) ;
boltOrg [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
boltOrg [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
boltOrg [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
BG_IK_MoveArm ( ent - > ghoul2 , lHandBolt , level . time , & ent - > s , ent - > client - > ps . torsoAnim /*BOTH_DEAD1*/ , boltOrg , & ent - > client - > ikStatus ,
ent - > client - > ps . origin , ent - > client - > ps . viewangles , ent - > modelScale , 500 , qfalse ) ;
}
}
else if ( ent - > client - > ikStatus )
{ //make sure we aren't IKing if we don't have anyone to hold onto us.
int lHandBolt = 0 ;
if ( ent & & ent - > inuse & & ent - > client & & ent - > ghoul2 )
{
lHandBolt = trap_G2API_AddBolt ( ent - > ghoul2 , 0 , " *l_hand " ) ;
}
else
{ //This shouldn't happen, but just in case it does, we'll have a failsafe.
ent - > client - > ikStatus = qfalse ;
}
if ( lHandBolt )
{
BG_IK_MoveArm ( ent - > ghoul2 , lHandBolt , level . time , & ent - > s ,
ent - > client - > ps . torsoAnim /*BOTH_DEAD1*/ , vec3_origin , & ent - > client - > ikStatus , ent - > client - > ps . origin , ent - > client - > ps . viewangles , ent - > modelScale , 500 , qtrue ) ;
}
}
}
else if ( ent - > m_pVehicle & & ent - > m_pVehicle - > m_pVehicleInfo - > type = = VH_WALKER )
{
vec3_t lookAngles ;
VectorCopy ( ent - > client - > ps . viewangles , legsAngles ) ;
legsAngles [ PITCH ] = 0 ;
AnglesToAxis ( legsAngles , legs ) ;
VectorCopy ( ent - > client - > ps . viewangles , lookAngles ) ;
lookAngles [ YAW ] = lookAngles [ ROLL ] = 0 ;
BG_G2ATSTAngles ( ent - > ghoul2 , level . time , lookAngles ) ;
}
else if ( ent - > NPC )
{ //an NPC not using a humanoid skeleton, do special angle stuff.
if ( ent - > s . eType = = ET_NPC & &
ent - > s . NPC_class = = CLASS_VEHICLE & &
ent - > m_pVehicle & &
ent - > m_pVehicle - > m_pVehicleInfo - > type = = VH_FIGHTER )
{ //fighters actually want to take pitch and roll into account for the axial angles
VectorCopy ( ent - > client - > ps . viewangles , legsAngles ) ;
AnglesToAxis ( legsAngles , legs ) ;
}
else
{
G_G2NPCAngles ( ent , legs , legsAngles ) ;
}
}
}
static GAME_INLINE qboolean SaberAttacking ( gentity_t * self )
{
if ( PM_SaberInParry ( self - > client - > ps . saberMove ) )
{
return qfalse ;
}
if ( PM_SaberInBrokenParry ( self - > client - > ps . saberMove ) )
{
return qfalse ;
}
if ( PM_SaberInDeflect ( self - > client - > ps . saberMove ) )
{
return qfalse ;
}
if ( PM_SaberInBounce ( self - > client - > ps . saberMove ) )
{
return qfalse ;
}
if ( PM_SaberInKnockaway ( self - > client - > ps . saberMove ) )
{
return qfalse ;
}
if ( BG_SaberInAttack ( self - > client - > ps . saberMove ) )
{
if ( self - > client - > ps . weaponstate = = WEAPON_FIRING & & self - > client - > ps . saberBlocked = = BLOCKED_NONE )
{ //if we're firing and not blocking, then we're attacking.
return qtrue ;
}
}
if ( BG_SaberInSpecial ( self - > client - > ps . saberMove ) )
{
return qtrue ;
}
return qfalse ;
}
typedef enum
{
LOCK_FIRST = 0 ,
LOCK_TOP = LOCK_FIRST ,
LOCK_DIAG_TR ,
LOCK_DIAG_TL ,
LOCK_DIAG_BR ,
LOCK_DIAG_BL ,
LOCK_R ,
LOCK_L ,
LOCK_RANDOM
} sabersLockMode_t ;
# define LOCK_IDEAL_DIST_TOP 32.0f
# define LOCK_IDEAL_DIST_CIRCLE 48.0f
# define SABER_HITDAMAGE 35
void WP_SaberBlockNonRandom ( gentity_t * self , vec3_t hitloc , qboolean missileBlock ) ;
int G_SaberLockAnim ( int attackerSaberStyle , int defenderSaberStyle , int topOrSide , int lockOrBreakOrSuperBreak , int winOrLose )
{
int baseAnim = - 1 ;
if ( lockOrBreakOrSuperBreak = = SABERLOCK_LOCK )
{ //special case: if we're using the same style and locking
if ( attackerSaberStyle = = defenderSaberStyle
| | ( attackerSaberStyle > = SS_FAST & & attackerSaberStyle < = SS_TAVION & & defenderSaberStyle > = SS_FAST & & defenderSaberStyle < = SS_TAVION ) )
{ //using same style
if ( winOrLose = = SABERLOCK_LOSE )
{ //you want the defender's stance...
switch ( defenderSaberStyle )
{
case SS_DUAL :
if ( topOrSide = = SABERLOCK_TOP )
{
baseAnim = BOTH_LK_DL_DL_T_L_2 ;
}
else
{
baseAnim = BOTH_LK_DL_DL_S_L_2 ;
}
break ;
case SS_STAFF :
if ( topOrSide = = SABERLOCK_TOP )
{
baseAnim = BOTH_LK_ST_ST_T_L_2 ;
}
else
{
baseAnim = BOTH_LK_ST_ST_S_L_2 ;
}
break ;
default :
if ( topOrSide = = SABERLOCK_TOP )
{
baseAnim = BOTH_LK_S_S_T_L_2 ;
}
else
{
baseAnim = BOTH_LK_S_S_S_L_2 ;
}
break ;
}
}
}
}
if ( baseAnim = = - 1 )
{
switch ( attackerSaberStyle )
{
case SS_DUAL :
switch ( defenderSaberStyle )
{
case SS_DUAL :
baseAnim = BOTH_LK_DL_DL_S_B_1_L ;
break ;
case SS_STAFF :
baseAnim = BOTH_LK_DL_ST_S_B_1_L ;
break ;
default : //single
baseAnim = BOTH_LK_DL_S_S_B_1_L ;
break ;
}
break ;
case SS_STAFF :
switch ( defenderSaberStyle )
{
case SS_DUAL :
baseAnim = BOTH_LK_ST_DL_S_B_1_L ;
break ;
case SS_STAFF :
baseAnim = BOTH_LK_ST_ST_S_B_1_L ;
break ;
default : //single
baseAnim = BOTH_LK_ST_S_S_B_1_L ;
break ;
}
break ;
default : //single
switch ( defenderSaberStyle )
{
case SS_DUAL :
baseAnim = BOTH_LK_S_DL_S_B_1_L ;
break ;
case SS_STAFF :
baseAnim = BOTH_LK_S_ST_S_B_1_L ;
break ;
default : //single
baseAnim = BOTH_LK_S_S_S_B_1_L ;
break ;
}
break ;
}
//side lock or top lock?
if ( topOrSide = = SABERLOCK_TOP )
{
baseAnim + = 5 ;
}
//lock, break or superbreak?
if ( lockOrBreakOrSuperBreak = = SABERLOCK_LOCK )
{
baseAnim + = 2 ;
}
else
{ //a break or superbreak
if ( lockOrBreakOrSuperBreak = = SABERLOCK_SUPERBREAK )
{
baseAnim + = 3 ;
}
//winner or loser?
if ( winOrLose = = SABERLOCK_WIN )
{
baseAnim + = 1 ;
}
}
}
return baseAnim ;
}
# include "../namespace_begin.h"
extern qboolean BG_CheckIncrementLockAnim ( int anim , int winOrLose ) ; //bg_saber.c
# include "../namespace_end.h"
# define LOCK_IDEAL_DIST_JKA 46.0f //all of the new saberlocks are 46.08 from each other because Richard Lico is da MAN
static GAME_INLINE qboolean WP_SabersCheckLock2 ( gentity_t * attacker , gentity_t * defender , sabersLockMode_t lockMode )
{
int attAnim , defAnim = 0 ;
float attStart = 0.5f , defStart = 0.5f ;
float idealDist = 48.0f ;
vec3_t attAngles , defAngles , defDir ;
vec3_t newOrg ;
vec3_t attDir ;
float diff = 0 ;
trace_t trace ;
//MATCH ANIMS
if ( lockMode = = LOCK_RANDOM )
{
lockMode = ( sabersLockMode_t ) Q_irand ( ( int ) LOCK_FIRST , ( int ) ( LOCK_RANDOM ) - 1 ) ;
}
if ( attacker - > client - > ps . fd . saberAnimLevel > = SS_FAST
& & attacker - > client - > ps . fd . saberAnimLevel < = SS_TAVION
& & defender - > client - > ps . fd . saberAnimLevel > = SS_FAST
& & defender - > client - > ps . fd . saberAnimLevel < = SS_TAVION )
{ //2 single sabers? Just do it the old way...
switch ( lockMode )
{
case LOCK_TOP :
attAnim = BOTH_BF2LOCK ;
defAnim = BOTH_BF1LOCK ;
attStart = defStart = 0.5f ;
idealDist = LOCK_IDEAL_DIST_TOP ;
break ;
case LOCK_DIAG_TR :
attAnim = BOTH_CCWCIRCLELOCK ;
defAnim = BOTH_CWCIRCLELOCK ;
attStart = defStart = 0.5f ;
idealDist = LOCK_IDEAL_DIST_CIRCLE ;
break ;
case LOCK_DIAG_TL :
attAnim = BOTH_CWCIRCLELOCK ;
defAnim = BOTH_CCWCIRCLELOCK ;
attStart = defStart = 0.5f ;
idealDist = LOCK_IDEAL_DIST_CIRCLE ;
break ;
case LOCK_DIAG_BR :
attAnim = BOTH_CWCIRCLELOCK ;
defAnim = BOTH_CCWCIRCLELOCK ;
attStart = defStart = 0.85f ;
idealDist = LOCK_IDEAL_DIST_CIRCLE ;
break ;
case LOCK_DIAG_BL :
attAnim = BOTH_CCWCIRCLELOCK ;
defAnim = BOTH_CWCIRCLELOCK ;
attStart = defStart = 0.85f ;
idealDist = LOCK_IDEAL_DIST_CIRCLE ;
break ;
case LOCK_R :
attAnim = BOTH_CCWCIRCLELOCK ;
defAnim = BOTH_CWCIRCLELOCK ;
attStart = defStart = 0.75f ;
idealDist = LOCK_IDEAL_DIST_CIRCLE ;
break ;
case LOCK_L :
attAnim = BOTH_CWCIRCLELOCK ;
defAnim = BOTH_CCWCIRCLELOCK ;
attStart = defStart = 0.75f ;
idealDist = LOCK_IDEAL_DIST_CIRCLE ;
break ;
default :
return qfalse ;
break ;
}
}
else
{ //use the new system
idealDist = LOCK_IDEAL_DIST_JKA ; //all of the new saberlocks are 46.08 from each other because Richard Lico is da MAN
if ( lockMode = = LOCK_TOP )
{ //top lock
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_TOP , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_TOP , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
attStart = defStart = 0.5f ;
}
else
{ //side lock
switch ( lockMode )
{
case LOCK_DIAG_TR :
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
attStart = defStart = 0.5f ;
break ;
case LOCK_DIAG_TL :
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
attStart = defStart = 0.5f ;
break ;
case LOCK_DIAG_BR :
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
if ( BG_CheckIncrementLockAnim ( attAnim , SABERLOCK_WIN ) )
{
attStart = 0.85f ; //move to end of anim
}
else
{
attStart = 0.15f ; //start at beginning of anim
}
if ( BG_CheckIncrementLockAnim ( defAnim , SABERLOCK_LOSE ) )
{
defStart = 0.85f ; //start at end of anim
}
else
{
defStart = 0.15f ; //start at beginning of anim
}
break ;
case LOCK_DIAG_BL :
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
if ( BG_CheckIncrementLockAnim ( attAnim , SABERLOCK_WIN ) )
{
attStart = 0.85f ; //move to end of anim
}
else
{
attStart = 0.15f ; //start at beginning of anim
}
if ( BG_CheckIncrementLockAnim ( defAnim , SABERLOCK_LOSE ) )
{
defStart = 0.85f ; //start at end of anim
}
else
{
defStart = 0.15f ; //start at beginning of anim
}
break ;
case LOCK_R :
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
if ( BG_CheckIncrementLockAnim ( attAnim , SABERLOCK_WIN ) )
{
attStart = 0.75f ; //move to end of anim
}
else
{
attStart = 0.25f ; //start at beginning of anim
}
if ( BG_CheckIncrementLockAnim ( defAnim , SABERLOCK_LOSE ) )
{
defStart = 0.75f ; //start at end of anim
}
else
{
defStart = 0.25f ; //start at beginning of anim
}
break ;
case LOCK_L :
attAnim = G_SaberLockAnim ( attacker - > client - > ps . fd . saberAnimLevel , defender - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_WIN ) ;
defAnim = G_SaberLockAnim ( defender - > client - > ps . fd . saberAnimLevel , attacker - > client - > ps . fd . saberAnimLevel , SABERLOCK_SIDE , SABERLOCK_LOCK , SABERLOCK_LOSE ) ;
//attacker starts with advantage
if ( BG_CheckIncrementLockAnim ( attAnim , SABERLOCK_WIN ) )
{
attStart = 0.75f ; //move to end of anim
}
else
{
attStart = 0.25f ; //start at beginning of anim
}
if ( BG_CheckIncrementLockAnim ( defAnim , SABERLOCK_LOSE ) )
{
defStart = 0.75f ; //start at end of anim
}
else
{
defStart = 0.25f ; //start at beginning of anim
}
break ;
default :
return qfalse ;
break ;
}
}
}
G_SetAnim ( attacker , NULL , SETANIM_BOTH , attAnim , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
attacker - > client - > ps . saberLockFrame = bgAllAnims [ attacker - > localAnimIndex ] . anims [ attAnim ] . firstFrame + ( bgAllAnims [ attacker - > localAnimIndex ] . anims [ attAnim ] . numFrames * attStart ) ;
G_SetAnim ( defender , NULL , SETANIM_BOTH , defAnim , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
defender - > client - > ps . saberLockFrame = bgAllAnims [ defender - > localAnimIndex ] . anims [ defAnim ] . firstFrame + ( bgAllAnims [ defender - > localAnimIndex ] . anims [ defAnim ] . numFrames * defStart ) ;
attacker - > client - > ps . saberLockHits = 0 ;
defender - > client - > ps . saberLockHits = 0 ;
attacker - > client - > ps . saberLockAdvance = qfalse ;
defender - > client - > ps . saberLockAdvance = qfalse ;
VectorClear ( attacker - > client - > ps . velocity ) ;
VectorClear ( defender - > client - > ps . velocity ) ;
attacker - > client - > ps . saberLockTime = defender - > client - > ps . saberLockTime = level . time + 10000 ;
attacker - > client - > ps . saberLockEnemy = defender - > s . number ;
defender - > client - > ps . saberLockEnemy = attacker - > s . number ;
attacker - > client - > ps . weaponTime = defender - > client - > ps . weaponTime = Q_irand ( 1000 , 3000 ) ; //delay 1 to 3 seconds before pushing
VectorSubtract ( defender - > r . currentOrigin , attacker - > r . currentOrigin , defDir ) ;
VectorCopy ( attacker - > client - > ps . viewangles , attAngles ) ;
attAngles [ YAW ] = vectoyaw ( defDir ) ;
SetClientViewAngle ( attacker , attAngles ) ;
defAngles [ PITCH ] = attAngles [ PITCH ] * - 1 ;
defAngles [ YAW ] = AngleNormalize180 ( attAngles [ YAW ] + 180 ) ;
defAngles [ ROLL ] = 0 ;
SetClientViewAngle ( defender , defAngles ) ;
//MATCH POSITIONS
diff = VectorNormalize ( defDir ) - idealDist ; //diff will be the total error in dist
//try to move attacker half the diff towards the defender
VectorMA ( attacker - > r . currentOrigin , diff * 0.5f , defDir , newOrg ) ;
trap_Trace ( & trace , attacker - > r . currentOrigin , attacker - > r . mins , attacker - > r . maxs , newOrg , attacker - > s . number , attacker - > clipmask ) ;
if ( ! trace . startsolid & & ! trace . allsolid )
{
G_SetOrigin ( attacker , trace . endpos ) ;
if ( attacker - > client )
{
VectorCopy ( trace . endpos , attacker - > client - > ps . origin ) ;
}
trap_LinkEntity ( attacker ) ;
}
//now get the defender's dist and do it for him too
VectorSubtract ( attacker - > r . currentOrigin , defender - > r . currentOrigin , attDir ) ;
diff = VectorNormalize ( attDir ) - idealDist ; //diff will be the total error in dist
//try to move defender all of the remaining diff towards the attacker
VectorMA ( defender - > r . currentOrigin , diff , attDir , newOrg ) ;
trap_Trace ( & trace , defender - > r . currentOrigin , defender - > r . mins , defender - > r . maxs , newOrg , defender - > s . number , defender - > clipmask ) ;
if ( ! trace . startsolid & & ! trace . allsolid )
{
if ( defender - > client )
{
VectorCopy ( trace . endpos , defender - > client - > ps . origin ) ;
}
G_SetOrigin ( defender , trace . endpos ) ;
trap_LinkEntity ( defender ) ;
}
//DONE!
return qtrue ;
}
qboolean WP_SabersCheckLock ( gentity_t * ent1 , gentity_t * ent2 )
{
float dist ;
qboolean ent1BlockingPlayer = qfalse ;
qboolean ent2BlockingPlayer = qfalse ;
if ( g_debugSaberLocks . integer )
{
WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_RANDOM ) ;
return qtrue ;
}
//for now.. it's not fair to the lone duelist.
//we need dual saber lock animations.
if ( g_gametype . integer = = GT_POWERDUEL )
{
return qfalse ;
}
if ( ! g_saberLocking . integer )
{
return qfalse ;
}
if ( ! ent1 - > client | | ! ent2 - > client )
{
return qfalse ;
}
if ( ent1 - > s . eType = = ET_NPC | |
ent2 - > s . eType = = ET_NPC )
{ //if either ents is NPC, then never let an NPC lock with someone on the same playerTeam
if ( ent1 - > client - > playerTeam = = ent2 - > client - > playerTeam )
{
return qfalse ;
}
}
if ( ! ent1 - > client - > ps . saberEntityNum | |
! ent2 - > client - > ps . saberEntityNum | |
ent1 - > client - > ps . saberInFlight | |
ent2 - > client - > ps . saberInFlight )
{ //can't get in lock if one of them has had the saber knocked out of his hand
return qfalse ;
}
if ( ent1 - > s . eType ! = ET_NPC & & ent2 - > s . eType ! = ET_NPC )
{ //can always get into locks with NPCs
if ( ! ent1 - > client - > ps . duelInProgress | |
! ent2 - > client - > ps . duelInProgress | |
ent1 - > client - > ps . duelIndex ! = ent2 - > s . number | |
ent2 - > client - > ps . duelIndex ! = ent1 - > s . number )
{ //only allow saber locking if two players are dueling with each other directly
if ( g_gametype . integer ! = GT_DUEL & & g_gametype . integer ! = GT_POWERDUEL )
{
return qfalse ;
}
}
}
if ( fabs ( ent1 - > r . currentOrigin [ 2 ] - ent2 - > r . currentOrigin [ 2 ] ) > 16 )
{
return qfalse ;
}
if ( ent1 - > client - > ps . groundEntityNum = = ENTITYNUM_NONE | |
ent2 - > client - > ps . groundEntityNum = = ENTITYNUM_NONE )
{
return qfalse ;
}
dist = DistanceSquared ( ent1 - > r . currentOrigin , ent2 - > r . currentOrigin ) ;
if ( dist < 64 | | dist > 6400 )
{ //between 8 and 80 from each other
return qfalse ;
}
if ( BG_InSpecialJump ( ent1 - > client - > ps . legsAnim ) )
{
return qfalse ;
}
if ( BG_InSpecialJump ( ent2 - > client - > ps . legsAnim ) )
{
return qfalse ;
}
if ( BG_InRoll ( & ent1 - > client - > ps , ent1 - > client - > ps . legsAnim ) )
{
return qfalse ;
}
if ( BG_InRoll ( & ent2 - > client - > ps , ent2 - > client - > ps . legsAnim ) )
{
return qfalse ;
}
if ( ent1 - > client - > ps . forceHandExtend ! = HANDEXTEND_NONE | |
ent2 - > client - > ps . forceHandExtend ! = HANDEXTEND_NONE )
{
return qfalse ;
}
if ( ( ent1 - > client - > ps . pm_flags & PMF_DUCKED ) | |
( ent2 - > client - > ps . pm_flags & PMF_DUCKED ) )
{
return qfalse ;
}
if ( ( ent1 - > client - > saber [ 0 ] . saberFlags & SFL_NOT_LOCKABLE )
| | ( ent2 - > client - > saber [ 0 ] . saberFlags & SFL_NOT_LOCKABLE ) )
{
return qfalse ;
}
if ( ent1 - > client - > saber [ 1 ] . model
& & ent1 - > client - > saber [ 1 ] . model [ 0 ]
& & ! ent1 - > client - > ps . saberHolstered
& & ( ent1 - > client - > saber [ 1 ] . saberFlags & SFL_NOT_LOCKABLE ) )
{
return qfalse ;
}
if ( ent2 - > client - > saber [ 1 ] . model
& & ent2 - > client - > saber [ 1 ] . model [ 0 ]
& & ! ent2 - > client - > ps . saberHolstered
& & ( ent2 - > client - > saber [ 1 ] . saberFlags & SFL_NOT_LOCKABLE ) )
{
return qfalse ;
}
if ( ! InFront ( ent1 - > client - > ps . origin , ent2 - > client - > ps . origin , ent2 - > client - > ps . viewangles , 0.4f ) )
{
return qfalse ;
}
if ( ! InFront ( ent2 - > client - > ps . origin , ent1 - > client - > ps . origin , ent1 - > client - > ps . viewangles , 0.4f ) )
{
return qfalse ;
}
//T to B lock
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_T__B_ | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_T__B_ | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_T__B_ | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_T__B_ | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_T__B_ | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_T__B_ | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_T__B_ )
{ //ent1 is attacking top-down
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_TOP ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_T__B_ | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_T__B_ | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_T__B_ | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_T__B_ | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_T__B_ | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_T__B_ | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_T__B_ )
{ //ent2 is attacking top-down
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_TOP ) ;
}
if ( ent1 - > s . number = = 0 & &
ent1 - > client - > ps . saberBlocking = = BLK_WIDE & & ent1 - > client - > ps . weaponTime < = 0 )
{
ent1BlockingPlayer = qtrue ;
}
if ( ent2 - > s . number = = 0 & &
ent2 - > client - > ps . saberBlocking = = BLK_WIDE & & ent2 - > client - > ps . weaponTime < = 0 )
{
ent2BlockingPlayer = qtrue ;
}
//TR to BL lock
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_TR_BL )
{ //ent1 is attacking diagonally
if ( ent2BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_DIAG_TR ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_TL )
{ //ent2 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_DIAG_TR ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_BR_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_BL )
{ //ent2 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_DIAG_BL ) ;
}
return qfalse ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_TR_BL )
{ //ent2 is attacking diagonally
if ( ent1BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_DIAG_TR ) ;
}
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_TL )
{ //ent1 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_DIAG_TR ) ;
}
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_BR_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_BL )
{ //ent1 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_DIAG_BL ) ;
}
return qfalse ;
}
//TL to BR lock
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_TL_BR )
{ //ent1 is attacking diagonally
if ( ent2BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_DIAG_TL ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_TR )
{ //ent2 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_DIAG_TL ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_BL_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_BR )
{ //ent2 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_DIAG_BR ) ;
}
return qfalse ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_TL_BR )
{ //ent2 is attacking diagonally
if ( ent1BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_DIAG_TL ) ;
}
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_TR )
{ //ent1 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_DIAG_TL ) ;
}
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_BL_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_BR )
{ //ent1 is attacking in the opposite diagonal
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_DIAG_BR ) ;
}
return qfalse ;
}
//L to R lock
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1__L__R | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2__L__R | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3__L__R | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4__L__R | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5__L__R | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6__L__R | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7__L__R )
{ //ent1 is attacking l to r
if ( ent2BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_L ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_TL_BR | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_TR | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_BL )
{ //ent2 is attacking or blocking on the r
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_L ) ;
}
return qfalse ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1__L__R | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2__L__R | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3__L__R | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4__L__R | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5__L__R | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6__L__R | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7__L__R )
{ //ent2 is attacking l to r
if ( ent1BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_L ) ;
}
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_TL_BR | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_TR | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_BL )
{ //ent1 is attacking or blocking on the r
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_L ) ;
}
return qfalse ;
}
//R to L lock
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1__R__L | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2__R__L | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3__R__L | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4__R__L | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5__R__L | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6__R__L | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7__R__L )
{ //ent1 is attacking r to l
if ( ent2BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_R ) ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7_TR_BL | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_TL | |
ent2 - > client - > ps . torsoAnim = = BOTH_P1_S1_BR )
{ //ent2 is attacking or blocking on the l
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_R ) ;
}
return qfalse ;
}
if ( ent2 - > client - > ps . torsoAnim = = BOTH_A1__R__L | |
ent2 - > client - > ps . torsoAnim = = BOTH_A2__R__L | |
ent2 - > client - > ps . torsoAnim = = BOTH_A3__R__L | |
ent2 - > client - > ps . torsoAnim = = BOTH_A4__R__L | |
ent2 - > client - > ps . torsoAnim = = BOTH_A5__R__L | |
ent2 - > client - > ps . torsoAnim = = BOTH_A6__R__L | |
ent2 - > client - > ps . torsoAnim = = BOTH_A7__R__L )
{ //ent2 is attacking r to l
if ( ent1BlockingPlayer )
{ //player will block this anyway
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_R ) ;
}
if ( ent1 - > client - > ps . torsoAnim = = BOTH_A1_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A2_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A3_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A4_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A5_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A6_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_A7_TR_BL | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_TL | |
ent1 - > client - > ps . torsoAnim = = BOTH_P1_S1_BR )
{ //ent1 is attacking or blocking on the l
return WP_SabersCheckLock2 ( ent2 , ent1 , LOCK_R ) ;
}
return qfalse ;
}
if ( ! Q_irand ( 0 , 10 ) )
{
return WP_SabersCheckLock2 ( ent1 , ent2 , LOCK_RANDOM ) ;
}
return qfalse ;
}
static GAME_INLINE int G_GetParryForBlock ( int block )
{
switch ( block )
{
case BLOCKED_UPPER_RIGHT :
return LS_PARRY_UR ;
break ;
case BLOCKED_UPPER_RIGHT_PROJ :
return LS_REFLECT_UR ;
break ;
case BLOCKED_UPPER_LEFT :
return LS_PARRY_UL ;
break ;
case BLOCKED_UPPER_LEFT_PROJ :
return LS_REFLECT_UL ;
break ;
case BLOCKED_LOWER_RIGHT :
return LS_PARRY_LR ;
break ;
case BLOCKED_LOWER_RIGHT_PROJ :
return LS_REFLECT_LR ;
break ;
case BLOCKED_LOWER_LEFT :
return LS_PARRY_LL ;
break ;
case BLOCKED_LOWER_LEFT_PROJ :
return LS_REFLECT_LL ;
break ;
case BLOCKED_TOP :
return LS_PARRY_UP ;
break ;
case BLOCKED_TOP_PROJ :
return LS_REFLECT_UP ;
break ;
default :
break ;
}
return LS_NONE ;
}
# include "../namespace_begin.h"
int PM_SaberBounceForAttack ( int move ) ;
int PM_SaberDeflectionForQuad ( int quad ) ;
# include "../namespace_end.h"
extern stringID_table_t animTable [ MAX_ANIMATIONS + 1 ] ;
static GAME_INLINE qboolean WP_GetSaberDeflectionAngle ( gentity_t * attacker , gentity_t * defender , float saberHitFraction )
{
qboolean animBasedDeflection = qtrue ;
int attSaberLevel , defSaberLevel ;
if ( ! attacker | | ! attacker - > client | | ! attacker - > ghoul2 )
{
return qfalse ;
}
if ( ! defender | | ! defender - > client | | ! defender - > ghoul2 )
{
return qfalse ;
}
if ( ( level . time - attacker - > client - > lastSaberStorageTime ) > 500 )
{ //last update was too long ago, something is happening to this client to prevent his saber from updating
return qfalse ;
}
if ( ( level . time - defender - > client - > lastSaberStorageTime ) > 500 )
{ //ditto
return qfalse ;
}
attSaberLevel = G_SaberAttackPower ( attacker , SaberAttacking ( attacker ) ) ;
defSaberLevel = G_SaberAttackPower ( defender , SaberAttacking ( defender ) ) ;
if ( animBasedDeflection )
{
//Hmm, let's try just basing it off the anim
int attQuadStart = saberMoveData [ attacker - > client - > ps . saberMove ] . startQuad ;
int attQuadEnd = saberMoveData [ attacker - > client - > ps . saberMove ] . endQuad ;
int defQuad = saberMoveData [ defender - > client - > ps . saberMove ] . endQuad ;
int quadDiff = fabs ( ( float ) ( defQuad - attQuadStart ) ) ;
if ( defender - > client - > ps . saberMove = = LS_READY )
{
//FIXME: we should probably do SOMETHING here...
//I have this return qfalse here in the hopes that
//the defender will pick a parry and the attacker
//will hit the defender's saber again.
//But maybe this func call should come *after*
//it's decided whether or not the defender is
//going to parry.
return qfalse ;
}
//reverse the left/right of the defQuad because of the mirrored nature of facing each other in combat
switch ( defQuad )
{
case Q_BR :
defQuad = Q_BL ;
break ;
case Q_R :
defQuad = Q_L ;
break ;
case Q_TR :
defQuad = Q_TL ;
break ;
case Q_TL :
defQuad = Q_TR ;
break ;
case Q_L :
defQuad = Q_R ;
break ;
case Q_BL :
defQuad = Q_BR ;
break ;
}
if ( quadDiff > 4 )
{ //wrap around so diff is never greater than 180 (4 * 45)
quadDiff = 4 - ( quadDiff - 4 ) ;
}
//have the quads, find a good anim to use
if ( ( ! quadDiff | | ( quadDiff = = 1 & & Q_irand ( 0 , 1 ) ) ) //defender pretty much stopped the attack at a 90 degree angle
& & ( defSaberLevel = = attSaberLevel | | Q_irand ( 0 , defSaberLevel - attSaberLevel ) > = 0 ) ) //and the defender's style is stronger
{
//bounce straight back
# ifndef FINAL_BUILD
int attMove = attacker - > client - > ps . saberMove ;
# endif
attacker - > client - > ps . saberMove = PM_SaberBounceForAttack ( attacker - > client - > ps . saberMove ) ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " attack %s vs. parry %s bounced to %s \n " ,
animTable [ saberMoveData [ attMove ] . animToUse ] . name ,
animTable [ saberMoveData [ defender - > client - > ps . saberMove ] . animToUse ] . name ,
animTable [ saberMoveData [ attacker - > client - > ps . saberMove ] . animToUse ] . name ) ;
}
# endif
attacker - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
return qfalse ;
}
else
{ //attack hit at an angle, figure out what angle it should bounce off att
int newQuad ;
quadDiff = defQuad - attQuadEnd ;
//add half the diff of between the defense and attack end to the attack end
if ( quadDiff > 4 )
{
quadDiff = 4 - ( quadDiff - 4 ) ;
}
else if ( quadDiff < - 4 )
{
quadDiff = - 4 + ( quadDiff + 4 ) ;
}
newQuad = attQuadEnd + ceil ( ( ( float ) quadDiff ) / 2.0f ) ;
if ( newQuad < Q_BR )
{ //less than zero wraps around
newQuad = Q_B + newQuad ;
}
if ( newQuad = = attQuadStart )
{ //never come off at the same angle that we would have if the attack was not interrupted
if ( Q_irand ( 0 , 1 ) )
{
newQuad - - ;
}
else
{
newQuad + + ;
}
if ( newQuad < Q_BR )
{
newQuad = Q_B ;
}
else if ( newQuad > Q_B )
{
newQuad = Q_BR ;
}
}
if ( newQuad = = defQuad )
{ //bounce straight back
# ifndef FINAL_BUILD
int attMove = attacker - > client - > ps . saberMove ;
# endif
attacker - > client - > ps . saberMove = PM_SaberBounceForAttack ( attacker - > client - > ps . saberMove ) ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " attack %s vs. parry %s bounced to %s \n " ,
animTable [ saberMoveData [ attMove ] . animToUse ] . name ,
animTable [ saberMoveData [ defender - > client - > ps . saberMove ] . animToUse ] . name ,
animTable [ saberMoveData [ attacker - > client - > ps . saberMove ] . animToUse ] . name ) ;
}
# endif
attacker - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
return qfalse ;
}
//else, pick a deflection
else
{
# ifndef FINAL_BUILD
int attMove = attacker - > client - > ps . saberMove ;
# endif
attacker - > client - > ps . saberMove = PM_SaberDeflectionForQuad ( newQuad ) ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " attack %s vs. parry %s deflected to %s \n " ,
animTable [ saberMoveData [ attMove ] . animToUse ] . name ,
animTable [ saberMoveData [ defender - > client - > ps . saberMove ] . animToUse ] . name ,
animTable [ saberMoveData [ attacker - > client - > ps . saberMove ] . animToUse ] . name ) ;
}
# endif
attacker - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
return qtrue ;
}
}
}
else
{ //old math-based method (probably broken)
vec3_t att_HitDir , def_BladeDir , temp ;
float hitDot ;
VectorCopy ( attacker - > client - > lastSaberBase_Always , temp ) ;
AngleVectors ( attacker - > client - > lastSaberDir_Always , att_HitDir , 0 , 0 ) ;
AngleVectors ( defender - > client - > lastSaberDir_Always , def_BladeDir , 0 , 0 ) ;
//now compare
hitDot = DotProduct ( att_HitDir , def_BladeDir ) ;
if ( hitDot < 0.25f & & hitDot > - 0.25f )
{ //hit pretty much perpendicular, pop straight back
attacker - > client - > ps . saberMove = PM_SaberBounceForAttack ( attacker - > client - > ps . saberMove ) ;
attacker - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
return qfalse ;
}
else
{ //a deflection
vec3_t att_Right , att_Up , att_DeflectionDir ;
float swingRDot , swingUDot ;
//get the direction of the deflection
VectorScale ( def_BladeDir , hitDot , att_DeflectionDir ) ;
//get our bounce straight back direction
VectorScale ( att_HitDir , - 1.0f , temp ) ;
//add the bounce back and deflection
VectorAdd ( att_DeflectionDir , temp , att_DeflectionDir ) ;
//normalize the result to determine what direction our saber should bounce back toward
VectorNormalize ( att_DeflectionDir ) ;
//need to know the direction of the deflectoin relative to the attacker's facing
VectorSet ( temp , 0 , attacker - > client - > ps . viewangles [ YAW ] , 0 ) ; //presumes no pitch!
AngleVectors ( temp , NULL , att_Right , att_Up ) ;
swingRDot = DotProduct ( att_Right , att_DeflectionDir ) ;
swingUDot = DotProduct ( att_Up , att_DeflectionDir ) ;
if ( swingRDot > 0.25f )
{ //deflect to right
if ( swingUDot > 0.25f )
{ //deflect to top
attacker - > client - > ps . saberMove = LS_D1_TR ;
}
else if ( swingUDot < - 0.25f )
{ //deflect to bottom
attacker - > client - > ps . saberMove = LS_D1_BR ;
}
else
{ //deflect horizontally
attacker - > client - > ps . saberMove = LS_D1__R ;
}
}
else if ( swingRDot < - 0.25f )
{ //deflect to left
if ( swingUDot > 0.25f )
{ //deflect to top
attacker - > client - > ps . saberMove = LS_D1_TL ;
}
else if ( swingUDot < - 0.25f )
{ //deflect to bottom
attacker - > client - > ps . saberMove = LS_D1_BL ;
}
else
{ //deflect horizontally
attacker - > client - > ps . saberMove = LS_D1__L ;
}
}
else
{ //deflect in middle
if ( swingUDot > 0.25f )
{ //deflect to top
attacker - > client - > ps . saberMove = LS_D1_T_ ;
}
else if ( swingUDot < - 0.25f )
{ //deflect to bottom
attacker - > client - > ps . saberMove = LS_D1_B_ ;
}
else
{ //deflect horizontally? Well, no such thing as straight back in my face, so use top
if ( swingRDot > 0 )
{
attacker - > client - > ps . saberMove = LS_D1_TR ;
}
else if ( swingRDot < 0 )
{
attacker - > client - > ps . saberMove = LS_D1_TL ;
}
else
{
attacker - > client - > ps . saberMove = LS_D1_T_ ;
}
}
}
attacker - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
return qtrue ;
}
}
}
int G_KnockawayForParry ( int move )
{
//FIXME: need actual anims for this
//FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center
switch ( move )
{
case LS_PARRY_UP :
return LS_K1_T_ ; //push up
break ;
case LS_PARRY_UR :
default : //case LS_READY:
return LS_K1_TR ; //push up, slightly to right
break ;
case LS_PARRY_UL :
return LS_K1_TL ; //push up and to left
break ;
case LS_PARRY_LR :
return LS_K1_BR ; //push down and to left
break ;
case LS_PARRY_LL :
return LS_K1_BL ; //push down and to right
break ;
}
}
# define SABER_NONATTACK_DAMAGE 1
//For strong attacks, we ramp damage based on the point in the attack animation
static GAME_INLINE int G_GetAttackDamage ( gentity_t * self , int minDmg , int maxDmg , float multPoint )
{
int peakDif = 0 ;
int speedDif = 0 ;
int totalDamage = maxDmg ;
float peakPoint = 0 ;
float attackAnimLength = bgAllAnims [ self - > localAnimIndex ] . anims [ self - > client - > ps . torsoAnim ] . numFrames * fabs ( ( float ) ( bgAllAnims [ self - > localAnimIndex ] . anims [ self - > client - > ps . torsoAnim ] . frameLerp ) ) ;
float currentPoint = 0 ;
float damageFactor = 0 ;
float animSpeedFactor = 1.0f ;
//Be sure to scale by the proper anim speed just as if we were going to play the animation
BG_SaberStartTransAnim ( self - > s . number , self - > client - > ps . fd . saberAnimLevel , self - > client - > ps . weapon , self - > client - > ps . torsoAnim , & animSpeedFactor , self - > client - > ps . brokenLimbs ) ;
speedDif = attackAnimLength - ( attackAnimLength * animSpeedFactor ) ;
attackAnimLength + = speedDif ;
peakPoint = attackAnimLength ;
peakPoint - = attackAnimLength * multPoint ;
//we treat torsoTimer as the point in the animation (closer it is to attackAnimLength, closer it is to beginning)
currentPoint = self - > client - > ps . torsoTimer ;
if ( peakPoint > currentPoint )
{
peakDif = ( peakPoint - currentPoint ) ;
}
else
{
peakDif = ( currentPoint - peakPoint ) ;
}
damageFactor = ( float ) ( ( currentPoint / peakPoint ) ) ;
if ( damageFactor > 1 )
{
damageFactor = ( 2.0f - damageFactor ) ;
}
totalDamage * = damageFactor ;
if ( totalDamage < minDmg )
{
totalDamage = minDmg ;
}
if ( totalDamage > maxDmg )
{
totalDamage = maxDmg ;
}
//Com_Printf("%i\n", totalDamage);
return totalDamage ;
}
//Get the point in the animation and return a percentage of the current point in the anim between 0 and the total anim length (0.0f - 1.0f)
static GAME_INLINE float G_GetAnimPoint ( gentity_t * self )
{
int speedDif = 0 ;
float attackAnimLength = bgAllAnims [ self - > localAnimIndex ] . anims [ self - > client - > ps . torsoAnim ] . numFrames * fabs ( ( float ) ( bgAllAnims [ self - > localAnimIndex ] . anims [ self - > client - > ps . torsoAnim ] . frameLerp ) ) ;
float currentPoint = 0 ;
float animSpeedFactor = 1.0f ;
float animPercentage = 0 ;
//Be sure to scale by the proper anim speed just as if we were going to play the animation
BG_SaberStartTransAnim ( self - > s . number , self - > client - > ps . fd . saberAnimLevel , self - > client - > ps . weapon , self - > client - > ps . torsoAnim , & animSpeedFactor , self - > client - > ps . brokenLimbs ) ;
speedDif = attackAnimLength - ( attackAnimLength * animSpeedFactor ) ;
attackAnimLength + = speedDif ;
currentPoint = self - > client - > ps . torsoTimer ;
animPercentage = currentPoint / attackAnimLength ;
//Com_Printf("%f\n", animPercentage);
return animPercentage ;
}
static GAME_INLINE qboolean G_ClientIdleInWorld ( gentity_t * ent )
{
if ( ent - > s . eType = = ET_NPC )
{
return qfalse ;
}
if ( ! ent - > client - > pers . cmd . upmove & &
! ent - > client - > pers . cmd . forwardmove & &
! ent - > client - > pers . cmd . rightmove & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_GESTURE ) & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_FORCEGRIP ) & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_ALT_ATTACK ) & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_FORCEPOWER ) & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_FORCE_LIGHTNING ) & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_FORCE_DRAIN ) & &
! ( ent - > client - > pers . cmd . buttons & BUTTON_ATTACK ) )
{
return qtrue ;
}
return qfalse ;
}
static GAME_INLINE qboolean G_G2TraceCollide ( trace_t * tr , vec3_t lastValidStart , vec3_t lastValidEnd , vec3_t traceMins , vec3_t traceMaxs )
{ //Hit the ent with the normal trace, try the collision trace.
G2Trace_t G2Trace ;
gentity_t * g2Hit ;
vec3_t angles ;
int tN = 0 ;
float fRadius = 0 ;
if ( ! d_saberGhoul2Collision . integer )
{
return qfalse ;
}
if ( ! g_entities [ tr - > entityNum ] . inuse /*||
( g_entities [ tr - > entityNum ] . s . eFlags & EF_DEAD ) */ )
{ //don't do perpoly on corpses.
return qfalse ;
}
if ( traceMins [ 0 ] | |
traceMins [ 1 ] | |
traceMins [ 2 ] | |
traceMaxs [ 0 ] | |
traceMaxs [ 1 ] | |
traceMaxs [ 2 ] )
{
fRadius = ( traceMaxs [ 0 ] - traceMins [ 0 ] ) / 2.0f ;
}
memset ( & G2Trace , 0 , sizeof ( G2Trace ) ) ;
while ( tN < MAX_G2_COLLISIONS )
{
G2Trace [ tN ] . mEntityNum = - 1 ;
tN + + ;
}
g2Hit = & g_entities [ tr - > entityNum ] ;
if ( g2Hit & & g2Hit - > inuse & & g2Hit - > ghoul2 )
{
vec3_t g2HitOrigin ;
angles [ ROLL ] = angles [ PITCH ] = 0 ;
if ( g2Hit - > client )
{
VectorCopy ( g2Hit - > client - > ps . origin , g2HitOrigin ) ;
angles [ YAW ] = g2Hit - > client - > ps . viewangles [ YAW ] ;
}
else
{
VectorCopy ( g2Hit - > r . currentOrigin , g2HitOrigin ) ;
angles [ YAW ] = g2Hit - > r . currentAngles [ YAW ] ;
}
if ( g_optvehtrace . integer & &
g2Hit - > s . eType = = ET_NPC & &
g2Hit - > s . NPC_class = = CLASS_VEHICLE & &
g2Hit - > m_pVehicle )
{
trap_G2API_CollisionDetectCache ( G2Trace , g2Hit - > ghoul2 , angles , g2HitOrigin , level . time , g2Hit - > s . number , lastValidStart , lastValidEnd , g2Hit - > modelScale , 0 , g_g2TraceLod . integer , fRadius ) ;
}
else
{
trap_G2API_CollisionDetect ( G2Trace , g2Hit - > ghoul2 , angles , g2HitOrigin , level . time , g2Hit - > s . number , lastValidStart , lastValidEnd , g2Hit - > modelScale , 0 , g_g2TraceLod . integer , fRadius ) ;
}
if ( G2Trace [ 0 ] . mEntityNum ! = g2Hit - > s . number )
{
tr - > fraction = 1.0f ;
tr - > entityNum = ENTITYNUM_NONE ;
tr - > startsolid = 0 ;
tr - > allsolid = 0 ;
return qfalse ;
}
else
{ //The ghoul2 trace result matches, so copy the collision position into the trace endpos and send it back.
VectorCopy ( G2Trace [ 0 ] . mCollisionPosition , tr - > endpos ) ;
VectorCopy ( G2Trace [ 0 ] . mCollisionNormal , tr - > plane . normal ) ;
if ( g2Hit - > client )
{
g2Hit - > client - > g2LastSurfaceHit = G2Trace [ 0 ] . mSurfaceIndex ;
g2Hit - > client - > g2LastSurfaceTime = level . time ;
}
return qtrue ;
}
}
return qfalse ;
}
static GAME_INLINE qboolean G_SaberInBackAttack ( int move )
{
switch ( move )
{
case LS_A_BACK :
case LS_A_BACK_CR :
case LS_A_BACKSTAB :
return qtrue ;
}
return qfalse ;
}
qboolean saberCheckKnockdown_Thrown ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other ) ;
qboolean saberCheckKnockdown_Smashed ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other , int damage ) ;
qboolean saberCheckKnockdown_BrokenParry ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other ) ;
typedef struct saberFace_s
{
vec3_t v1 ;
vec3_t v2 ;
vec3_t v3 ;
} saberFace_t ;
//build faces around blade for collision checking -rww
static GAME_INLINE void G_BuildSaberFaces ( vec3_t base , vec3_t tip , float radius , vec3_t fwd ,
vec3_t right , int * fNum , saberFace_t * * fList )
{
static saberFace_t faces [ 12 ] ;
int i = 0 ;
float * d1 = NULL , * d2 = NULL ;
vec3_t invFwd ;
vec3_t invRight ;
VectorCopy ( fwd , invFwd ) ;
VectorInverse ( invFwd ) ;
VectorCopy ( right , invRight ) ;
VectorInverse ( invRight ) ;
while ( i < 8 )
{
//yeah, this part is kind of a hack, but eh
if ( i < 2 )
{ //"left" surface
d1 = & fwd [ 0 ] ;
d2 = & invRight [ 0 ] ;
}
else if ( i < 4 )
{ //"right" surface
d1 = & fwd [ 0 ] ;
d2 = & right [ 0 ] ;
}
else if ( i < 6 )
{ //"front" surface
d1 = & right [ 0 ] ;
d2 = & fwd [ 0 ] ;
}
else if ( i < 8 )
{ //"back" surface
d1 = & right [ 0 ] ;
d2 = & invFwd [ 0 ] ;
}
//first triangle for this surface
VectorMA ( base , radius / 2.0f , d1 , faces [ i ] . v1 ) ;
VectorMA ( faces [ i ] . v1 , radius / 2.0f , d2 , faces [ i ] . v1 ) ;
VectorMA ( tip , radius / 2.0f , d1 , faces [ i ] . v2 ) ;
VectorMA ( faces [ i ] . v2 , radius / 2.0f , d2 , faces [ i ] . v2 ) ;
VectorMA ( tip , - radius / 2.0f , d1 , faces [ i ] . v3 ) ;
VectorMA ( faces [ i ] . v3 , radius / 2.0f , d2 , faces [ i ] . v3 ) ;
i + + ;
//second triangle for this surface
VectorMA ( tip , - radius / 2.0f , d1 , faces [ i ] . v1 ) ;
VectorMA ( faces [ i ] . v1 , radius / 2.0f , d2 , faces [ i ] . v1 ) ;
VectorMA ( base , radius / 2.0f , d1 , faces [ i ] . v2 ) ;
VectorMA ( faces [ i ] . v2 , radius / 2.0f , d2 , faces [ i ] . v2 ) ;
VectorMA ( base , - radius / 2.0f , d1 , faces [ i ] . v3 ) ;
VectorMA ( faces [ i ] . v3 , radius / 2.0f , d2 , faces [ i ] . v3 ) ;
i + + ;
}
//top surface
//face 1
VectorMA ( tip , radius / 2.0f , fwd , faces [ i ] . v1 ) ;
VectorMA ( faces [ i ] . v1 , - radius / 2.0f , right , faces [ i ] . v1 ) ;
VectorMA ( tip , radius / 2.0f , fwd , faces [ i ] . v2 ) ;
VectorMA ( faces [ i ] . v2 , radius / 2.0f , right , faces [ i ] . v2 ) ;
VectorMA ( tip , - radius / 2.0f , fwd , faces [ i ] . v3 ) ;
VectorMA ( faces [ i ] . v3 , - radius / 2.0f , right , faces [ i ] . v3 ) ;
i + + ;
//face 2
VectorMA ( tip , radius / 2.0f , fwd , faces [ i ] . v1 ) ;
VectorMA ( faces [ i ] . v1 , radius / 2.0f , right , faces [ i ] . v1 ) ;
VectorMA ( tip , - radius / 2.0f , fwd , faces [ i ] . v2 ) ;
VectorMA ( faces [ i ] . v2 , - radius / 2.0f , right , faces [ i ] . v2 ) ;
VectorMA ( tip , - radius / 2.0f , fwd , faces [ i ] . v3 ) ;
VectorMA ( faces [ i ] . v3 , radius / 2.0f , right , faces [ i ] . v3 ) ;
i + + ;
//bottom surface
//face 1
VectorMA ( base , radius / 2.0f , fwd , faces [ i ] . v1 ) ;
VectorMA ( faces [ i ] . v1 , - radius / 2.0f , right , faces [ i ] . v1 ) ;
VectorMA ( base , radius / 2.0f , fwd , faces [ i ] . v2 ) ;
VectorMA ( faces [ i ] . v2 , radius / 2.0f , right , faces [ i ] . v2 ) ;
VectorMA ( base , - radius / 2.0f , fwd , faces [ i ] . v3 ) ;
VectorMA ( faces [ i ] . v3 , - radius / 2.0f , right , faces [ i ] . v3 ) ;
i + + ;
//face 2
VectorMA ( base , radius / 2.0f , fwd , faces [ i ] . v1 ) ;
VectorMA ( faces [ i ] . v1 , radius / 2.0f , right , faces [ i ] . v1 ) ;
VectorMA ( base , - radius / 2.0f , fwd , faces [ i ] . v2 ) ;
VectorMA ( faces [ i ] . v2 , - radius / 2.0f , right , faces [ i ] . v2 ) ;
VectorMA ( base , - radius / 2.0f , fwd , faces [ i ] . v3 ) ;
VectorMA ( faces [ i ] . v3 , radius / 2.0f , right , faces [ i ] . v3 ) ;
i + + ;
//yeah.. always going to be 12 I suppose.
* fNum = i ;
* fList = & faces [ 0 ] ;
}
//collision utility function -rww
static GAME_INLINE void G_SabCol_CalcPlaneEq ( vec3_t x , vec3_t y , vec3_t z , float * planeEq )
{
planeEq [ 0 ] = x [ 1 ] * ( y [ 2 ] - z [ 2 ] ) + y [ 1 ] * ( z [ 2 ] - x [ 2 ] ) + z [ 1 ] * ( x [ 2 ] - y [ 2 ] ) ;
planeEq [ 1 ] = x [ 2 ] * ( y [ 0 ] - z [ 0 ] ) + y [ 2 ] * ( z [ 0 ] - x [ 0 ] ) + z [ 2 ] * ( x [ 0 ] - y [ 0 ] ) ;
planeEq [ 2 ] = x [ 0 ] * ( y [ 1 ] - z [ 1 ] ) + y [ 0 ] * ( z [ 1 ] - x [ 1 ] ) + z [ 0 ] * ( x [ 1 ] - y [ 1 ] ) ;
planeEq [ 3 ] = - ( x [ 0 ] * ( y [ 1 ] * z [ 2 ] - z [ 1 ] * y [ 2 ] ) + y [ 0 ] * ( z [ 1 ] * x [ 2 ] - x [ 1 ] * z [ 2 ] ) + z [ 0 ] * ( x [ 1 ] * y [ 2 ] - y [ 1 ] * x [ 2 ] ) ) ;
}
//collision utility function -rww
static GAME_INLINE int G_SabCol_PointRelativeToPlane ( vec3_t pos , float * side , float * planeEq )
{
* side = planeEq [ 0 ] * pos [ 0 ] + planeEq [ 1 ] * pos [ 1 ] + planeEq [ 2 ] * pos [ 2 ] + planeEq [ 3 ] ;
if ( * side > 0.0f )
{
return 1 ;
}
else if ( * side < 0.0f )
{
return - 1 ;
}
return 0 ;
}
//do actual collision check using generated saber "faces"
static GAME_INLINE qboolean G_SaberFaceCollisionCheck ( int fNum , saberFace_t * fList , vec3_t atkStart ,
vec3_t atkEnd , vec3_t atkMins , vec3_t atkMaxs , vec3_t impactPoint )
{
static float planeEq [ 4 ] ;
static float side , side2 , dist ;
static vec3_t dir ;
static vec3_t point ;
int i = 0 ;
if ( VectorCompare ( atkMins , vec3_origin ) & & VectorCompare ( atkMaxs , vec3_origin ) )
{
VectorSet ( atkMins , - 1.0f , - 1.0f , - 1.0f ) ;
VectorSet ( atkMaxs , 1.0f , 1.0f , 1.0f ) ;
}
VectorSubtract ( atkEnd , atkStart , dir ) ;
while ( i < fNum )
{
G_SabCol_CalcPlaneEq ( fList - > v1 , fList - > v2 , fList - > v3 , planeEq ) ;
if ( G_SabCol_PointRelativeToPlane ( atkStart , & side , planeEq ) ! =
G_SabCol_PointRelativeToPlane ( atkEnd , & side2 , planeEq ) )
{ //start/end points intersect with the plane
static vec3_t extruded ;
static vec3_t minPoint , maxPoint ;
static vec3_t planeNormal ;
static int facing ;
VectorCopy ( & planeEq [ 0 ] , planeNormal ) ;
side2 = planeNormal [ 0 ] * dir [ 0 ] + planeNormal [ 1 ] * dir [ 1 ] + planeNormal [ 2 ] * dir [ 2 ] ;
dist = side / side2 ;
VectorMA ( atkStart , - dist , dir , point ) ;
VectorAdd ( point , atkMins , minPoint ) ;
VectorAdd ( point , atkMaxs , maxPoint ) ;
//point is now the point at which we intersect on the plane.
//see if that point is within the edges of the face.
VectorMA ( fList - > v1 , - 2.0f , planeNormal , extruded ) ;
G_SabCol_CalcPlaneEq ( fList - > v1 , fList - > v2 , extruded , planeEq ) ;
facing = G_SabCol_PointRelativeToPlane ( point , & side , planeEq ) ;
if ( facing < 0 )
{ //not intersecting.. let's try with the mins/maxs and see if they interesect on the edge plane
facing = G_SabCol_PointRelativeToPlane ( minPoint , & side , planeEq ) ;
if ( facing < 0 )
{
facing = G_SabCol_PointRelativeToPlane ( maxPoint , & side , planeEq ) ;
}
}
if ( facing > = 0 )
{ //first edge is facing...
VectorMA ( fList - > v2 , - 2.0f , planeNormal , extruded ) ;
G_SabCol_CalcPlaneEq ( fList - > v2 , fList - > v3 , extruded , planeEq ) ;
facing = G_SabCol_PointRelativeToPlane ( point , & side , planeEq ) ;
if ( facing < 0 )
{ //not intersecting.. let's try with the mins/maxs and see if they interesect on the edge plane
facing = G_SabCol_PointRelativeToPlane ( minPoint , & side , planeEq ) ;
if ( facing < 0 )
{
facing = G_SabCol_PointRelativeToPlane ( maxPoint , & side , planeEq ) ;
}
}
if ( facing > = 0 )
{ //second edge is facing...
VectorMA ( fList - > v3 , - 2.0f , planeNormal , extruded ) ;
G_SabCol_CalcPlaneEq ( fList - > v3 , fList - > v1 , extruded , planeEq ) ;
facing = G_SabCol_PointRelativeToPlane ( point , & side , planeEq ) ;
if ( facing < 0 )
{ //not intersecting.. let's try with the mins/maxs and see if they interesect on the edge plane
facing = G_SabCol_PointRelativeToPlane ( minPoint , & side , planeEq ) ;
if ( facing < 0 )
{
facing = G_SabCol_PointRelativeToPlane ( maxPoint , & side , planeEq ) ;
}
}
if ( facing > = 0 )
{ //third edge is facing.. success
VectorCopy ( point , impactPoint ) ;
return qtrue ;
}
}
}
}
i + + ;
fList + + ;
}
//did not hit anything
return qfalse ;
}
//check for collision of 2 blades -rww
static GAME_INLINE qboolean G_SaberCollide ( gentity_t * atk , gentity_t * def , vec3_t atkStart ,
vec3_t atkEnd , vec3_t atkMins , vec3_t atkMaxs , vec3_t impactPoint )
{
static int i , j ;
if ( ! g_saberBladeFaces . integer )
{ //detailed check not enabled
return qtrue ;
}
if ( ! atk - > inuse | | ! atk - > client | | ! def - > inuse | | ! def - > client )
{ //must have 2 clients and a valid saber entity
return qfalse ;
}
i = 0 ;
while ( i < MAX_SABERS )
{
j = 0 ;
if ( def - > client - > saber [ i ] . model & & def - > client - > saber [ i ] . model [ 0 ] )
{ //valid saber on the defender
bladeInfo_t * blade ;
vec3_t v , fwd , right , base , tip ;
int fNum ;
saberFace_t * fList ;
//go through each blade on the defender's sabers
while ( j < def - > client - > saber [ i ] . numBlades )
{
blade = & def - > client - > saber [ i ] . blade [ j ] ;
if ( ( level . time - blade - > storageTime ) < 200 )
{ //recently updated
//first get base and tip of blade
VectorCopy ( blade - > muzzlePoint , base ) ;
VectorMA ( base , blade - > lengthMax , blade - > muzzleDir , tip ) ;
//Now get relative angles between the points
VectorSubtract ( tip , base , v ) ;
vectoangles ( v , v ) ;
AngleVectors ( v , NULL , right , fwd ) ;
//now build collision faces for this blade
G_BuildSaberFaces ( base , tip , blade - > radius * 3.0f , fwd , right , & fNum , & fList ) ;
if ( fNum > 0 )
{
#if 0
if ( atk - > s . number = = 0 )
{
int x = 0 ;
saberFace_t * l = fList ;
while ( x < fNum )
{
G_TestLine ( fList - > v1 , fList - > v2 , 0x0000ff , 100 ) ;
G_TestLine ( fList - > v2 , fList - > v3 , 0x0000ff , 100 ) ;
G_TestLine ( fList - > v3 , fList - > v1 , 0x0000ff , 100 ) ;
fList + + ;
x + + ;
}
fList = l ;
}
# endif
if ( G_SaberFaceCollisionCheck ( fNum , fList , atkStart , atkEnd , atkMins , atkMaxs , impactPoint ) )
{ //collided
return qtrue ;
}
}
}
j + + ;
}
}
i + + ;
}
return qfalse ;
}
float WP_SaberBladeLength ( saberInfo_t * saber )
{ //return largest length
int i ;
float len = 0.0f ;
for ( i = 0 ; i < saber - > numBlades ; i + + )
{
if ( saber - > blade [ i ] . lengthMax > len )
{
len = saber - > blade [ i ] . lengthMax ;
}
}
return len ;
}
float WP_SaberLength ( gentity_t * ent )
{ //return largest length
if ( ! ent | | ! ent - > client )
{
return 0.0f ;
}
else
{
int i ;
float len , bestLen = 0.0f ;
for ( i = 0 ; i < MAX_SABERS ; i + + )
{
len = WP_SaberBladeLength ( & ent - > client - > saber [ i ] ) ;
if ( len > bestLen )
{
bestLen = len ;
}
}
return bestLen ;
}
}
int WPDEBUG_SaberColor ( saber_colors_t saberColor )
{
switch ( ( int ) ( saberColor ) )
{
case SABER_RED :
return 0x000000ff ;
break ;
case SABER_ORANGE :
return 0x000088ff ;
break ;
case SABER_YELLOW :
return 0x0000ffff ;
break ;
case SABER_GREEN :
return 0x0000ff00 ;
break ;
case SABER_BLUE :
return 0x00ff0000 ;
break ;
case SABER_PURPLE :
return 0x00ff00ff ;
break ;
default :
return 0x00ffffff ; //white
break ;
}
}
/*
WP_SabersIntersect
Breaks the two saber paths into 2 tris each and tests each tri for the first saber path against each of the other saber path ' s tris
FIXME : subdivide the arc into a consistant increment
FIXME : test the intersection to see if the sabers really did intersect ( weren ' t going in the same direction and / or passed through same point at different times ) ?
*/
extern qboolean tri_tri_intersect ( vec3_t V0 , vec3_t V1 , vec3_t V2 , vec3_t U0 , vec3_t U1 , vec3_t U2 ) ;
# define SABER_EXTRAPOLATE_DIST 16.0f
qboolean WP_SabersIntersect ( gentity_t * ent1 , int ent1SaberNum , int ent1BladeNum , gentity_t * ent2 , qboolean checkDir )
{
vec3_t saberBase1 , saberTip1 , saberBaseNext1 , saberTipNext1 ;
vec3_t saberBase2 , saberTip2 , saberBaseNext2 , saberTipNext2 ;
int ent2SaberNum = 0 , ent2BladeNum = 0 ;
vec3_t dir ;
if ( ! ent1 | | ! ent2 )
{
return qfalse ;
}
if ( ! ent1 - > client | | ! ent2 - > client )
{
return qfalse ;
}
if ( BG_SabersOff ( & ent1 - > client - > ps )
| | BG_SabersOff ( & ent2 - > client - > ps ) )
{
return qfalse ;
}
for ( ent2SaberNum = 0 ; ent2SaberNum < MAX_SABERS ; ent2SaberNum + + )
{
if ( ent2 - > client - > saber [ ent2SaberNum ] . type ! = SABER_NONE )
{
for ( ent2BladeNum = 0 ; ent2BladeNum < ent2 - > client - > saber [ ent2SaberNum ] . numBlades ; ent2BladeNum + + )
{
if ( ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . lengthMax > 0 )
{ //valid saber and this blade is on
//if ( ent1->client->saberInFlight )
{
VectorCopy ( ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzlePointOld , saberBase1 ) ;
VectorCopy ( ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzlePoint , saberBaseNext1 ) ;
VectorSubtract ( ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzlePoint , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzlePointOld , dir ) ;
VectorNormalize ( dir ) ;
VectorMA ( saberBaseNext1 , SABER_EXTRAPOLATE_DIST , dir , saberBaseNext1 ) ;
VectorMA ( saberBase1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . lengthMax + SABER_EXTRAPOLATE_DIST , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzleDirOld , saberTip1 ) ;
VectorMA ( saberBaseNext1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . lengthMax + SABER_EXTRAPOLATE_DIST , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzleDir , saberTipNext1 ) ;
VectorSubtract ( saberTipNext1 , saberTip1 , dir ) ;
VectorNormalize ( dir ) ;
VectorMA ( saberTipNext1 , SABER_EXTRAPOLATE_DIST , dir , saberTipNext1 ) ;
}
/*
else
{
VectorCopy ( ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzlePoint , saberBase1 ) ;
VectorCopy ( ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzlePointNext , saberBaseNext1 ) ;
VectorMA ( saberBase1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . lengthMax , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzleDir , saberTip1 ) ;
VectorMA ( saberBaseNext1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . lengthMax , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzleDirNext , saberTipNext1 ) ;
}
*/
//if ( ent2->client->saberInFlight )
{
VectorCopy ( ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzlePointOld , saberBase2 ) ;
VectorCopy ( ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzlePoint , saberBaseNext2 ) ;
VectorSubtract ( ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzlePoint , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzlePointOld , dir ) ;
VectorNormalize ( dir ) ;
VectorMA ( saberBaseNext2 , SABER_EXTRAPOLATE_DIST , dir , saberBaseNext2 ) ;
VectorMA ( saberBase2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . lengthMax + SABER_EXTRAPOLATE_DIST , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzleDirOld , saberTip2 ) ;
VectorMA ( saberBaseNext2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . lengthMax + SABER_EXTRAPOLATE_DIST , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzleDir , saberTipNext2 ) ;
VectorSubtract ( saberTipNext2 , saberTip2 , dir ) ;
VectorNormalize ( dir ) ;
VectorMA ( saberTipNext2 , SABER_EXTRAPOLATE_DIST , dir , saberTipNext2 ) ;
}
/*
else
{
VectorCopy ( ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzlePoint , saberBase2 ) ;
VectorCopy ( ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzlePointNext , saberBaseNext2 ) ;
VectorMA ( saberBase2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . lengthMax , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzleDir , saberTip2 ) ;
VectorMA ( saberBaseNext2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . lengthMax , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzleDirNext , saberTipNext2 ) ;
}
*/
if ( checkDir )
{ //check the direction of the two swings to make sure the sabers are swinging towards each other
vec3_t saberDir1 , saberDir2 ;
float dot = 0.0f ;
VectorSubtract ( saberTipNext1 , saberTip1 , saberDir1 ) ;
VectorSubtract ( saberTipNext2 , saberTip2 , saberDir2 ) ;
VectorNormalize ( saberDir1 ) ;
VectorNormalize ( saberDir2 ) ;
if ( DotProduct ( saberDir1 , saberDir2 ) > 0.6f )
{ //sabers moving in same dir, probably didn't actually hit
continue ;
}
//now check orientation of sabers, make sure they're not parallel or close to it
dot = DotProduct ( ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . muzzleDir , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . muzzleDir ) ;
if ( dot > 0.9f | | dot < - 0.9f )
{ //too parallel to really block effectively?
continue ;
}
}
# ifdef DEBUG_SABER_BOX
if ( g_saberDebugBox . integer = = 2 | | g_saberDebugBox . integer = = 4 )
{
G_TestLine ( saberBase1 , saberTip1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . color , 500 ) ;
G_TestLine ( saberTip1 , saberTipNext1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . color , 500 ) ;
G_TestLine ( saberTipNext1 , saberBase1 , ent1 - > client - > saber [ ent1SaberNum ] . blade [ ent1BladeNum ] . color , 500 ) ;
G_TestLine ( saberBase2 , saberTip2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . color , 500 ) ;
G_TestLine ( saberTip2 , saberTipNext2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . color , 500 ) ;
G_TestLine ( saberTipNext2 , saberBase2 , ent2 - > client - > saber [ ent2SaberNum ] . blade [ ent2BladeNum ] . color , 500 ) ;
}
# endif
if ( tri_tri_intersect ( saberBase1 , saberTip1 , saberBaseNext1 , saberBase2 , saberTip2 , saberBaseNext2 ) )
{
return qtrue ;
}
if ( tri_tri_intersect ( saberBase1 , saberTip1 , saberBaseNext1 , saberBase2 , saberTip2 , saberTipNext2 ) )
{
return qtrue ;
}
if ( tri_tri_intersect ( saberBase1 , saberTip1 , saberTipNext1 , saberBase2 , saberTip2 , saberBaseNext2 ) )
{
return qtrue ;
}
if ( tri_tri_intersect ( saberBase1 , saberTip1 , saberTipNext1 , saberBase2 , saberTip2 , saberTipNext2 ) )
{
return qtrue ;
}
}
}
}
}
return qfalse ;
}
static GAME_INLINE int G_PowerLevelForSaberAnim ( gentity_t * ent , int saberNum , qboolean mySaberHit )
{
if ( ! ent | | ! ent - > client | | saberNum > = MAX_SABERS )
{
return FORCE_LEVEL_0 ;
}
else
{
int anim = ent - > client - > ps . torsoAnim ;
int animTimer = ent - > client - > ps . torsoTimer ;
int animTimeElapsed = BG_AnimLength ( ent - > localAnimIndex , ( animNumber_t ) anim ) - animTimer ;
saberInfo_t * saber = & ent - > client - > saber [ saberNum ] ;
if ( anim > = BOTH_A1_T__B_ & & anim < = BOTH_D1_B____ )
{
//FIXME: these two need their own style
if ( saber - > type = = SABER_LANCE )
{
return FORCE_LEVEL_4 ;
}
else if ( saber - > type = = SABER_TRIDENT )
{
return FORCE_LEVEL_3 ;
}
return FORCE_LEVEL_1 ;
}
if ( anim > = BOTH_A2_T__B_ & & anim < = BOTH_D2_B____ )
{
return FORCE_LEVEL_2 ;
}
if ( anim > = BOTH_A3_T__B_ & & anim < = BOTH_D3_B____ )
{
return FORCE_LEVEL_3 ;
}
if ( anim > = BOTH_A4_T__B_ & & anim < = BOTH_D4_B____ )
{ //desann
return FORCE_LEVEL_4 ;
}
if ( anim > = BOTH_A5_T__B_ & & anim < = BOTH_D5_B____ )
{ //tavion
return FORCE_LEVEL_2 ;
}
if ( anim > = BOTH_A6_T__B_ & & anim < = BOTH_D6_B____ )
{ //dual
return FORCE_LEVEL_2 ;
}
if ( anim > = BOTH_A7_T__B_ & & anim < = BOTH_D7_B____ )
{ //staff
return FORCE_LEVEL_2 ;
}
if ( anim > = BOTH_P1_S1_T_ & & anim < = BOTH_H1_S1_BR )
{ //parries, knockaways and broken parries
return FORCE_LEVEL_1 ; //FIXME: saberAnimLevel?
}
switch ( anim )
{
case BOTH_A2_STABBACK1 :
if ( mySaberHit )
{ //someone else hit my saber, not asking for damage level, but defense strength
return FORCE_LEVEL_1 ;
}
if ( animTimer < 450 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 400 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_ATTACK_BACK :
if ( animTimer < 500 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_CROUCHATTACKBACK1 :
if ( animTimer < 800 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_BUTTERFLY_LEFT :
case BOTH_BUTTERFLY_RIGHT :
case BOTH_BUTTERFLY_FL1 :
case BOTH_BUTTERFLY_FR1 :
//FIXME: break up?
return FORCE_LEVEL_3 ;
break ;
case BOTH_FJSS_TR_BL :
case BOTH_FJSS_TL_BR :
//FIXME: break up?
return FORCE_LEVEL_3 ;
break ;
case BOTH_K1_S1_T_ : //# knockaway saber top
case BOTH_K1_S1_TR : //# knockaway saber top right
case BOTH_K1_S1_TL : //# knockaway saber top left
case BOTH_K1_S1_BL : //# knockaway saber bottom left
case BOTH_K1_S1_B_ : //# knockaway saber bottom
case BOTH_K1_S1_BR : //# knockaway saber bottom right
//FIXME: break up?
return FORCE_LEVEL_3 ;
break ;
case BOTH_LUNGE2_B__T_ :
if ( mySaberHit )
{ //someone else hit my saber, not asking for damage level, but defense strength
return FORCE_LEVEL_1 ;
}
if ( animTimer < 400 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 150 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_FORCELEAP2_T__B_ :
if ( animTimer < 400 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 550 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_VS_ATR_S :
case BOTH_VS_ATL_S :
case BOTH_VT_ATR_S :
case BOTH_VT_ATL_S :
return FORCE_LEVEL_3 ; //???
break ;
case BOTH_JUMPFLIPSLASHDOWN1 :
if ( animTimer < = 1000 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 600 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_JUMPFLIPSTABDOWN :
if ( animTimer < = 1300 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < = 300 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_JUMPATTACK6 :
/*
if ( pm - > ps )
{
if ( ( pm - > ps - > legsAnimTimer > = 1450
& & BG_AnimLength ( g_entities [ ps - > clientNum ] . client - > clientInfo . animFileIndex , BOTH_JUMPATTACK6 ) - pm - > ps - > legsAnimTimer > = 400 )
| | ( pm - > ps - > legsAnimTimer > = 400
& & BG_AnimLength ( g_entities [ ps - > clientNum ] . client - > clientInfo . animFileIndex , BOTH_JUMPATTACK6 ) - pm - > ps - > legsAnimTimer > = 1100 ) )
{ //pretty much sideways
return FORCE_LEVEL_3 ;
}
}
*/
if ( ( animTimer > = 1450
& & animTimeElapsed > = 400 )
| | ( animTimer > = 400
& & animTimeElapsed > = 1100 ) )
{ //pretty much sideways
return FORCE_LEVEL_3 ;
}
return FORCE_LEVEL_0 ;
break ;
case BOTH_JUMPATTACK7 :
if ( animTimer < = 1200 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 200 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_SPINATTACK6 :
if ( animTimeElapsed < = 200 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_2 ; //FORCE_LEVEL_3;
break ;
case BOTH_SPINATTACK7 :
if ( animTimer < = 500 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 500 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_2 ; //FORCE_LEVEL_3;
break ;
case BOTH_FORCELONGLEAP_ATTACK :
if ( animTimeElapsed < = 200 )
{ //1st four frames of anim
return FORCE_LEVEL_3 ;
}
break ;
/*
case BOTH_A7_KICK_F : //these kicks attack, too
case BOTH_A7_KICK_B :
case BOTH_A7_KICK_R :
case BOTH_A7_KICK_L :
//FIXME: break up
return FORCE_LEVEL_3 ;
break ;
*/
case BOTH_STABDOWN :
if ( animTimer < = 900 )
{ //end of anim
return FORCE_LEVEL_3 ;
}
break ;
case BOTH_STABDOWN_STAFF :
if ( animTimer < = 850 )
{ //end of anim
return FORCE_LEVEL_3 ;
}
break ;
case BOTH_STABDOWN_DUAL :
if ( animTimer < = 900 )
{ //end of anim
return FORCE_LEVEL_3 ;
}
break ;
case BOTH_A6_SABERPROTECT :
if ( animTimer < 650 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 250 )
{ //start of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A7_SOULCAL :
if ( animTimer < 650 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 600 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A1_SPECIAL :
if ( animTimer < 600 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 200 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A2_SPECIAL :
if ( animTimer < 300 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 200 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A3_SPECIAL :
if ( animTimer < 700 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 200 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_FLIP_ATTACK7 :
return FORCE_LEVEL_3 ;
break ;
case BOTH_PULL_IMPALE_STAB :
if ( mySaberHit )
{ //someone else hit my saber, not asking for damage level, but defense strength
return FORCE_LEVEL_1 ;
}
if ( animTimer < 1000 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_PULL_IMPALE_SWING :
if ( animTimer < 500 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 650 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_ALORA_SPIN_SLASH :
if ( animTimer < 900 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 250 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A6_FB :
if ( mySaberHit )
{ //someone else hit my saber, not asking for damage level, but defense strength
return FORCE_LEVEL_1 ;
}
if ( animTimer < 250 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 250 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A6_LR :
if ( mySaberHit )
{ //someone else hit my saber, not asking for damage level, but defense strength
return FORCE_LEVEL_1 ;
}
if ( animTimer < 250 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 250 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_3 ;
break ;
case BOTH_A7_HILT :
return FORCE_LEVEL_0 ;
break ;
//===SABERLOCK SUPERBREAKS START===========================================================================
case BOTH_LK_S_DL_T_SB_1_W :
if ( animTimer < 700 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_S_ST_S_SB_1_W :
if ( animTimer < 300 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_S_DL_S_SB_1_W :
case BOTH_LK_S_S_S_SB_1_W :
if ( animTimer < 700 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 400 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_S_ST_T_SB_1_W :
case BOTH_LK_S_S_T_SB_1_W :
if ( animTimer < 150 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 400 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_DL_DL_T_SB_1_W :
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_DL_DL_S_SB_1_W :
case BOTH_LK_DL_ST_S_SB_1_W :
if ( animTimeElapsed < 1000 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_DL_ST_T_SB_1_W :
if ( animTimer < 950 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 650 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_DL_S_S_SB_1_W :
if ( saberNum ! = 0 )
{ //only right hand saber does damage in this suberbreak
return FORCE_LEVEL_0 ;
}
if ( animTimer < 900 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 450 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_DL_S_T_SB_1_W :
if ( saberNum ! = 0 )
{ //only right hand saber does damage in this suberbreak
return FORCE_LEVEL_0 ;
}
if ( animTimer < 250 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 150 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_ST_DL_S_SB_1_W :
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_ST_DL_T_SB_1_W :
//special suberbreak - doesn't kill, just kicks them backwards
return FORCE_LEVEL_0 ;
break ;
case BOTH_LK_ST_ST_S_SB_1_W :
case BOTH_LK_ST_S_S_SB_1_W :
if ( animTimer < 800 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 350 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
return FORCE_LEVEL_5 ;
break ;
case BOTH_LK_ST_ST_T_SB_1_W :
case BOTH_LK_ST_S_T_SB_1_W :
return FORCE_LEVEL_5 ;
break ;
//===SABERLOCK SUPERBREAKS START===========================================================================
case BOTH_HANG_ATTACK :
//FIME: break up
if ( animTimer < 1000 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else if ( animTimeElapsed < 250 )
{ //beginning of anim
return FORCE_LEVEL_0 ;
}
else
{ //sweet spot
return FORCE_LEVEL_5 ;
}
break ;
case BOTH_ROLL_STAB :
if ( mySaberHit )
{ //someone else hit my saber, not asking for damage level, but defense strength
return FORCE_LEVEL_1 ;
}
if ( animTimeElapsed > 400 )
{ //end of anim
return FORCE_LEVEL_0 ;
}
else
{
return FORCE_LEVEL_3 ;
}
break ;
}
return FORCE_LEVEL_0 ;
}
}
# define MAX_SABER_VICTIMS 16
static int victimEntityNum [ MAX_SABER_VICTIMS ] ;
static qboolean victimHitEffectDone [ MAX_SABER_VICTIMS ] ;
static float totalDmg [ MAX_SABER_VICTIMS ] ;
static vec3_t dmgDir [ MAX_SABER_VICTIMS ] ;
static vec3_t dmgSpot [ MAX_SABER_VICTIMS ] ;
static qboolean dismemberDmg [ MAX_SABER_VICTIMS ] ;
static int saberKnockbackFlags [ MAX_SABER_VICTIMS ] ;
static int numVictims = 0 ;
void WP_SaberClearDamage ( void )
{
int ven ;
for ( ven = 0 ; ven < MAX_SABER_VICTIMS ; ven + + )
{
victimEntityNum [ ven ] = ENTITYNUM_NONE ;
}
memset ( victimHitEffectDone , 0 , sizeof ( victimHitEffectDone ) ) ;
memset ( totalDmg , 0 , sizeof ( totalDmg ) ) ;
memset ( dmgDir , 0 , sizeof ( dmgDir ) ) ;
memset ( dmgSpot , 0 , sizeof ( dmgSpot ) ) ;
memset ( dismemberDmg , 0 , sizeof ( dismemberDmg ) ) ;
memset ( saberKnockbackFlags , 0 , sizeof ( saberKnockbackFlags ) ) ;
numVictims = 0 ;
}
void WP_SaberDamageAdd ( int trVictimEntityNum , vec3_t trDmgDir , vec3_t trDmgSpot ,
int trDmg , qboolean doDismemberment , int knockBackFlags )
{
if ( trVictimEntityNum < 0 | | trVictimEntityNum > = ENTITYNUM_WORLD )
{
return ;
}
if ( trDmg )
{ //did some damage to something
int curVictim = 0 ;
int i ;
for ( i = 0 ; i < numVictims ; i + + )
{
if ( victimEntityNum [ i ] = = trVictimEntityNum )
{ //already hit this guy before
curVictim = i ;
break ;
}
}
if ( i = = numVictims )
{ //haven't hit his guy before
if ( numVictims + 1 > = MAX_SABER_VICTIMS )
{ //can't add another victim at this time
return ;
}
//add a new victim to the list
curVictim = numVictims ;
victimEntityNum [ numVictims + + ] = trVictimEntityNum ;
}
totalDmg [ curVictim ] + = trDmg ;
if ( VectorCompare ( dmgDir [ curVictim ] , vec3_origin ) )
{
VectorCopy ( trDmgDir , dmgDir [ curVictim ] ) ;
}
if ( VectorCompare ( dmgSpot [ curVictim ] , vec3_origin ) )
{
VectorCopy ( trDmgSpot , dmgSpot [ curVictim ] ) ;
}
if ( doDismemberment )
{
dismemberDmg [ curVictim ] = qtrue ;
}
saberKnockbackFlags [ curVictim ] | = knockBackFlags ;
}
}
void WP_SaberApplyDamage ( gentity_t * self )
{
int i ;
if ( ! numVictims )
{
return ;
}
for ( i = 0 ; i < numVictims ; i + + )
{
gentity_t * victim = NULL ;
int dflags = 0 ;
victim = & g_entities [ victimEntityNum [ i ] ] ;
// nmckenzie: SABER_DAMAGE_WALLS
if ( ! victim - > client )
{
totalDmg [ i ] * = g_saberWallDamageScale . value ;
}
if ( ! dismemberDmg [ i ] )
{ //don't do dismemberment!
dflags | = DAMAGE_NO_DISMEMBER ;
}
dflags | = saberKnockbackFlags [ i ] ;
G_Damage ( victim , self , self , dmgDir [ i ] , dmgSpot [ i ] , totalDmg [ i ] , dflags , MOD_SABER ) ;
}
}
void WP_SaberDoHit ( gentity_t * self , int saberNum , int bladeNum )
{
int i ;
if ( ! numVictims )
{
return ;
}
for ( i = 0 ; i < numVictims ; i + + )
{
gentity_t * te = NULL , * victim = NULL ;
qboolean isDroid = qfalse ;
if ( victimHitEffectDone [ i ] )
{
continue ;
}
victimHitEffectDone [ i ] = qtrue ;
victim = & g_entities [ victimEntityNum [ i ] ] ;
if ( victim - > client )
{
class_t npc_class = victim - > client - > NPC_class ;
if ( npc_class = = CLASS_SEEKER | | npc_class = = CLASS_PROBE | | npc_class = = CLASS_MOUSE | | npc_class = = CLASS_REMOTE | |
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 )
{ //don't make "blood" sparks for droids.
isDroid = qtrue ;
}
}
te = G_TempEntity ( dmgSpot [ i ] , EV_SABER_HIT ) ;
if ( te )
{
te - > s . otherEntityNum = victimEntityNum [ i ] ;
te - > s . otherEntityNum2 = self - > s . number ;
te - > s . weapon = saberNum ;
te - > s . legsAnim = bladeNum ;
VectorCopy ( dmgSpot [ i ] , te - > s . origin ) ;
//VectorCopy(tr.plane.normal, te->s.angles);
VectorScale ( dmgDir [ i ] , - 1 , te - > s . angles ) ;
if ( ! te - > s . angles [ 0 ] & & ! te - > s . angles [ 1 ] & & ! te - > s . angles [ 2 ] )
{ //don't let it play with no direction
te - > s . angles [ 1 ] = 1 ;
}
if ( ! isDroid & & ( victim - > client | | victim - > s . eType = = ET_NPC | |
victim - > s . eType = = ET_BODY ) )
{
if ( totalDmg [ i ] < 5 )
{
te - > s . eventParm = 3 ;
}
else if ( totalDmg [ i ] < 20 )
{
te - > s . eventParm = 2 ;
}
else
{
te - > s . eventParm = 1 ;
}
}
else
{
if ( ! WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ saberNum ] , bladeNum )
& & ( self - > client - > saber [ saberNum ] . saberFlags2 & SFL2_NO_CLASH_FLARE ) )
{ //don't do clash flare
}
else if ( WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ saberNum ] , bladeNum )
& & ( self - > client - > saber [ saberNum ] . saberFlags2 & SFL2_NO_CLASH_FLARE2 ) )
{ //don't do clash flare
}
else
{
if ( totalDmg [ i ] > SABER_NONATTACK_DAMAGE )
{ //I suppose I could tie this into the saberblock event, but I'm tired of adding flags to that thing.
gentity_t * teS = G_TempEntity ( te - > s . origin , EV_SABER_CLASHFLARE ) ;
VectorCopy ( te - > s . origin , teS - > s . origin ) ;
}
te - > s . eventParm = 0 ;
}
}
}
}
}
extern qboolean G_EntIsBreakable ( int entityNum ) ;
extern void G_Knockdown ( gentity_t * victim ) ;
void WP_SaberRadiusDamage ( gentity_t * ent , vec3_t point , float radius , int damage , float knockBack )
{
if ( ! ent | | ! ent - > client )
{
return ;
}
else if ( radius < = 0.0f | | ( damage < = 0 & & knockBack < = 0 ) )
{
return ;
}
else
{
vec3_t mins , maxs , entDir ;
int radiusEnts [ 128 ] ;
gentity_t * radiusEnt = NULL ;
int numEnts , i ;
float dist ;
//Setup the bbox to search in
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = point [ i ] - radius ;
maxs [ i ] = point [ i ] + radius ;
}
//Get the number of entities in a given space
numEnts = trap_EntitiesInBox ( mins , maxs , radiusEnts , 128 ) ;
for ( i = 0 ; i < numEnts ; i + + )
{
radiusEnt = & g_entities [ radiusEnts [ i ] ] ;
if ( ! radiusEnt - > inuse )
{
continue ;
}
if ( radiusEnt = = ent )
{ //Skip myself
continue ;
}
if ( radiusEnt - > client = = NULL )
{ //must be a client
if ( G_EntIsBreakable ( radiusEnt - > s . number ) )
{ //damage breakables within range, but not as much
G_Damage ( radiusEnt , ent , ent , vec3_origin , radiusEnt - > r . currentOrigin , 10 , 0 , MOD_MELEE ) ;
}
continue ;
}
if ( ( radiusEnt - > client - > ps . eFlags2 & EF2_HELD_BY_MONSTER ) )
{ //can't be one being held
continue ;
}
VectorSubtract ( radiusEnt - > r . currentOrigin , point , entDir ) ;
dist = VectorNormalize ( entDir ) ;
if ( dist < = radius )
{ //in range
if ( damage > 0 )
{ //do damage
int points = ceil ( ( float ) damage * dist / radius ) ;
G_Damage ( radiusEnt , ent , ent , vec3_origin , radiusEnt - > r . currentOrigin , points , DAMAGE_NO_KNOCKBACK , MOD_MELEE ) ;
}
if ( knockBack > 0 )
{ //do knockback
if ( radiusEnt - > client
& & radiusEnt - > client - > NPC_class ! = CLASS_RANCOR
& & radiusEnt - > client - > NPC_class ! = CLASS_ATST
& & ! ( radiusEnt - > flags & FL_NO_KNOCKBACK ) ) //don't throw them back
{
float knockbackStr = knockBack * dist / radius ;
entDir [ 2 ] + = 0.1f ;
VectorNormalize ( entDir ) ;
G_Throw ( radiusEnt , entDir , knockbackStr ) ;
if ( radiusEnt - > health > 0 )
{ //still alive
if ( knockbackStr > 50 )
{ //close enough and knockback high enough to possibly knock down
if ( dist < ( radius * 0.5f )
| | radiusEnt - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE )
{ //within range of my fist or within ground-shaking range and not in the air
G_Knockdown ( radiusEnt ) ; //, ent, entDir, 500, qtrue );
}
}
}
}
}
}
}
}
}
static qboolean saberDoClashEffect = qfalse ;
static vec3_t saberClashPos = { 0 } ;
static vec3_t saberClashNorm = { 0 } ;
static int saberClashEventParm = 1 ;
void WP_SaberDoClash ( gentity_t * self , int saberNum , int bladeNum )
{
if ( saberDoClashEffect )
{
gentity_t * te = G_TempEntity ( saberClashPos , EV_SABER_BLOCK ) ;
VectorCopy ( saberClashPos , te - > s . origin ) ;
VectorCopy ( saberClashNorm , te - > s . angles ) ;
te - > s . eventParm = saberClashEventParm ;
te - > s . otherEntityNum2 = self - > s . number ;
te - > s . weapon = saberNum ;
te - > s . legsAnim = bladeNum ;
}
}
void WP_SaberBounceSound ( gentity_t * ent , int saberNum , int bladeNum )
{
int index = 1 ;
if ( ! ent | | ! ent - > client )
{
return ;
}
index = Q_irand ( 1 , 9 ) ;
if ( ! WP_SaberBladeUseSecondBladeStyle ( & ent - > client - > saber [ saberNum ] , bladeNum )
& & ent - > client - > saber [ saberNum ] . bounceSound [ 0 ] )
{
G_Sound ( ent , CHAN_AUTO , ent - > client - > saber [ saberNum ] . bounceSound [ Q_irand ( 0 , 2 ) ] ) ;
}
else if ( WP_SaberBladeUseSecondBladeStyle ( & ent - > client - > saber [ saberNum ] , bladeNum )
& & ent - > client - > saber [ saberNum ] . bounce2Sound [ 0 ] )
{
G_Sound ( ent , CHAN_AUTO , ent - > client - > saber [ saberNum ] . bounce2Sound [ Q_irand ( 0 , 2 ) ] ) ;
}
else if ( ! WP_SaberBladeUseSecondBladeStyle ( & ent - > client - > saber [ saberNum ] , bladeNum )
& & ent - > client - > saber [ saberNum ] . blockSound [ 0 ] )
{
G_Sound ( ent , CHAN_AUTO , ent - > client - > saber [ saberNum ] . blockSound [ Q_irand ( 0 , 2 ) ] ) ;
}
else if ( WP_SaberBladeUseSecondBladeStyle ( & ent - > client - > saber [ saberNum ] , bladeNum )
& & ent - > client - > saber [ saberNum ] . block2Sound [ 0 ] )
{
G_Sound ( ent , CHAN_AUTO , ent - > client - > saber [ saberNum ] . block2Sound [ Q_irand ( 0 , 2 ) ] ) ;
}
else
{
G_Sound ( ent , CHAN_AUTO , G_SoundIndex ( va ( " sound/weapons/saber/saberblock%d.wav " , index ) ) ) ;
}
}
static qboolean saberHitWall = qfalse ;
static qboolean saberHitSaber = qfalse ;
static float saberHitFraction = 1.0f ;
//rww - MP version of the saber damage function. This is where all the things like blocking, triggering a parry,
//triggering a broken parry, doing actual damage, etc. are done for the saber. It doesn't resemble the SP
//version very much, but functionality is (hopefully) about the same.
//This is a large function. I feel sort of bad inlining it. But it does get called tons of times per frame.
# include "../namespace_begin.h"
qboolean BG_SuperBreakWinAnim ( int anim ) ;
# include "../namespace_end.h"
static GAME_INLINE qboolean CheckSaberDamage ( gentity_t * self , int rSaberNum , int rBladeNum , vec3_t saberStart , vec3_t saberEnd , qboolean doInterpolate , int trMask , qboolean extrapolate )
{
static trace_t tr ;
static vec3_t dir ;
static vec3_t saberTrMins , saberTrMaxs ;
static vec3_t lastValidStart ;
static vec3_t lastValidEnd ;
static int selfSaberLevel ;
static int otherSaberLevel ;
int dmg = 0 ;
int attackStr = 0 ;
float saberBoxSize = d_saberBoxTraceSize . value ;
qboolean idleDamage = qfalse ;
qboolean didHit = qfalse ;
qboolean sabersClashed = qfalse ;
qboolean unblockable = qfalse ;
qboolean didDefense = qfalse ;
qboolean didOffense = qfalse ;
qboolean saberTraceDone = qfalse ;
qboolean otherUnblockable = qfalse ;
qboolean tryDeflectAgain = qfalse ;
gentity_t * otherOwner ;
if ( BG_SabersOff ( & self - > client - > ps ) )
{
return qfalse ;
}
selfSaberLevel = G_SaberAttackPower ( self , SaberAttacking ( self ) ) ;
//Add the standard radius into the box size
saberBoxSize + = ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . radius * 0.5f ) ;
if ( self - > client - > ps . weaponTime < = 0 )
{ //if not doing any attacks or anything, just use point traces.
VectorClear ( saberTrMins ) ;
VectorClear ( saberTrMaxs ) ;
}
else if ( d_saberGhoul2Collision . integer )
{
if ( d_saberSPStyleDamage . integer )
{ //SP-size saber damage traces
VectorSet ( saberTrMins , - 2 , - 2 , - 2 ) ;
VectorSet ( saberTrMaxs , 2 , 2 , 2 ) ;
}
else
{
VectorSet ( saberTrMins , - saberBoxSize * 3 , - saberBoxSize * 3 , - saberBoxSize * 3 ) ;
VectorSet ( saberTrMaxs , saberBoxSize * 3 , saberBoxSize * 3 , saberBoxSize * 3 ) ;
}
}
else if ( self - > client - > ps . fd . saberAnimLevel < FORCE_LEVEL_2 )
{ //box trace for fast, because it doesn't get updated so often
VectorSet ( saberTrMins , - saberBoxSize , - saberBoxSize , - saberBoxSize ) ;
VectorSet ( saberTrMaxs , saberBoxSize , saberBoxSize , saberBoxSize ) ;
}
else if ( d_saberAlwaysBoxTrace . integer )
{
VectorSet ( saberTrMins , - saberBoxSize , - saberBoxSize , - saberBoxSize ) ;
VectorSet ( saberTrMaxs , saberBoxSize , saberBoxSize , saberBoxSize ) ;
}
else
{ //just trace the minimum blade radius
saberBoxSize = ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . radius * 0.4f ) ;
VectorSet ( saberTrMins , - saberBoxSize , - saberBoxSize , - saberBoxSize ) ;
VectorSet ( saberTrMaxs , saberBoxSize , saberBoxSize , saberBoxSize ) ;
}
while ( ! saberTraceDone )
{
if ( doInterpolate
& & ! d_saberSPStyleDamage . integer )
{ //This didn't quite work out like I hoped. But it's better than nothing. Sort of.
vec3_t oldSaberStart , oldSaberEnd , saberDif , oldSaberDif ;
int traceTests = 0 ;
float trDif = 8 ;
if ( ( level . time - self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . lastTime ) > 100 )
{ //no valid last pos, use current
VectorCopy ( saberStart , oldSaberStart ) ;
VectorCopy ( saberEnd , oldSaberEnd ) ;
}
else
{ //trace from last pos
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . base , oldSaberStart ) ;
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . tip , oldSaberEnd ) ;
}
VectorSubtract ( saberStart , saberEnd , saberDif ) ;
VectorSubtract ( oldSaberStart , oldSaberEnd , oldSaberDif ) ;
VectorNormalize ( saberDif ) ;
VectorNormalize ( oldSaberDif ) ;
saberEnd [ 0 ] = saberStart [ 0 ] - ( saberDif [ 0 ] * trDif ) ;
saberEnd [ 1 ] = saberStart [ 1 ] - ( saberDif [ 1 ] * trDif ) ;
saberEnd [ 2 ] = saberStart [ 2 ] - ( saberDif [ 2 ] * trDif ) ;
oldSaberEnd [ 0 ] = oldSaberStart [ 0 ] - ( oldSaberDif [ 0 ] * trDif ) ;
oldSaberEnd [ 1 ] = oldSaberStart [ 1 ] - ( oldSaberDif [ 1 ] * trDif ) ;
oldSaberEnd [ 2 ] = oldSaberStart [ 2 ] - ( oldSaberDif [ 2 ] * trDif ) ;
trap_Trace ( & tr , saberEnd , saberTrMins , saberTrMaxs , saberStart , self - > s . number , trMask ) ;
VectorCopy ( saberEnd , lastValidStart ) ;
VectorCopy ( saberStart , lastValidEnd ) ;
if ( tr . entityNum < MAX_CLIENTS )
{
G_G2TraceCollide ( & tr , lastValidStart , lastValidEnd , saberTrMins , saberTrMaxs ) ;
}
else if ( tr . entityNum < ENTITYNUM_WORLD )
{
gentity_t * trHit = & g_entities [ tr . entityNum ] ;
if ( trHit - > inuse & & trHit - > ghoul2 )
{ //hit a non-client entity with a g2 instance
G_G2TraceCollide ( & tr , lastValidStart , lastValidEnd , saberTrMins , saberTrMaxs ) ;
}
}
trDif + + ;
while ( tr . fraction = = 1.0 & & traceTests < 4 & & tr . entityNum > = ENTITYNUM_NONE )
{
if ( ( level . time - self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . lastTime ) > 100 )
{ //no valid last pos, use current
VectorCopy ( saberStart , oldSaberStart ) ;
VectorCopy ( saberEnd , oldSaberEnd ) ;
}
else
{ //trace from last pos
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . base , oldSaberStart ) ;
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . tip , oldSaberEnd ) ;
}
VectorSubtract ( saberStart , saberEnd , saberDif ) ;
VectorSubtract ( oldSaberStart , oldSaberEnd , oldSaberDif ) ;
VectorNormalize ( saberDif ) ;
VectorNormalize ( oldSaberDif ) ;
saberEnd [ 0 ] = saberStart [ 0 ] - ( saberDif [ 0 ] * trDif ) ;
saberEnd [ 1 ] = saberStart [ 1 ] - ( saberDif [ 1 ] * trDif ) ;
saberEnd [ 2 ] = saberStart [ 2 ] - ( saberDif [ 2 ] * trDif ) ;
oldSaberEnd [ 0 ] = oldSaberStart [ 0 ] - ( oldSaberDif [ 0 ] * trDif ) ;
oldSaberEnd [ 1 ] = oldSaberStart [ 1 ] - ( oldSaberDif [ 1 ] * trDif ) ;
oldSaberEnd [ 2 ] = oldSaberStart [ 2 ] - ( oldSaberDif [ 2 ] * trDif ) ;
trap_Trace ( & tr , saberEnd , saberTrMins , saberTrMaxs , saberStart , self - > s . number , trMask ) ;
VectorCopy ( saberEnd , lastValidStart ) ;
VectorCopy ( saberStart , lastValidEnd ) ;
if ( tr . entityNum < MAX_CLIENTS )
{
G_G2TraceCollide ( & tr , lastValidStart , lastValidEnd , saberTrMins , saberTrMaxs ) ;
}
else if ( tr . entityNum < ENTITYNUM_WORLD )
{
gentity_t * trHit = & g_entities [ tr . entityNum ] ;
if ( trHit - > inuse & & trHit - > ghoul2 )
{ //hit a non-client entity with a g2 instance
G_G2TraceCollide ( & tr , lastValidStart , lastValidEnd , saberTrMins , saberTrMaxs ) ;
}
}
traceTests + + ;
trDif + = 8 ;
}
}
else
{
vec3_t saberEndExtrapolated ;
if ( extrapolate )
{ //extrapolate 16
vec3_t diff ;
VectorSubtract ( saberEnd , saberStart , diff ) ;
VectorNormalize ( diff ) ;
VectorMA ( saberStart , SABER_EXTRAPOLATE_DIST , diff , saberEndExtrapolated ) ;
}
else
{
VectorCopy ( saberEnd , saberEndExtrapolated ) ;
}
trap_Trace ( & tr , saberStart , saberTrMins , saberTrMaxs , saberEndExtrapolated , self - > s . number , trMask ) ;
VectorCopy ( saberStart , lastValidStart ) ;
VectorCopy ( saberEndExtrapolated , lastValidEnd ) ;
/*
if ( tr . allsolid | | tr . startsolid )
{
if ( tr . entityNum = = ENTITYNUM_NONE )
{
qboolean whah = qtrue ;
}
Com_Printf ( " saber trace start/all solid - ent is %d \n " , tr . entityNum ) ;
}
*/
if ( tr . entityNum < MAX_CLIENTS )
{
G_G2TraceCollide ( & tr , lastValidStart , lastValidEnd , saberTrMins , saberTrMaxs ) ;
}
else if ( tr . entityNum < ENTITYNUM_WORLD )
{
gentity_t * trHit = & g_entities [ tr . entityNum ] ;
if ( trHit - > inuse & & trHit - > ghoul2 )
{ //hit a non-client entity with a g2 instance
G_G2TraceCollide ( & tr , lastValidStart , lastValidEnd , saberTrMins , saberTrMaxs ) ;
}
}
}
saberTraceDone = qtrue ;
}
if ( self - > client - > ps . saberAttackWound < level . time
& & ( SaberAttacking ( self )
| | BG_SuperBreakWinAnim ( self - > client - > ps . torsoAnim )
| | ( d_saberSPStyleDamage . integer & & self - > client - > ps . saberInFlight & & rSaberNum = = 0 )
| | ( WP_SaberBladeDoTransitionDamage ( & self - > client - > saber [ rSaberNum ] , rBladeNum ) & & BG_SaberInTransitionAny ( self - > client - > ps . saberMove ) )
| | ( self - > client - > ps . m_iVehicleNum & & self - > client - > ps . saberMove > LS_READY ) )
)
{ //this animation is that of the last attack movement, and so it should do full damage
qboolean saberInSpecial = BG_SaberInSpecial ( self - > client - > ps . saberMove ) ;
qboolean inBackAttack = G_SaberInBackAttack ( self - > client - > ps . saberMove ) ;
if ( d_saberSPStyleDamage . integer )
{
float fDmg = 0.0f ;
if ( self - > client - > ps . saberInFlight )
{
gentity_t * saberEnt = & g_entities [ self - > client - > ps . saberEntityNum ] ;
if ( ! saberEnt
| | ! saberEnt - > s . saberInFlight )
{ //does less damage on the way back
fDmg = 1.0f ;
attackStr = FORCE_LEVEL_0 ;
}
else
{
fDmg = 2.5f * self - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] ;
attackStr = FORCE_LEVEL_1 ;
}
}
else
{
attackStr = G_PowerLevelForSaberAnim ( self , rSaberNum , qfalse ) ;
if ( g_saberRealisticCombat . integer )
{
switch ( attackStr )
{
default :
case FORCE_LEVEL_3 :
fDmg = 10.0f ;
break ;
case FORCE_LEVEL_2 :
fDmg = 5.0f ;
break ;
case FORCE_LEVEL_1 :
case FORCE_LEVEL_0 :
fDmg = 2.5f ;
break ;
}
}
else
{
if ( self - > client - > ps . torsoAnim = = BOTH_SPINATTACK6
| | self - > client - > ps . torsoAnim = = BOTH_SPINATTACK7 )
{ //too easy to do, lower damage
fDmg = 2.5f ;
}
else
{
fDmg = 2.5f * ( float ) attackStr ;
}
}
}
if ( g_saberRealisticCombat . integer > 1 )
{ //always do damage, and lots of it
if ( g_saberRealisticCombat . integer > 2 )
{ //always do damage, and lots of it
fDmg = 25.0f ;
}
else if ( fDmg > 0.1f )
{ //only do super damage if we would have done damage according to normal rules
fDmg = 25.0f ;
}
}
/*
if ( dmg > 0.1f )
{
if ( ( self - > client - > ps . forcePowersActive & ( 1 < < FP_RAGE ) ) )
{ //add some damage if raged
dmg + = self - > client - > ps . forcePowerLevel [ FP_RAGE ] * 5.0f ;
}
else if ( self - > client - > ps . forceRageRecoveryTime )
{ //halve it if recovering
dmg * = 0.5f ;
}
}
*/
if ( g_gametype . integer ! = GT_DUEL
& & g_gametype . integer ! = GT_POWERDUEL
& & g_gametype . integer ! = GT_SIEGE )
{ //in faster-paced games, sabers do more damage
fDmg * = 2.0f ;
}
if ( fDmg )
{ //the longer the trace, the more damage it does
//FIXME: in SP, we only use the part of the trace that's actually *inside* the hit ent...
float traceLength = Distance ( saberEnd , saberStart ) ;
if ( tr . fraction > = 1.0f )
{ //allsolid?
dmg = ceil ( fDmg * traceLength * 0.1f * 0.33f ) ;
}
else
{ //fractional hit, the sooner you hit in the trace, the more damage you did
dmg = ceil ( fDmg * traceLength * ( 1.0f - tr . fraction ) * 0.1f * 0.33f ) ; //(1.0f-tr.fraction) isn't really accurate, but kind of simulates what we have in SP
}
# ifdef DEBUG_SABER_BOX
if ( g_saberDebugBox . integer = = 3 | | g_saberDebugBox . integer = = 4 )
{
G_TestLine ( saberStart , saberEnd , 0x0000ff , 50 ) ;
}
# endif
}
/*
if ( dmg )
{
Com_Printf ( " CL %i SABER DMG: %i, anim %s, torsoTimer %i \n " , self - > s . number , dmg , animTable [ self - > client - > ps . torsoAnim ] . name , self - > client - > ps . torsoTimer ) ;
}
*/
if ( self - > client - > ps . torsoAnim = = BOTH_A1_SPECIAL
| | self - > client - > ps . torsoAnim = = BOTH_A2_SPECIAL
| | self - > client - > ps . torsoAnim = = BOTH_A3_SPECIAL )
{ //parry/block/break-parry bonus for single-style kata moves
attackStr + + ;
}
if ( BG_SuperBreakWinAnim ( self - > client - > ps . torsoAnim ) )
{
trMask & = ~ CONTENTS_LIGHTSABER ;
}
}
else
{
dmg = SABER_HITDAMAGE ;
if ( self - > client - > ps . fd . saberAnimLevel = = SS_STAFF | |
self - > client - > ps . fd . saberAnimLevel = = SS_DUAL )
{
if ( saberInSpecial )
{
//it will get auto-ramped based on the point in the attack, later on
if ( self - > client - > ps . saberMove = = LS_SPINATTACK | |
self - > client - > ps . saberMove = = LS_SPINATTACK_DUAL )
{ //these attacks are long and have the potential to hit a lot so they will do less damage.
dmg = 10 ;
}
else
{
if ( BG_KickingAnim ( self - > client - > ps . legsAnim ) | |
BG_KickingAnim ( self - > client - > ps . torsoAnim ) )
{ //saber shouldn't do more than min dmg during kicks
dmg = 2 ;
}
else if ( BG_SaberInKata ( self - > client - > ps . saberMove ) )
{ //special kata move
if ( self - > client - > ps . fd . saberAnimLevel = = SS_DUAL )
{ //this is the nasty saber twirl, do big damage cause it makes you vulnerable
dmg = 90 ;
}
else
{ //staff kata
dmg = G_GetAttackDamage ( self , 60 , 70 , 0.5f ) ;
}
}
else
{
//dmg = 90;
//ramp from 2 to 90 by default for other specials
dmg = G_GetAttackDamage ( self , 2 , 90 , 0.5f ) ;
}
}
}
else
{ //otherwise we'll ramp up to 70 I guess, for both dual and staff
dmg = G_GetAttackDamage ( self , 2 , 70 , 0.5f ) ;
}
}
else if ( self - > client - > ps . fd . saberAnimLevel = = 3 )
{
//new damage-ramping system
if ( ! saberInSpecial & & ! inBackAttack )
{
dmg = G_GetAttackDamage ( self , 2 , 120 , 0.5f ) ;
}
else if ( saberInSpecial & &
( self - > client - > ps . saberMove = = LS_A_JUMP_T__B_ ) )
{
dmg = G_GetAttackDamage ( self , 2 , 180 , 0.65f ) ;
}
else if ( inBackAttack )
{
dmg = G_GetAttackDamage ( self , 2 , 30 , 0.5f ) ; //can hit multiple times (and almost always does), so..
}
else
{
dmg = 100 ;
}
}
else if ( self - > client - > ps . fd . saberAnimLevel = = 2 )
{
if ( saberInSpecial & &
( self - > client - > ps . saberMove = = LS_A_FLIP_STAB | | self - > client - > ps . saberMove = = LS_A_FLIP_SLASH ) )
{ //a well-timed hit with this can do a full 85
dmg = G_GetAttackDamage ( self , 2 , 80 , 0.5f ) ;
}
else if ( inBackAttack )
{
dmg = G_GetAttackDamage ( self , 2 , 25 , 0.5f ) ;
}
else
{
dmg = 60 ;
}
}
else if ( self - > client - > ps . fd . saberAnimLevel = = 1 )
{
if ( saberInSpecial & &
( self - > client - > ps . saberMove = = LS_A_LUNGE ) )
{
dmg = G_GetAttackDamage ( self , 2 , SABER_HITDAMAGE - 5 , 0.3f ) ;
}
else if ( inBackAttack )
{
dmg = G_GetAttackDamage ( self , 2 , 30 , 0.5f ) ;
}
else
{
dmg = SABER_HITDAMAGE ;
}
}
attackStr = self - > client - > ps . fd . saberAnimLevel ;
}
}
else if ( self - > client - > ps . saberAttackWound < level . time & &
self - > client - > ps . saberIdleWound < level . time )
{ //just touching, do minimal damage and only check for it every 200ms (mainly to cut down on network traffic for hit events)
if ( ( self - > client - > saber [ 0 ] . saberFlags2 & SFL2_NO_IDLE_EFFECT ) )
{ //no idle damage or effects
return qtrue ; //true cause even though we didn't get a hit, we don't want to do those extra traces because the debounce time says not to.
}
trMask & = ~ CONTENTS_LIGHTSABER ;
if ( d_saberSPStyleDamage . integer )
{
if ( BG_SaberInReturn ( self - > client - > ps . saberMove ) )
{
dmg = SABER_NONATTACK_DAMAGE ;
}
else
{
if ( d_saberSPStyleDamage . integer = = 2 )
{
dmg = SABER_NONATTACK_DAMAGE ;
}
else
{
dmg = 0 ;
}
}
}
else
{
dmg = SABER_NONATTACK_DAMAGE ;
}
idleDamage = qtrue ;
}
else
{
return qtrue ; //true cause even though we didn't get a hit, we don't want to do those extra traces because the debounce time says not to.
}
if ( BG_SaberInSpecial ( self - > client - > ps . saberMove ) )
{
qboolean inBackAttack = G_SaberInBackAttack ( self - > client - > ps . saberMove ) ;
unblockable = qtrue ;
self - > client - > ps . saberBlocked = 0 ;
if ( d_saberSPStyleDamage . integer )
{
}
else if ( ! inBackAttack )
{
if ( self - > client - > ps . saberMove = = LS_A_JUMP_T__B_ )
{ //do extra damage for special unblockables
dmg + = 5 ; //This is very tiny, because this move has a huge damage ramp
}
else if ( self - > client - > ps . saberMove = = LS_A_FLIP_STAB | | self - > client - > ps . saberMove = = LS_A_FLIP_SLASH )
{
dmg + = 5 ; //ditto
if ( dmg < = 40 | | G_GetAnimPoint ( self ) < = 0.4f )
{ //sort of a hack, don't want it doing big damage in the off points of the anim
dmg = 2 ;
}
}
else if ( self - > client - > ps . saberMove = = LS_A_LUNGE )
{
dmg + = 2 ; //and ditto again
if ( G_GetAnimPoint ( self ) < = 0.4f )
{ //same as above
dmg = 2 ;
}
}
else if ( self - > client - > ps . saberMove = = LS_SPINATTACK | |
self - > client - > ps . saberMove = = LS_SPINATTACK_DUAL )
{ //do a constant significant amount of damage but ramp up a little to the mid-point
dmg = G_GetAttackDamage ( self , 2 , dmg + 3 , 0.5f ) ;
dmg + = 10 ;
}
else
{
//dmg += 20;
if ( BG_KickingAnim ( self - > client - > ps . legsAnim ) | |
BG_KickingAnim ( self - > client - > ps . torsoAnim ) )
{ //saber shouldn't do more than min dmg during kicks
dmg = 2 ;
}
else
{ //auto-ramp it I guess since it's a special we don't have a special case for.
dmg = G_GetAttackDamage ( self , 5 , dmg + 5 , 0.5f ) ;
}
}
}
}
if ( ! dmg )
{
if ( tr . entityNum < MAX_CLIENTS | |
( g_entities [ tr . entityNum ] . inuse & & ( g_entities [ tr . entityNum ] . r . contents & CONTENTS_LIGHTSABER ) ) )
{
return qtrue ;
}
return qfalse ;
}
if ( dmg > SABER_NONATTACK_DAMAGE )
{
dmg * = g_saberDamageScale . value ;
//see if this specific saber has a damagescale
if ( ! WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum )
& & self - > client - > saber [ rSaberNum ] . damageScale ! = 1.0f )
{
dmg = ceil ( ( float ) dmg * self - > client - > saber [ rSaberNum ] . damageScale ) ;
}
else if ( WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum )
& & self - > client - > saber [ rSaberNum ] . damageScale2 ! = 1.0f )
{
dmg = ceil ( ( float ) dmg * self - > client - > saber [ rSaberNum ] . damageScale2 ) ;
}
if ( ( self - > client - > ps . brokenLimbs & ( 1 < < BROKENLIMB_RARM ) ) | |
( self - > client - > ps . brokenLimbs & ( 1 < < BROKENLIMB_LARM ) ) )
{ //weaken it if an arm is broken
dmg * = 0.3 ;
if ( dmg < = SABER_NONATTACK_DAMAGE )
{
dmg = SABER_NONATTACK_DAMAGE + 1 ;
}
}
}
if ( dmg > SABER_NONATTACK_DAMAGE & & self - > client - > ps . isJediMaster )
{ //give the Jedi Master more saber attack power
dmg * = 2 ;
}
if ( dmg > SABER_NONATTACK_DAMAGE & & g_gametype . integer = = GT_SIEGE & &
self - > client - > siegeClass ! = - 1 & & ( bgSiegeClasses [ self - > client - > siegeClass ] . classflags & ( 1 < < CFL_MORESABERDMG ) ) )
{ //this class is flagged to do extra saber damage. I guess 2x will do for now.
dmg * = 2 ;
}
if ( g_gametype . integer = = GT_POWERDUEL & &
self - > client - > sess . duelTeam = = DUELTEAM_LONE )
{ //always x2 when we're powerdueling alone... er, so, we apparently no longer want this? So they say.
if ( g_duel_fraglimit . integer )
{
//dmg *= 1.5 - (.4 * (float)self->client->sess.wins / (float)g_duel_fraglimit.integer);
}
//dmg *= 2;
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer > 2 & & dmg > 1 )
{
Com_Printf ( " CL %i SABER DMG: %i \n " , self - > s . number , dmg ) ;
}
# endif
VectorSubtract ( saberEnd , saberStart , dir ) ;
VectorNormalize ( dir ) ;
if ( tr . entityNum = = ENTITYNUM_WORLD | |
g_entities [ tr . entityNum ] . s . eType = = ET_TERRAIN )
{ //register this as a wall hit for jedi AI
self - > client - > ps . saberEventFlags | = SEF_HITWALL ;
saberHitWall = qtrue ;
}
if ( saberHitWall
& & ( self - > client - > saber [ rSaberNum ] . saberFlags & SFL_BOUNCE_ON_WALLS )
& & ( BG_SaberInAttackPure ( self - > client - > ps . saberMove ) //only in a normal attack anim
| | self - > client - > ps . saberMove = = LS_A_JUMP_T__B_ ) //or in the strong jump-fwd-attack "death from above" move
)
{ //then bounce off
/*
qboolean onlyIfNotSpecial = qfalse ;
qboolean skipIt = qfalse ;
if ( tr . plane . normal [ 2 ] > = 0.8f | |
tr . plane . normal [ 2 ] < = - 0.8f | |
VectorCompare ( tr . plane . normal , vec3_origin ) )
{
if ( ( tr . plane . normal [ 2 ] > = 0.8f | | VectorCompare ( tr . plane . normal , vec3_origin ) ) & &
self - > client - > ps . viewangles [ PITCH ] < = 30.0f & &
self - > client - > pers . cmd . upmove > = 0 )
{ //don't hit the ground if we are not looking down a lot/crouched
skipIt = qtrue ;
}
else
{
onlyIfNotSpecial = qtrue ;
}
}
if ( ! skipIt & & ( ! onlyIfNotSpecial | | ! BG_SaberInSpecial ( self - > client - > ps . saberMove ) ) )
*/
{
gentity_t * te = NULL ;
/*
qboolean pre = saberDoClashEffect ;
VectorCopy ( tr . endpos , saberClashPos ) ;
VectorCopy ( tr . plane . normal , saberClashNorm ) ;
saberClashEventParm = 1 ;
saberDoClashEffect = qtrue ;
WP_SaberDoClash ( self , rSaberNum , rBladeNum ) ;
saberDoClashEffect = pre ;
*/
self - > client - > ps . saberMove = BG_BrokenParryForAttack ( self - > client - > ps . saberMove ) ;
self - > client - > ps . saberBlocked = BLOCKED_PARRY_BROKEN ;
if ( self - > client - > ps . torsoAnim = = self - > client - > ps . legsAnim )
{ //set anim now on both parts
int anim = saberMoveData [ self - > client - > ps . saberMove ] . animToUse ;
G_SetAnim ( self , & self - > client - > pers . cmd , SETANIM_BOTH , anim , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
}
//do bounce sound & force feedback
WP_SaberBounceSound ( self , rSaberNum , rBladeNum ) ;
//do hit effect
te = G_TempEntity ( tr . endpos , EV_SABER_HIT ) ;
te - > s . otherEntityNum = ENTITYNUM_NONE ; //we didn't hit anyone in particular
te - > s . otherEntityNum2 = self - > s . number ; //send this so it knows who we are
te - > s . weapon = rSaberNum ;
te - > s . legsAnim = rBladeNum ;
VectorCopy ( tr . endpos , te - > s . origin ) ;
VectorCopy ( tr . plane . normal , te - > s . angles ) ;
if ( ! te - > s . angles [ 0 ] & & ! te - > s . angles [ 1 ] & & ! te - > s . angles [ 2 ] )
{ //don't let it play with no direction
te - > s . angles [ 1 ] = 1 ;
}
//do radius damage/knockback, if any
if ( ! WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum ) )
{
WP_SaberRadiusDamage ( self , tr . endpos , self - > client - > saber [ rSaberNum ] . splashRadius , self - > client - > saber [ rSaberNum ] . splashDamage , self - > client - > saber [ rSaberNum ] . splashKnockback ) ;
}
else
{
WP_SaberRadiusDamage ( self , tr . endpos , self - > client - > saber [ rSaberNum ] . splashRadius2 , self - > client - > saber [ rSaberNum ] . splashDamage2 , self - > client - > saber [ rSaberNum ] . splashKnockback2 ) ;
}
return qtrue ;
}
}
//rww - I'm saying || tr.startsolid here, because otherwise your saber tends to skip positions and go through
//people, and the compensation traces start in their bbox too. Which results in the saber passing through people
//when you visually cut right through them. Which sucks.
if ( ( tr . fraction ! = 1 | | tr . startsolid ) & &
g_entities [ tr . entityNum ] . takedamage & &
( g_entities [ tr . entityNum ] . health > 0 | | ! ( g_entities [ tr . entityNum ] . s . eFlags & EF_DISINTEGRATION ) ) & &
tr . entityNum ! = self - > s . number & &
g_entities [ tr . entityNum ] . inuse )
{ //hit something that had health and takes damage
if ( idleDamage & &
g_entities [ tr . entityNum ] . client & &
OnSameTeam ( self , & g_entities [ tr . entityNum ] ) & &
! g_friendlySaber . integer )
{
return qfalse ;
}
if ( g_entities [ tr . entityNum ] . client & &
g_entities [ tr . entityNum ] . client - > ps . duelInProgress & &
g_entities [ tr . entityNum ] . client - > ps . duelIndex ! = self - > s . number )
{
return qfalse ;
}
if ( g_entities [ tr . entityNum ] . client & &
self - > client - > ps . duelInProgress & &
self - > client - > ps . duelIndex ! = g_entities [ tr . entityNum ] . s . number )
{
return qfalse ;
}
if ( BG_StabDownAnim ( self - > client - > ps . torsoAnim )
& & g_entities [ tr . entityNum ] . client
& & ! BG_InKnockDownOnGround ( & g_entities [ tr . entityNum ] . client - > ps ) )
{ //stabdowns only damage people who are actually on the ground...
return qfalse ;
}
self - > client - > ps . saberIdleWound = level . time + g_saberDmgDelay_Idle . integer ;
didHit = qtrue ;
if ( ! d_saberSPStyleDamage . integer //let's trying making blocks have to be blocked by a saber
& & g_entities [ tr . entityNum ] . client
& & ! unblockable
& & WP_SaberCanBlock ( & g_entities [ tr . entityNum ] , tr . endpos , 0 , MOD_SABER , qfalse , attackStr ) )
{ //hit a client who blocked the attack (fake: didn't actually hit their saber)
if ( dmg < = SABER_NONATTACK_DAMAGE )
{
self - > client - > ps . saberIdleWound = level . time + g_saberDmgDelay_Idle . integer ;
}
saberDoClashEffect = qtrue ;
VectorCopy ( tr . endpos , saberClashPos ) ;
VectorCopy ( tr . plane . normal , saberClashNorm ) ;
saberClashEventParm = 1 ;
if ( dmg > SABER_NONATTACK_DAMAGE )
{
int lockFactor = g_saberLockFactor . integer ;
if ( ( g_entities [ tr . entityNum ] . client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] - self - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] ) > 1 & &
Q_irand ( 1 , 10 ) < lockFactor * 2 )
{ //Just got blocked by someone with a decently higher attack level, so enter into a lock (where they have the advantage due to a higher attack lev)
if ( ! G_ClientIdleInWorld ( & g_entities [ tr . entityNum ] ) )
{
if ( ( trMask & CONTENTS_LIGHTSABER )
& & WP_SabersCheckLock ( self , & g_entities [ tr . entityNum ] ) )
{
self - > client - > ps . saberBlocked = BLOCKED_NONE ;
g_entities [ tr . entityNum ] . client - > ps . saberBlocked = BLOCKED_NONE ;
return didHit ;
}
}
}
else if ( Q_irand ( 1 , 20 ) < lockFactor )
{
if ( ! G_ClientIdleInWorld ( & g_entities [ tr . entityNum ] ) )
{
if ( ( trMask & CONTENTS_LIGHTSABER )
& & WP_SabersCheckLock ( self , & g_entities [ tr . entityNum ] ) )
{
self - > client - > ps . saberBlocked = BLOCKED_NONE ;
g_entities [ tr . entityNum ] . client - > ps . saberBlocked = BLOCKED_NONE ;
return didHit ;
}
}
}
}
otherOwner = & g_entities [ tr . entityNum ] ;
goto blockStuff ;
}
else
{ //damage the thing we hit
qboolean doDismemberment = qfalse ;
int knockbackFlags = 0 ;
if ( g_entities [ tr . entityNum ] . client )
{ //not a "jedi", so make them suffer more
if ( dmg > SABER_NONATTACK_DAMAGE )
{ //don't bother increasing just for idle touch damage
dmg * = 1.5 ;
}
}
/*
if ( g_entities [ tr . entityNum ] . client
& & g_entities [ tr . entityNum ] . client - > ps . weapon ! = WP_SABER ) //fd.forcePowerLevel[FP_SABER_OFFENSE])
{ //not a "jedi", so make them suffer more
if ( dmg > SABER_NONATTACK_DAMAGE )
{ //don't bother increasing just for idle touch damage
dmg * = 1.5 ;
}
}
*/
if ( ! d_saberSPStyleDamage . integer )
{
if ( g_entities [ tr . entityNum ] . client & & g_entities [ tr . entityNum ] . client - > ps . weapon = = WP_SABER )
{ //for jedi using the saber, half the damage (this comes with the increased default dmg debounce time)
if ( g_gametype . integer ! = GT_SIEGE )
{ //unless siege..
if ( dmg > SABER_NONATTACK_DAMAGE & & ! unblockable )
{ //don't reduce damage if it's only 1, or if this is an unblockable attack
if ( dmg = = SABER_HITDAMAGE )
{ //level 1 attack
dmg * = 0.7 ;
}
else
{
dmg * = 0.5 ;
}
}
}
}
}
if ( self - > s . eType = = ET_NPC & &
g_entities [ tr . entityNum ] . client & &
self - > client - > playerTeam = = g_entities [ tr . entityNum ] . client - > playerTeam )
{ //Oops. Since he's an NPC, we'll be forgiving and cut the damage down.
dmg * = 0.2f ;
}
//store the damage, we'll apply it later
if ( ! WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum )
& & ! ( self - > client - > saber [ rSaberNum ] . saberFlags2 & SFL2_NO_DISMEMBERMENT ) )
{
doDismemberment = qtrue ;
}
if ( WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum )
& & ! ( self - > client - > saber [ rSaberNum ] . saberFlags2 & SFL2_NO_DISMEMBERMENT ) )
{
doDismemberment = qtrue ;
}
if ( ! WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum )
& & self - > client - > saber [ rSaberNum ] . knockbackScale > 0.0f )
{
if ( rSaberNum < 1 )
{
knockbackFlags = DAMAGE_SABER_KNOCKBACK1 ;
}
else
{
knockbackFlags = DAMAGE_SABER_KNOCKBACK2 ;
}
}
if ( WP_SaberBladeUseSecondBladeStyle ( & self - > client - > saber [ rSaberNum ] , rBladeNum )
& & self - > client - > saber [ rSaberNum ] . knockbackScale > 0.0f )
{
if ( rSaberNum < 1 )
{
knockbackFlags = DAMAGE_SABER_KNOCKBACK1_B2 ;
}
else
{
knockbackFlags = DAMAGE_SABER_KNOCKBACK2_B2 ;
}
}
WP_SaberDamageAdd ( tr . entityNum , dir , tr . endpos , dmg , doDismemberment , knockbackFlags ) ;
if ( g_entities [ tr . entityNum ] . client )
{
//Let jedi AI know if it hit an enemy
if ( self - > enemy & & self - > enemy = = & g_entities [ tr . entityNum ] )
{
self - > client - > ps . saberEventFlags | = SEF_HITENEMY ;
}
else
{
self - > client - > ps . saberEventFlags | = SEF_HITOBJECT ;
}
}
if ( d_saberSPStyleDamage . integer )
{
}
else
{
self - > client - > ps . saberAttackWound = level . time + 100 ;
}
}
}
else if ( ( tr . fraction ! = 1 | | tr . startsolid ) & &
( g_entities [ tr . entityNum ] . r . contents & CONTENTS_LIGHTSABER ) & &
g_entities [ tr . entityNum ] . r . contents ! = - 1 & &
g_entities [ tr . entityNum ] . inuse )
{ //saber clash
otherOwner = & g_entities [ g_entities [ tr . entityNum ] . r . ownerNum ] ;
if ( ! otherOwner - > inuse | | ! otherOwner - > client )
{
return qfalse ;
}
if ( otherOwner
& & otherOwner - > client
& & otherOwner - > client - > ps . saberInFlight )
{ //don't do extra collision checking vs sabers in air
}
else
{ //hit an in-hand saber, do extra collision check against it
if ( d_saberSPStyleDamage . integer )
{ //use SP-style blade-collision test
if ( ! WP_SabersIntersect ( self , rSaberNum , rBladeNum , otherOwner , qfalse ) )
{ //sabers did not actually intersect
return qfalse ;
}
}
else
{ //MP-style
if ( ! G_SaberCollide ( self , otherOwner , lastValidStart ,
lastValidEnd , saberTrMins , saberTrMaxs , tr . endpos ) )
{ //detailed collision did not produce results...
return qfalse ;
}
}
}
if ( OnSameTeam ( self , otherOwner ) & &
! g_friendlySaber . integer )
{
return qfalse ;
}
if ( ( self - > s . eType = = ET_NPC | | otherOwner - > s . eType = = ET_NPC ) & & //just make sure one of us is an npc
self - > client - > playerTeam = = otherOwner - > client - > playerTeam & &
g_gametype . integer ! = GT_SIEGE )
{ //don't hit your teammate's sabers if you are an NPC. It can be rather annoying.
return qfalse ;
}
if ( otherOwner - > client - > ps . duelInProgress & &
otherOwner - > client - > ps . duelIndex ! = self - > s . number )
{
return qfalse ;
}
if ( self - > client - > ps . duelInProgress & &
self - > client - > ps . duelIndex ! = otherOwner - > s . number )
{
return qfalse ;
}
if ( g_debugSaberLocks . integer )
{
WP_SabersCheckLock2 ( self , otherOwner , LOCK_RANDOM ) ;
return qtrue ;
}
didHit = qtrue ;
self - > client - > ps . saberIdleWound = level . time + g_saberDmgDelay_Idle . integer ;
if ( dmg < = SABER_NONATTACK_DAMAGE )
{
self - > client - > ps . saberIdleWound = level . time + g_saberDmgDelay_Idle . integer ;
}
saberDoClashEffect = qtrue ;
VectorCopy ( tr . endpos , saberClashPos ) ;
VectorCopy ( tr . plane . normal , saberClashNorm ) ;
saberClashEventParm = 1 ;
sabersClashed = qtrue ;
saberHitSaber = qtrue ;
saberHitFraction = tr . fraction ;
if ( saberCheckKnockdown_Smashed ( & g_entities [ tr . entityNum ] , otherOwner , self , dmg ) )
{ //smashed it out of the air
return qfalse ;
}
//is this my thrown saber?
if ( self - > client - > ps . saberEntityNum
& & self - > client - > ps . saberInFlight
& & rSaberNum = = 0
& & saberCheckKnockdown_Smashed ( & g_entities [ self - > client - > ps . saberEntityNum ] , self , otherOwner , dmg ) )
{ //they smashed it out of the air
return qfalse ;
}
blockStuff :
otherUnblockable = qfalse ;
if ( otherOwner & & otherOwner - > client & & otherOwner - > client - > ps . saberInFlight )
{
return qfalse ;
}
//this is a thrown saber, don't do any fancy saber-saber collision stuff
if ( self - > client - > ps . saberEntityNum
& & self - > client - > ps . saberInFlight
& & rSaberNum = = 0 )
{
return qfalse ;
}
otherSaberLevel = G_SaberAttackPower ( otherOwner , SaberAttacking ( otherOwner ) ) ;
if ( dmg > SABER_NONATTACK_DAMAGE & & ! unblockable & & ! otherUnblockable )
{
int lockFactor = g_saberLockFactor . integer ;
if ( sabersClashed & & Q_irand ( 1 , 20 ) < = lockFactor )
{
if ( ! G_ClientIdleInWorld ( otherOwner ) )
{
if ( WP_SabersCheckLock ( self , otherOwner ) )
{
self - > client - > ps . saberBlocked = BLOCKED_NONE ;
otherOwner - > client - > ps . saberBlocked = BLOCKED_NONE ;
return didHit ;
}
}
}
}
if ( ! otherOwner | | ! otherOwner - > client )
{
return didHit ;
}
if ( BG_SaberInSpecial ( otherOwner - > client - > ps . saberMove ) )
{
otherUnblockable = qtrue ;
otherOwner - > client - > ps . saberBlocked = 0 ;
}
if ( sabersClashed & &
dmg > SABER_NONATTACK_DAMAGE & &
selfSaberLevel < FORCE_LEVEL_3 & &
! PM_SaberInBounce ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInParry ( self - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( self - > client - > ps . saberMove ) & &
! BG_SaberInSpecial ( self - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( self - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( self - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( self - > client - > ps . saberMove ) & &
! unblockable )
{
//if (Q_irand(1, 10) <= 6)
if ( 1 ) //for now, just always try a deflect. (deflect func can cause bounces too)
{
if ( ! WP_GetSaberDeflectionAngle ( self , otherOwner , tr . fraction ) )
{
tryDeflectAgain = qtrue ; //Failed the deflect, try it again if we can if the guy we're smashing goes into a parry and we don't break it
}
else
{
self - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
didOffense = qtrue ;
}
}
else
{
self - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
didOffense = qtrue ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i clashed into client %i's saber, did BLOCKED_ATK_BOUNCE \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
}
}
if ( ( ( selfSaberLevel < FORCE_LEVEL_3 & & ( ( tryDeflectAgain & & Q_irand ( 1 , 10 ) < = 3 ) | | ( ! tryDeflectAgain & & Q_irand ( 1 , 10 ) < = 7 ) ) ) | | ( Q_irand ( 1 , 10 ) < = 1 & & otherSaberLevel > = FORCE_LEVEL_3 ) )
& & ! PM_SaberInBounce ( self - > client - > ps . saberMove )
& & ! PM_SaberInBrokenParry ( otherOwner - > client - > ps . saberMove )
& & ! BG_SaberInSpecial ( otherOwner - > client - > ps . saberMove )
& & ! PM_SaberInBounce ( otherOwner - > client - > ps . saberMove )
& & ! PM_SaberInDeflect ( otherOwner - > client - > ps . saberMove )
& & ! PM_SaberInReflect ( otherOwner - > client - > ps . saberMove )
& & ( otherSaberLevel > FORCE_LEVEL_2 | | ( otherOwner - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] > = 3 & & Q_irand ( 0 , otherSaberLevel ) ) )
& & ! unblockable
& & ! otherUnblockable
& & dmg > SABER_NONATTACK_DAMAGE
& & ! didOffense ) //don't allow the person we're attacking to do this if we're making an unblockable attack
{ //knockaways can make fast-attacker go into a broken parry anim if the ent is using fast or med. In MP, we also randomly decide this for level 3 attacks.
//Going to go ahead and let idle damage do simple knockaways. Looks sort of good that way.
//turn the parry into a knockaway
if ( self - > client - > ps . saberEntityNum ) //make sure he has his saber still
{
saberCheckKnockdown_BrokenParry ( & g_entities [ self - > client - > ps . saberEntityNum ] , self , otherOwner ) ;
}
if ( ! PM_SaberInParry ( otherOwner - > client - > ps . saberMove ) )
{
WP_SaberBlockNonRandom ( otherOwner , tr . endpos , qfalse ) ;
otherOwner - > client - > ps . saberMove = BG_KnockawayForParry ( otherOwner - > client - > ps . saberBlocked ) ;
otherOwner - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
}
else
{
otherOwner - > client - > ps . saberMove = G_KnockawayForParry ( otherOwner - > client - > ps . saberMove ) ; //BG_KnockawayForParry( otherOwner->client->ps.saberBlocked );
otherOwner - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
}
//make them (me) go into a broken parry
self - > client - > ps . saberMove = BG_BrokenParryForAttack ( self - > client - > ps . saberMove ) ;
self - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i sent client %i into a reflected attack with a knockaway \n " , otherOwner - > s . number , self - > s . number ) ;
}
# endif
didDefense = qtrue ;
}
else if ( ( selfSaberLevel > FORCE_LEVEL_2 | | unblockable ) & & //if we're doing a special attack, we can send them into a broken parry too (MP only)
( otherOwner - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] < selfSaberLevel | | ( otherOwner - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] = = selfSaberLevel & & ( Q_irand ( 1 , 10 ) > = otherSaberLevel * 1.5 | | unblockable ) ) ) & &
PM_SaberInParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInParry ( self - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( self - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( self - > client - > ps . saberMove ) & &
dmg > SABER_NONATTACK_DAMAGE & &
! didOffense & &
! otherUnblockable )
{ //they are in a parry, and we are slamming down on them with a move of equal or greater force than their defense, so send them into a broken parry.. unless they are already in one.
if ( otherOwner - > client - > ps . saberEntityNum ) //make sure he has his saber still
{
saberCheckKnockdown_BrokenParry ( & g_entities [ otherOwner - > client - > ps . saberEntityNum ] , otherOwner , self ) ;
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i sent client %i into a broken parry \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
otherOwner - > client - > ps . saberMove = BG_BrokenParryForParry ( otherOwner - > client - > ps . saberMove ) ;
otherOwner - > client - > ps . saberBlocked = BLOCKED_PARRY_BROKEN ;
didDefense = qtrue ;
}
else if ( ( selfSaberLevel > FORCE_LEVEL_2 ) & & //if we're doing a special attack, we can send them into a broken parry too (MP only)
//( otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] < selfSaberLevel || (otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] == selfSaberLevel && (Q_irand(1, 10) >= otherSaberLevel*3 || unblockable)) ) &&
otherSaberLevel > = FORCE_LEVEL_3 & &
PM_SaberInParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInParry ( self - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( self - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( self - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( self - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( self - > client - > ps . saberMove ) & &
dmg > SABER_NONATTACK_DAMAGE & &
! didOffense & &
! unblockable )
{ //they are in a parry, and we are slamming down on them with a move of equal or greater force than their defense, so send them into a broken parry.. unless they are already in one.
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i bounced off of client %i's saber \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
if ( ! tryDeflectAgain )
{
if ( ! WP_GetSaberDeflectionAngle ( self , otherOwner , tr . fraction ) )
{
tryDeflectAgain = qtrue ;
}
}
didOffense = qtrue ;
}
else if ( SaberAttacking ( otherOwner ) & & dmg > SABER_NONATTACK_DAMAGE & & ! BG_SaberInSpecial ( otherOwner - > client - > ps . saberMove ) & & ! didOffense & & ! otherUnblockable )
{ //they were attacking and our saber hit their saber, make them bounce. But if they're in a special attack, leave them.
if ( ! PM_SaberInBounce ( self - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( self - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( self - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( otherOwner - > client - > ps . saberMove ) )
{
int attackAdv , defendStr = G_PowerLevelForSaberAnim ( otherOwner , 0 , qtrue ) , attackBonus = 0 ;
if ( otherOwner - > client - > ps . torsoAnim = = BOTH_A1_SPECIAL
| | otherOwner - > client - > ps . torsoAnim = = BOTH_A2_SPECIAL
| | otherOwner - > client - > ps . torsoAnim = = BOTH_A3_SPECIAL )
{ //parry/block/break-parry bonus for single-style kata moves
defendStr + + ;
}
defendStr + = Q_irand ( 0 , otherOwner - > client - > saber [ 0 ] . parryBonus ) ;
if ( otherOwner - > client - > saber [ 1 ] . model
& & otherOwner - > client - > saber [ 1 ] . model [ 0 ]
& & ! otherOwner - > client - > ps . saberHolstered )
{
defendStr + = Q_irand ( 0 , otherOwner - > client - > saber [ 1 ] . parryBonus ) ;
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i and client %i bounced off of each other's sabers \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
attackBonus = Q_irand ( 0 , self - > client - > saber [ 0 ] . breakParryBonus ) ;
if ( self - > client - > saber [ 1 ] . model
& & self - > client - > saber [ 1 ] . model [ 0 ]
& & ! self - > client - > ps . saberHolstered )
{
attackBonus + = Q_irand ( 0 , self - > client - > saber [ 1 ] . breakParryBonus ) ;
}
attackAdv = ( attackStr + attackBonus + self - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] ) - ( defendStr + otherOwner - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] ) ;
if ( attackAdv > 1 )
{ //I won, he should knockaway
otherOwner - > client - > ps . saberMove = BG_BrokenParryForAttack ( otherOwner - > client - > ps . saberMove ) ;
otherOwner - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
}
else if ( attackAdv > 0 )
{ //I won, he should bounce, I should continue
otherOwner - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
}
else if ( attackAdv < 1 )
{ //I lost, I get knocked away
self - > client - > ps . saberMove = BG_BrokenParryForAttack ( self - > client - > ps . saberMove ) ;
self - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
}
else if ( attackAdv < 0 )
{ //I lost, I bounce off
self - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
}
else
{ //even, both bounce off
self - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
otherOwner - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
}
didOffense = qtrue ;
}
}
if ( d_saberGhoul2Collision . integer & & ! didDefense & & dmg < = SABER_NONATTACK_DAMAGE & & ! otherUnblockable ) //with perpoly, it looks pretty weird to have clash flares coming off the guy's face and whatnot
{
if ( ! PM_SaberInParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( otherOwner - > client - > ps . saberMove ) & &
! BG_SaberInSpecial ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( otherOwner - > client - > ps . saberMove ) )
{
WP_SaberBlockNonRandom ( otherOwner , tr . endpos , qfalse ) ;
otherOwner - > client - > ps . saberEventFlags | = SEF_PARRIED ;
}
}
else if ( ! didDefense & & dmg > SABER_NONATTACK_DAMAGE & & ! otherUnblockable ) //if not more than idle damage, don't even bother blocking.
{ //block
if ( ! PM_SaberInParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( otherOwner - > client - > ps . saberMove ) & &
! BG_SaberInSpecial ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( otherOwner - > client - > ps . saberMove ) )
{
qboolean crushTheParry = qfalse ;
if ( unblockable )
{ //It's unblockable. So send us into a broken parry immediately.
crushTheParry = qtrue ;
}
if ( ! SaberAttacking ( otherOwner ) )
{
int otherIdleStr = otherOwner - > client - > ps . fd . saberAnimLevel ;
if ( otherIdleStr = = SS_DUAL
| | otherIdleStr = = SS_STAFF )
{
otherIdleStr = SS_MEDIUM ;
}
WP_SaberBlockNonRandom ( otherOwner , tr . endpos , qfalse ) ;
otherOwner - > client - > ps . saberEventFlags | = SEF_PARRIED ;
self - > client - > ps . saberEventFlags | = SEF_BLOCKED ;
if ( attackStr + self - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] > otherIdleStr + otherOwner - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] )
{
crushTheParry = qtrue ;
}
else
{
tryDeflectAgain = qtrue ;
}
}
else if ( selfSaberLevel > otherSaberLevel | |
( selfSaberLevel = = otherSaberLevel & & Q_irand ( 1 , 10 ) < = 2 ) )
{ //they are attacking, and we managed to make them break
//Give them a parry, so we can later break it.
WP_SaberBlockNonRandom ( otherOwner , tr . endpos , qfalse ) ;
crushTheParry = qtrue ;
if ( otherOwner - > client - > ps . saberEntityNum ) //make sure he has his saber still
{
saberCheckKnockdown_BrokenParry ( & g_entities [ otherOwner - > client - > ps . saberEntityNum ] , otherOwner , self ) ;
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i forced client %i into a broken parry with a stronger attack \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
}
else
{ //They are attacking, so are we, and obviously they have an attack level higher than or equal to ours
if ( selfSaberLevel = = otherSaberLevel )
{ //equal level, try to bounce off each other's sabers
if ( ! didOffense & &
! PM_SaberInParry ( self - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( self - > client - > ps . saberMove ) & &
! BG_SaberInSpecial ( self - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( self - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( self - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( self - > client - > ps . saberMove ) & &
! unblockable )
{
self - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
didOffense = qtrue ;
}
if ( ! didDefense & &
! PM_SaberInParry ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBrokenParry ( otherOwner - > client - > ps . saberMove ) & &
! BG_SaberInSpecial ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInBounce ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInDeflect ( otherOwner - > client - > ps . saberMove ) & &
! PM_SaberInReflect ( otherOwner - > client - > ps . saberMove ) & &
! unblockable )
{
otherOwner - > client - > ps . saberBlocked = BLOCKED_ATK_BOUNCE ;
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Equal attack level bounce/deflection for clients %i and %i \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
self - > client - > ps . saberEventFlags | = SEF_DEFLECTED ;
otherOwner - > client - > ps . saberEventFlags | = SEF_DEFLECTED ;
}
else if ( ( level . time - otherOwner - > client - > lastSaberStorageTime ) < 500 & & ! unblockable ) //make sure the stored saber data is updated
{ //They are higher, this means they can actually smash us into a broken parry
//Using reflected anims instead now
self - > client - > ps . saberMove = BG_BrokenParryForAttack ( self - > client - > ps . saberMove ) ;
self - > client - > ps . saberBlocked = BLOCKED_PARRY_BROKEN ;
if ( self - > client - > ps . saberEntityNum ) //make sure he has his saber still
{
saberCheckKnockdown_BrokenParry ( & g_entities [ self - > client - > ps . saberEntityNum ] , self , otherOwner ) ;
}
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i hit client %i's stronger attack, was forced into a broken parry \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
otherOwner - > client - > ps . saberEventFlags & = ~ SEF_BLOCKED ;
didOffense = qtrue ;
}
}
if ( crushTheParry & & PM_SaberInParry ( G_GetParryForBlock ( otherOwner - > client - > ps . saberBlocked ) ) )
{ //This means that the attack actually hit our saber, and we went to block it.
//But, one of the above cases says we actually can't. So we will be smashed into a broken parry instead.
otherOwner - > client - > ps . saberMove = BG_BrokenParryForParry ( G_GetParryForBlock ( otherOwner - > client - > ps . saberBlocked ) ) ;
otherOwner - > client - > ps . saberBlocked = BLOCKED_PARRY_BROKEN ;
otherOwner - > client - > ps . saberEventFlags & = ~ SEF_PARRIED ;
self - > client - > ps . saberEventFlags & = ~ SEF_BLOCKED ;
# ifndef FINAL_BUILD
if ( g_saberDebugPrint . integer )
{
Com_Printf ( " Client %i broke through %i's parry with a special or stronger attack \n " , self - > s . number , otherOwner - > s . number ) ;
}
# endif
}
else if ( PM_SaberInParry ( G_GetParryForBlock ( otherOwner - > client - > ps . saberBlocked ) ) & & ! didOffense & & tryDeflectAgain )
{ //We want to try deflecting again because the other is in the parry and we haven't made any new moves
int preMove = otherOwner - > client - > ps . saberMove ;
otherOwner - > client - > ps . saberMove = G_GetParryForBlock ( otherOwner - > client - > ps . saberBlocked ) ;
WP_GetSaberDeflectionAngle ( self , otherOwner , tr . fraction ) ;
otherOwner - > client - > ps . saberMove = preMove ;
}
}
}
self - > client - > ps . saberAttackWound = level . time + g_saberDmgDelay_Wound . integer ;
}
return didHit ;
}
GAME_INLINE int VectorCompare2 ( const vec3_t v1 , const vec3_t v2 ) {
if ( v1 [ 0 ] > v2 [ 0 ] + 0.0001f | | v1 [ 0 ] < v2 [ 0 ] - 0.0001f
| | v1 [ 1 ] > v2 [ 1 ] + 0.0001f | | v1 [ 1 ] < v2 [ 1 ] - 0.0001f
| | v1 [ 2 ] > v2 [ 2 ] + 0.0001f | | v1 [ 2 ] < v2 [ 2 ] - 0.0001f ) {
return 0 ;
}
return 1 ;
}
# define MAX_SABER_SWING_INC 0.33f
void G_SPSaberDamageTraceLerped ( gentity_t * self , int saberNum , int bladeNum , vec3_t baseNew , vec3_t endNew , int clipmask )
{
vec3_t baseOld , endOld ;
vec3_t mp1 , mp2 ;
vec3_t md1 , md2 ;
if ( ( level . time - self - > client - > saber [ saberNum ] . blade [ bladeNum ] . trail . lastTime ) > 100 )
{ //no valid last pos, use current
VectorCopy ( baseNew , baseOld ) ;
VectorCopy ( endNew , endOld ) ;
}
else
{ //trace from last pos
VectorCopy ( self - > client - > saber [ saberNum ] . blade [ bladeNum ] . trail . base , baseOld ) ;
VectorCopy ( self - > client - > saber [ saberNum ] . blade [ bladeNum ] . trail . tip , endOld ) ;
}
VectorCopy ( baseOld , mp1 ) ;
VectorCopy ( baseNew , mp2 ) ;
VectorSubtract ( endOld , baseOld , md1 ) ;
VectorNormalize ( md1 ) ;
VectorSubtract ( endNew , baseNew , md2 ) ;
VectorNormalize ( md2 ) ;
saberHitWall = qfalse ;
saberHitSaber = qfalse ;
saberHitFraction = 1.0f ;
if ( VectorCompare2 ( baseOld , baseNew ) & & VectorCompare2 ( endOld , endNew ) )
{ //no diff
CheckSaberDamage ( self , saberNum , bladeNum , baseNew , endNew , qfalse , clipmask , qfalse ) ;
}
else
{ //saber moved, lerp
float step = 8 , stepsize = 8 ; //aveLength,
vec3_t ma1 , ma2 , md2ang , curBase1 , curBase2 ;
int xx ;
vec3_t curMD1 , curMD2 ; //, mdDiff, dirDiff;
float dirInc , curDirFrac ;
vec3_t baseDiff , bladePointOld , bladePointNew ;
qboolean extrapolate = qtrue ;
//do the trace at the base first
VectorCopy ( baseOld , bladePointOld ) ;
VectorCopy ( baseNew , bladePointNew ) ;
CheckSaberDamage ( self , saberNum , bladeNum , bladePointOld , bladePointNew , qfalse , clipmask , qtrue ) ;
//if hit a saber, shorten rest of traces to match
if ( saberHitFraction < 1.0f )
{
//adjust muzzleDir...
vec3_t ma1 , ma2 ;
vectoangles ( md1 , ma1 ) ;
vectoangles ( md2 , ma2 ) ;
for ( xx = 0 ; xx < 3 ; xx + + )
{
md2ang [ xx ] = LerpAngle ( ma1 [ xx ] , ma2 [ xx ] , saberHitFraction ) ;
}
AngleVectors ( md2ang , md2 , NULL , NULL ) ;
//shorten the base pos
VectorSubtract ( mp2 , mp1 , baseDiff ) ;
VectorMA ( mp1 , saberHitFraction , baseDiff , baseNew ) ;
VectorMA ( baseNew , self - > client - > saber [ saberNum ] . blade [ bladeNum ] . lengthMax , md2 , endNew ) ;
}
//If the angle diff in the blade is high, need to do it in chunks of 33 to avoid flattening of the arc
if ( BG_SaberInAttack ( self - > client - > ps . saberMove )
| | BG_SaberInSpecialAttack ( self - > client - > ps . torsoAnim )
| | BG_SpinningSaberAnim ( self - > client - > ps . torsoAnim )
| | BG_InSpecialJump ( self - > client - > ps . torsoAnim ) )
//|| (g_timescale->value<1.0f&&BG_SaberInTransitionAny( ent->client->ps.saberMove )) )
{
curDirFrac = DotProduct ( md1 , md2 ) ;
}
else
{
curDirFrac = 1.0f ;
}
//NOTE: if saber spun at least 180 degrees since last damage trace, this is not reliable...!
if ( fabs ( curDirFrac ) < 1.0f - MAX_SABER_SWING_INC )
{ //the saber blade spun more than 33 degrees since the last damage trace
curDirFrac = dirInc = 1.0f / ( ( 1.0f - curDirFrac ) / MAX_SABER_SWING_INC ) ;
}
else
{
curDirFrac = 1.0f ;
dirInc = 0.0f ;
}
//qboolean hit_saber = qfalse;
vectoangles ( md1 , ma1 ) ;
vectoangles ( md2 , ma2 ) ;
//VectorSubtract( md2, md1, mdDiff );
VectorCopy ( md1 , curMD2 ) ;
VectorCopy ( baseOld , curBase2 ) ;
while ( 1 )
{
VectorCopy ( curMD2 , curMD1 ) ;
VectorCopy ( curBase2 , curBase1 ) ;
if ( curDirFrac > = 1.0f )
{
VectorCopy ( md2 , curMD2 ) ;
VectorCopy ( baseNew , curBase2 ) ;
}
else
{
for ( xx = 0 ; xx < 3 ; xx + + )
{
md2ang [ xx ] = LerpAngle ( ma1 [ xx ] , ma2 [ xx ] , curDirFrac ) ;
}
AngleVectors ( md2ang , curMD2 , NULL , NULL ) ;
//VectorMA( md1, curDirFrac, mdDiff, curMD2 );
VectorSubtract ( baseNew , baseOld , baseDiff ) ;
VectorMA ( baseOld , curDirFrac , baseDiff , curBase2 ) ;
}
// Move up the blade in intervals of stepsize
for ( step = stepsize ; step < = self - > client - > saber [ saberNum ] . blade [ bladeNum ] . lengthMax /*&& step < self->client->saber[saberNum].blade[bladeNum].lengthOld*/ ; step + = stepsize )
{
VectorMA ( curBase1 , step , curMD1 , bladePointOld ) ;
VectorMA ( curBase2 , step , curMD2 , bladePointNew ) ;
if ( step + stepsize > = self - > client - > saber [ saberNum ] . blade [ bladeNum ] . lengthMax )
{
extrapolate = qfalse ;
}
//do the damage trace
CheckSaberDamage ( self , saberNum , bladeNum , bladePointOld , bladePointNew , qfalse , clipmask , extrapolate ) ;
/*
if ( WP_SaberDamageForTrace ( ent - > s . number , bladePointOld , bladePointNew , baseDamage , curMD2 ,
qfalse , entPowerLevel , ent - > client - > ps . saber [ saberNum ] . type , qtrue ,
saberNum , bladeNum ) )
{
hit_wall = qtrue ;
}
*/
//if hit a saber, shorten rest of traces to match
if ( saberHitFraction < 1.0f )
{
vec3_t curMA1 , curMA2 ;
//adjust muzzle endpoint
VectorSubtract ( mp2 , mp1 , baseDiff ) ;
VectorMA ( mp1 , saberHitFraction , baseDiff , baseNew ) ;
VectorMA ( baseNew , self - > client - > saber [ saberNum ] . blade [ bladeNum ] . lengthMax , curMD2 , endNew ) ;
//adjust muzzleDir...
vectoangles ( curMD1 , curMA1 ) ;
vectoangles ( curMD2 , curMA2 ) ;
for ( xx = 0 ; xx < 3 ; xx + + )
{
md2ang [ xx ] = LerpAngle ( curMA1 [ xx ] , curMA2 [ xx ] , saberHitFraction ) ;
}
AngleVectors ( md2ang , curMD2 , NULL , NULL ) ;
saberHitSaber = qtrue ;
}
if ( saberHitWall )
{
break ;
}
}
if ( saberHitWall | | saberHitSaber )
{
break ;
}
if ( curDirFrac > = 1.0f )
{
break ;
}
else
{
curDirFrac + = dirInc ;
if ( curDirFrac > = 1.0f )
{
curDirFrac = 1.0f ;
}
}
}
//do the trace at the end last
//Special check- adjust for length of blade not being a multiple of 12
/*
aveLength = ( ent - > client - > ps . saber [ saberNum ] . blade [ bladeNum ] . lengthOld + ent - > client - > ps . saber [ saberNum ] . blade [ bladeNum ] . length ) / 2 ;
if ( step > aveLength )
{ //less dmg if the last interval was not stepsize
tipDmgMod = ( stepsize - ( step - aveLength ) ) / stepsize ;
}
//NOTE: since this is the tip, we do not extrapolate the extra 16
if ( WP_SaberDamageForTrace ( ent - > s . number , endOld , endNew , tipDmgMod * baseDamage , md2 ,
qfalse , entPowerLevel , ent - > client - > ps . saber [ saberNum ] . type , qfalse ,
saberNum , bladeNum ) )
{
hit_wall = qtrue ;
}
*/
}
}
# include "../namespace_begin.h"
qboolean BG_SaberInTransitionAny ( int move ) ;
# include "../namespace_end.h"
qboolean WP_ForcePowerUsable ( gentity_t * self , forcePowers_t forcePower ) ;
qboolean InFOV3 ( vec3_t spot , vec3_t from , vec3_t fromAngles , int hFOV , int vFOV ) ;
qboolean Jedi_WaitingAmbush ( gentity_t * self ) ;
void Jedi_Ambush ( gentity_t * self ) ;
evasionType_t Jedi_SaberBlockGo ( gentity_t * self , usercmd_t * cmd , vec3_t pHitloc , vec3_t phitDir , gentity_t * incoming , float dist ) ;
void NPC_SetLookTarget ( gentity_t * self , int entNum , int clearTime ) ;
void WP_SaberStartMissileBlockCheck ( gentity_t * self , usercmd_t * ucmd )
{
float dist ;
gentity_t * ent , * incoming = NULL ;
int entityList [ MAX_GENTITIES ] ;
int numListedEntities ;
vec3_t mins , maxs ;
int i , e ;
float closestDist , radius = 256 ;
vec3_t forward , dir , missile_dir , fwdangles = { 0 } ;
trace_t trace ;
vec3_t traceTo , entDir ;
float dot1 , dot2 ;
float lookTDist = - 1 ;
gentity_t * lookT = NULL ;
qboolean doFullRoutine = qtrue ;
//keep this updated even if we don't get below
if ( ! ( self - > client - > ps . eFlags2 & EF2_HELD_BY_MONSTER ) )
{ //lookTarget is set by and to the monster that's holding you, no other operations can change that
self - > client - > ps . hasLookTarget = qfalse ;
}
if ( self - > client - > ps . weapon ! = WP_SABER & & self - > client - > NPC_class ! = CLASS_BOBAFETT )
{
doFullRoutine = qfalse ;
}
else if ( self - > client - > ps . saberInFlight )
{
doFullRoutine = qfalse ;
}
else if ( self - > client - > ps . fd . forcePowersActive & ( 1 < < FP_LIGHTNING ) )
{ //can't block while zapping
doFullRoutine = qfalse ;
}
else if ( self - > client - > ps . fd . forcePowersActive & ( 1 < < FP_DRAIN ) )
{ //can't block while draining
doFullRoutine = qfalse ;
}
else if ( self - > client - > ps . fd . forcePowersActive & ( 1 < < FP_PUSH ) )
{ //can't block while shoving
doFullRoutine = qfalse ;
}
else if ( self - > client - > ps . fd . forcePowersActive & ( 1 < < FP_GRIP ) )
{ //can't block while gripping (FIXME: or should it break the grip? Pain should break the grip, I think...)
doFullRoutine = qfalse ;
}
if ( self - > client - > ps . weaponTime > 0 )
{ //don't autoblock while busy with stuff
return ;
}
if ( ( self - > client - > saber [ 0 ] . saberFlags & SFL_NOT_ACTIVE_BLOCKING ) )
{ //can't actively block with this saber type
return ;
}
if ( self - > health < = 0 )
{ //dead don't try to block (NOTE: actual deflection happens in missile code)
return ;
}
if ( PM_InKnockDown ( & self - > client - > ps ) )
{ //can't block when knocked down
return ;
}
if ( BG_SabersOff ( & self - > client - > ps ) & & self - > client - > NPC_class ! = CLASS_BOBAFETT )
{
if ( self - > s . eType ! = ET_NPC )
{ //player doesn't auto-activate
doFullRoutine = qfalse ;
}
}
if ( self - > s . eType = = ET_PLAYER )
{ //don't do this if already attacking!
if ( ucmd - > buttons & BUTTON_ATTACK
| | BG_SaberInAttack ( self - > client - > ps . saberMove )
| | BG_SaberInSpecialAttack ( self - > client - > ps . torsoAnim )
| | BG_SaberInTransitionAny ( self - > client - > ps . saberMove ) )
{
doFullRoutine = qfalse ;
}
}
if ( self - > client - > ps . fd . forcePowerDebounce [ FP_SABER_DEFENSE ] > level . time )
{ //can't block while gripping (FIXME: or should it break the grip? Pain should break the grip, I think...)
doFullRoutine = qfalse ;
}
fwdangles [ 1 ] = self - > client - > ps . viewangles [ 1 ] ;
AngleVectors ( fwdangles , forward , NULL , NULL ) ;
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = self - > r . currentOrigin [ i ] - radius ;
maxs [ i ] = self - > r . currentOrigin [ i ] + radius ;
}
numListedEntities = trap_EntitiesInBox ( mins , maxs , entityList , MAX_GENTITIES ) ;
closestDist = radius ;
for ( e = 0 ; e < numListedEntities ; e + + )
{
ent = & g_entities [ entityList [ e ] ] ;
if ( ent = = self )
continue ;
//as long as we're here I'm going to get a looktarget too, I guess. -rww
if ( self - > s . eType = = ET_PLAYER & &
ent - > client & &
( ent - > s . eType = = ET_NPC | | ent - > s . eType = = ET_PLAYER ) & &
! OnSameTeam ( ent , self ) & &
ent - > client - > sess . sessionTeam ! = TEAM_SPECTATOR & &
! ( ent - > client - > ps . pm_flags & PMF_FOLLOW ) & &
( ent - > s . eType ! = ET_NPC | | ent - > s . NPC_class ! = CLASS_VEHICLE ) & & //don't look at vehicle NPCs
ent - > health > 0 )
{ //seems like a valid enemy to look at.
vec3_t vecSub ;
float vecLen ;
VectorSubtract ( self - > client - > ps . origin , ent - > client - > ps . origin , vecSub ) ;
vecLen = VectorLength ( vecSub ) ;
if ( lookTDist = = - 1 | | vecLen < lookTDist )
{
trace_t tr ;
vec3_t myEyes ;
VectorCopy ( self - > client - > ps . origin , myEyes ) ;
myEyes [ 2 ] + = self - > client - > ps . viewheight ;
trap_Trace ( & tr , myEyes , NULL , NULL , ent - > client - > ps . origin , self - > s . number , MASK_PLAYERSOLID ) ;
if ( tr . fraction = = 1.0f | | tr . entityNum = = ent - > s . number )
{ //we have a clear line of sight to him, so it's all good.
lookT = ent ;
lookTDist = vecLen ;
}
}
}
if ( ! doFullRoutine )
{ //don't care about the rest then
continue ;
}
if ( ent - > r . ownerNum = = self - > s . number )
continue ;
if ( ! ( ent - > inuse ) )
continue ;
if ( ent - > s . eType ! = ET_MISSILE & & ! ( ent - > s . eFlags & EF_MISSILE_STICK ) )
{ //not a normal projectile
gentity_t * pOwner ;
if ( ent - > r . ownerNum < 0 | | ent - > r . ownerNum > = ENTITYNUM_WORLD )
{ //not going to be a client then.
continue ;
}
pOwner = & g_entities [ ent - > r . ownerNum ] ;
if ( ! pOwner - > inuse | | ! pOwner - > client )
{
continue ; //not valid cl owner
}
if ( ! pOwner - > client - > ps . saberEntityNum | |
! pOwner - > client - > ps . saberInFlight | |
pOwner - > client - > ps . saberEntityNum ! = ent - > s . number )
{ //the saber is knocked away and/or not flying actively, or this ent is not the cl's saber ent at all
continue ;
}
//If we get here then it's ok to be treated as a thrown saber, I guess.
}
else
{
if ( ent - > s . pos . trType = = TR_STATIONARY & & self - > s . eType = = ET_PLAYER )
{ //nothing you can do with a stationary missile if you're the player
continue ;
}
}
//see if they're in front of me
VectorSubtract ( ent - > r . currentOrigin , self - > r . currentOrigin , dir ) ;
dist = VectorNormalize ( dir ) ;
//FIXME: handle detpacks, proximity mines and tripmines
if ( ent - > s . weapon = = WP_THERMAL )
{ //thermal detonator!
if ( self - > NPC & & dist < ent - > splashRadius )
{
if ( dist < ent - > splashRadius & &
ent - > nextthink < level . time + 600 & &
ent - > count & &
self - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE & &
( ent - > s . pos . trType = = TR_STATIONARY | |
ent - > s . pos . trType = = TR_INTERPOLATE | |
( dot1 = DotProduct ( dir , forward ) ) < SABER_REFLECT_MISSILE_CONE | |
! WP_ForcePowerUsable ( self , FP_PUSH ) ) )
{ //TD is close enough to hurt me, I'm on the ground and the thing is at rest or behind me and about to blow up, or I don't have force-push so force-jump!
//FIXME: sometimes this might make me just jump into it...?
self - > client - > ps . fd . forceJumpCharge = 480 ;
}
else if ( self - > client - > NPC_class ! = CLASS_BOBAFETT )
{ //FIXME: check forcePushRadius[NPC->client->ps.forcePowerLevel[FP_PUSH]]
ForceThrow ( self , qfalse ) ;
}
}
continue ;
}
else if ( ent - > splashDamage & & ent - > splashRadius )
{ //exploding missile
//FIXME: handle tripmines and detpacks somehow...
// maybe do a force-gesture that makes them explode?
// But what if we're within it's splashradius?
if ( self - > s . eType = = ET_PLAYER )
{ //players don't auto-handle these at all
continue ;
}
else
{
//if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK)
// && self->client->NPC_class != CLASS_BOBAFETT )
if ( 0 ) //Maybe handle this later?
{ //a placed explosive like a tripmine or detpack
if ( InFOV3 ( ent - > r . currentOrigin , self - > client - > renderInfo . eyePoint , self - > client - > ps . viewangles , 90 , 90 ) )
{ //in front of me
if ( G_ClearLOS4 ( self , ent ) )
{ //can see it
vec3_t throwDir ;
//make the gesture
ForceThrow ( self , qfalse ) ;
//take it off the wall and toss it
ent - > s . pos . trType = TR_GRAVITY ;
ent - > s . eType = ET_MISSILE ;
ent - > s . eFlags & = ~ EF_MISSILE_STICK ;
ent - > flags | = FL_BOUNCE_HALF ;
AngleVectors ( ent - > r . currentAngles , throwDir , NULL , NULL ) ;
VectorMA ( ent - > r . currentOrigin , ent - > r . maxs [ 0 ] + 4 , throwDir , ent - > r . currentOrigin ) ;
VectorCopy ( ent - > r . currentOrigin , ent - > s . pos . trBase ) ;
VectorScale ( throwDir , 300 , ent - > s . pos . trDelta ) ;
ent - > s . pos . trDelta [ 2 ] + = 150 ;
VectorMA ( ent - > s . pos . trDelta , 800 , dir , ent - > s . pos . trDelta ) ;
ent - > s . pos . trTime = level . time ; // move a bit on the very first frame
VectorCopy ( ent - > r . currentOrigin , ent - > s . pos . trBase ) ;
ent - > r . ownerNum = self - > s . number ;
// make it explode, but with less damage
ent - > splashDamage / = 3 ;
ent - > splashRadius / = 3 ;
//ent->think = WP_Explode;
ent - > nextthink = level . time + Q_irand ( 500 , 3000 ) ;
}
}
}
else if ( dist < ent - > splashRadius & &
self - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE & &
( DotProduct ( dir , forward ) < SABER_REFLECT_MISSILE_CONE | |
! WP_ForcePowerUsable ( self , FP_PUSH ) ) )
{ //NPCs try to evade it
self - > client - > ps . fd . forceJumpCharge = 480 ;
}
else if ( self - > client - > NPC_class ! = CLASS_BOBAFETT )
{ //else, try to force-throw it away
//FIXME: check forcePushRadius[NPC->client->ps.forcePowerLevel[FP_PUSH]]
ForceThrow ( self , qfalse ) ;
}
}
//otherwise, can't block it, so we're screwed
continue ;
}
if ( ent - > s . weapon ! = WP_SABER )
{ //only block shots coming from behind
if ( ( dot1 = DotProduct ( dir , forward ) ) < SABER_REFLECT_MISSILE_CONE )
continue ;
}
else if ( self - > s . eType = = ET_PLAYER )
{ //player never auto-blocks thrown sabers
continue ;
} //NPCs always try to block sabers coming from behind!
//see if they're heading towards me
VectorCopy ( ent - > s . pos . trDelta , missile_dir ) ;
VectorNormalize ( missile_dir ) ;
if ( ( dot2 = DotProduct ( dir , missile_dir ) ) > 0 )
continue ;
//FIXME: must have a clear trace to me, too...
if ( dist < closestDist )
{
VectorCopy ( self - > r . currentOrigin , traceTo ) ;
traceTo [ 2 ] = self - > r . absmax [ 2 ] - 4 ;
trap_Trace ( & trace , ent - > r . currentOrigin , ent - > r . mins , ent - > r . maxs , traceTo , ent - > s . number , ent - > clipmask ) ;
if ( trace . allsolid | | trace . startsolid | | ( trace . fraction < 1.0f & & trace . entityNum ! = self - > s . number & & trace . entityNum ! = self - > client - > ps . saberEntityNum ) )
{ //okay, try one more check
VectorNormalize2 ( ent - > s . pos . trDelta , entDir ) ;
VectorMA ( ent - > r . currentOrigin , radius , entDir , traceTo ) ;
trap_Trace ( & trace , ent - > r . currentOrigin , ent - > r . mins , ent - > r . maxs , traceTo , ent - > s . number , ent - > clipmask ) ;
if ( trace . allsolid | | trace . startsolid | | ( trace . fraction < 1.0f & & trace . entityNum ! = self - > s . number & & trace . entityNum ! = self - > client - > ps . saberEntityNum ) )
{ //can't hit me, ignore it
continue ;
}
}
if ( self - > s . eType = = ET_NPC )
{ //An NPC
if ( self - > NPC & & ! self - > enemy & & ent - > r . ownerNum ! = ENTITYNUM_NONE )
{
gentity_t * owner = & g_entities [ ent - > r . ownerNum ] ;
if ( owner - > health > = 0 & & ( ! owner - > client | | owner - > client - > playerTeam ! = self - > client - > playerTeam ) )
{
G_SetEnemy ( self , owner ) ;
}
}
}
//FIXME: if NPC, predict the intersection between my current velocity/path and the missile's, see if it intersects my bounding box (+/-saberLength?), don't try to deflect unless it does?
closestDist = dist ;
incoming = ent ;
}
}
if ( self - > s . eType = = ET_NPC & & self - > localAnimIndex < = 1 )
{ //humanoid NPCs don't set angles based on server angles for looking, unlike other NPCs
if ( self - > client & & self - > client - > renderInfo . lookTarget < ENTITYNUM_WORLD )
{
lookT = & g_entities [ self - > client - > renderInfo . lookTarget ] ;
}
}
if ( lookT )
{ //we got a looktarget at some point so we'll assign it then.
if ( ! ( self - > client - > ps . eFlags2 & EF2_HELD_BY_MONSTER ) )
{ //lookTarget is set by and to the monster that's holding you, no other operations can change that
self - > client - > ps . hasLookTarget = qtrue ;
self - > client - > ps . lookTarget = lookT - > s . number ;
}
}
if ( ! doFullRoutine )
{ //then we're done now
return ;
}
if ( incoming )
{
if ( self - > NPC /*&& !G_ControlledByPlayer( self )*/ )
{
if ( Jedi_WaitingAmbush ( self ) )
{
Jedi_Ambush ( self ) ;
}
if ( self - > client - > NPC_class = = CLASS_BOBAFETT
& & ( self - > client - > ps . eFlags2 & EF2_FLYING ) //moveType == MT_FLYSWIM
& & incoming - > methodOfDeath ! = MOD_ROCKET_HOMING )
{ //a hovering Boba Fett, not a tracking rocket
if ( ! Q_irand ( 0 , 1 ) )
{ //strafe
self - > NPC - > standTime = 0 ;
self - > client - > ps . fd . forcePowerDebounce [ FP_SABER_DEFENSE ] = level . time + Q_irand ( 1000 , 2000 ) ;
}
if ( ! Q_irand ( 0 , 1 ) )
{ //go up/down
TIMER_Set ( self , " heightChange " , Q_irand ( 1000 , 3000 ) ) ;
self - > client - > ps . fd . forcePowerDebounce [ FP_SABER_DEFENSE ] = level . time + Q_irand ( 1000 , 2000 ) ;
}
}
else if ( Jedi_SaberBlockGo ( self , & self - > NPC - > last_ucmd , NULL , NULL , incoming , 0.0f ) ! = EVASION_NONE )
{ //make sure to turn on your saber if it's not on
if ( self - > client - > NPC_class ! = CLASS_BOBAFETT )
{
//self->client->ps.SaberActivate();
WP_ActivateSaber ( self ) ;
}
}
}
else //player
{
gentity_t * owner = & g_entities [ incoming - > r . ownerNum ] ;
WP_SaberBlockNonRandom ( self , incoming - > r . currentOrigin , qtrue ) ;
if ( owner & & owner - > client & & ( ! self - > enemy | | self - > enemy - > s . weapon ! = WP_SABER ) ) //keep enemy jedi over shooters
{
self - > enemy = owner ;
//NPC_SetLookTarget( self, owner->s.number, level.time+1000 );
//player looktargetting done differently
}
}
}
}
# define MIN_SABER_SLICE_DISTANCE 50
# define MIN_SABER_SLICE_RETURN_DISTANCE 30
# define SABER_THROWN_HIT_DAMAGE 30
# define SABER_THROWN_RETURN_HIT_DAMAGE 5
void thrownSaberTouch ( gentity_t * saberent , gentity_t * other , trace_t * trace ) ;
static GAME_INLINE qboolean CheckThrownSaberDamaged ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * ent , int dist , int returning , qboolean noDCheck )
{
vec3_t vecsub ;
float veclen ;
gentity_t * te ;
if ( saberOwner & & saberOwner - > client & & saberOwner - > client - > ps . saberAttackWound > level . time )
{
return qfalse ;
}
if ( ent & & ent - > client & & ent - > inuse & & ent - > s . number ! = saberOwner - > s . number & &
ent - > health > 0 & & ent - > takedamage & &
trap_InPVS ( ent - > client - > ps . origin , saberent - > r . currentOrigin ) & &
ent - > client - > sess . sessionTeam ! = TEAM_SPECTATOR & &
( ent - > client - > pers . connected | | ent - > s . eType = = ET_NPC ) )
{ //hit a client
if ( ent - > inuse & & ent - > client & &
ent - > client - > ps . duelInProgress & &
ent - > client - > ps . duelIndex ! = saberOwner - > s . number )
{
return qfalse ;
}
if ( ent - > inuse & & ent - > client & &
saberOwner - > client - > ps . duelInProgress & &
saberOwner - > client - > ps . duelIndex ! = ent - > s . number )
{
return qfalse ;
}
VectorSubtract ( saberent - > r . currentOrigin , ent - > client - > ps . origin , vecsub ) ;
veclen = VectorLength ( vecsub ) ;
if ( veclen < dist )
{ //within range
trace_t tr ;
trap_Trace ( & tr , saberent - > r . currentOrigin , NULL , NULL , ent - > client - > ps . origin , saberent - > s . number , MASK_SHOT ) ;
if ( tr . fraction = = 1 | | tr . entityNum = = ent - > s . number )
{ //Slice them
if ( ! saberOwner - > client - > ps . isJediMaster & & WP_SaberCanBlock ( ent , tr . endpos , 0 , MOD_SABER , qfalse , 999 ) )
{ //they blocked it
WP_SaberBlockNonRandom ( ent , tr . endpos , qfalse ) ;
te = G_TempEntity ( tr . endpos , EV_SABER_BLOCK ) ;
VectorCopy ( tr . endpos , te - > s . origin ) ;
VectorCopy ( tr . plane . normal , te - > s . angles ) ;
if ( ! te - > s . angles [ 0 ] & & ! te - > s . angles [ 1 ] & & ! te - > s . angles [ 2 ] )
{
te - > s . angles [ 1 ] = 1 ;
}
te - > s . eventParm = 1 ;
te - > s . weapon = 0 ; //saberNum
te - > s . legsAnim = 0 ; //bladeNum
if ( saberCheckKnockdown_Thrown ( saberent , saberOwner , & g_entities [ tr . entityNum ] ) )
{ //it was knocked out of the air
return qfalse ;
}
if ( ! returning )
{ //return to owner if blocked
thrownSaberTouch ( saberent , saberent , NULL ) ;
}
saberOwner - > client - > ps . saberAttackWound = level . time + 500 ;
return qfalse ;
}
else
{ //a good hit
vec3_t dir ;
int dflags = 0 ;
VectorSubtract ( tr . endpos , saberent - > r . currentOrigin , dir ) ;
VectorNormalize ( dir ) ;
if ( ! dir [ 0 ] & & ! dir [ 1 ] & & ! dir [ 2 ] )
{
dir [ 1 ] = 1 ;
}
if ( ( saberOwner - > client - > saber [ 0 ] . saberFlags2 & SFL2_NO_DISMEMBERMENT ) )
{
dflags | = DAMAGE_NO_DISMEMBER ;
}
if ( saberOwner - > client - > saber [ 0 ] . knockbackScale > 0.0f )
{
dflags | = DAMAGE_SABER_KNOCKBACK1 ;
}
if ( saberOwner - > client - > ps . isJediMaster )
{ //2x damage for the Jedi Master
G_Damage ( ent , saberOwner , saberOwner , dir , tr . endpos , saberent - > damage * 2 , dflags , MOD_SABER ) ;
}
else
{
G_Damage ( ent , saberOwner , saberOwner , dir , tr . endpos , saberent - > damage , dflags , MOD_SABER ) ;
}
te = G_TempEntity ( tr . endpos , EV_SABER_HIT ) ;
te - > s . otherEntityNum = ent - > s . number ;
te - > s . otherEntityNum2 = saberOwner - > s . number ;
te - > s . weapon = 0 ; //saberNum
te - > s . legsAnim = 0 ; //bladeNum
VectorCopy ( tr . endpos , te - > s . origin ) ;
VectorCopy ( tr . plane . normal , te - > s . angles ) ;
if ( ! te - > s . angles [ 0 ] & & ! te - > s . angles [ 1 ] & & ! te - > s . angles [ 2 ] )
{
te - > s . angles [ 1 ] = 1 ;
}
te - > s . eventParm = 1 ;
if ( ! returning )
{ //return to owner if blocked
thrownSaberTouch ( saberent , saberent , NULL ) ;
}
}
saberOwner - > client - > ps . saberAttackWound = level . time + 500 ;
}
}
}
else if ( ent & & ! ent - > client & & ent - > inuse & & ent - > takedamage & & ent - > health > 0 & & ent - > s . number ! = saberOwner - > s . number & &
ent - > s . number ! = saberent - > s . number & & ( noDCheck | | trap_InPVS ( ent - > r . currentOrigin , saberent - > r . currentOrigin ) ) )
{ //hit a non-client
if ( noDCheck )
{
veclen = 0 ;
}
else
{
VectorSubtract ( saberent - > r . currentOrigin , ent - > r . currentOrigin , vecsub ) ;
veclen = VectorLength ( vecsub ) ;
}
if ( veclen < dist )
{
trace_t tr ;
vec3_t entOrigin ;
if ( ent - > s . eType = = ET_MOVER )
{
VectorSubtract ( ent - > r . absmax , ent - > r . absmin , entOrigin ) ;
VectorMA ( ent - > r . absmin , 0.5 , entOrigin , entOrigin ) ;
VectorAdd ( ent - > r . absmin , ent - > r . absmax , entOrigin ) ;
VectorScale ( entOrigin , 0.5f , entOrigin ) ;
}
else
{
VectorCopy ( ent - > r . currentOrigin , entOrigin ) ;
}
trap_Trace ( & tr , saberent - > r . currentOrigin , NULL , NULL , entOrigin , saberent - > s . number , MASK_SHOT ) ;
if ( tr . fraction = = 1 | | tr . entityNum = = ent - > s . number )
{
vec3_t dir ;
int dflags = 0 ;
VectorSubtract ( tr . endpos , entOrigin , dir ) ;
VectorNormalize ( dir ) ;
if ( ( saberOwner - > client - > saber [ 0 ] . saberFlags2 & SFL2_NO_DISMEMBERMENT ) )
{
dflags | = DAMAGE_NO_DISMEMBER ;
}
if ( saberOwner - > client - > saber [ 0 ] . knockbackScale > 0.0f )
{
dflags | = DAMAGE_SABER_KNOCKBACK1 ;
}
if ( ent - > s . eType = = ET_NPC )
{ //an animent
G_Damage ( ent , saberOwner , saberOwner , dir , tr . endpos , 40 , dflags , MOD_SABER ) ;
}
else
{
G_Damage ( ent , saberOwner , saberOwner , dir , tr . endpos , 5 , dflags , MOD_SABER ) ;
}
te = G_TempEntity ( tr . endpos , EV_SABER_HIT ) ;
te - > s . otherEntityNum = ENTITYNUM_NONE ; //don't do this for throw damage
//te->s.otherEntityNum = ent->s.number;
te - > s . otherEntityNum2 = saberOwner - > s . number ; //actually, do send this, though - for the overridden per-saber hit effects/sounds
te - > s . weapon = 0 ; //saberNum
te - > s . legsAnim = 0 ; //bladeNum
VectorCopy ( tr . endpos , te - > s . origin ) ;
VectorCopy ( tr . plane . normal , te - > s . angles ) ;
if ( ! te - > s . angles [ 0 ] & & ! te - > s . angles [ 1 ] & & ! te - > s . angles [ 2 ] )
{
te - > s . angles [ 1 ] = 1 ;
}
if ( ent - > s . eType = = ET_MOVER )
{
if ( saberOwner
& & saberOwner - > client
& & ( saberOwner - > client - > saber [ 0 ] . saberFlags2 & SFL2_NO_CLASH_FLARE ) )
{ //don't do clash flare - NOTE: assumes same is true for both sabers if using dual sabers!
G_FreeEntity ( te ) ; //kind of a waste, but...
}
else
{
//I suppose I could tie this into the saberblock event, but I'm tired of adding flags to that thing.
gentity_t * teS = G_TempEntity ( te - > s . origin , EV_SABER_CLASHFLARE ) ;
VectorCopy ( te - > s . origin , teS - > s . origin ) ;
te - > s . eventParm = 0 ;
}
}
else
{
te - > s . eventParm = 1 ;
}
if ( ! returning )
{ //return to owner if blocked
thrownSaberTouch ( saberent , saberent , NULL ) ;
}
saberOwner - > client - > ps . saberAttackWound = level . time + 500 ;
}
}
}
return qtrue ;
}
static GAME_INLINE void saberCheckRadiusDamage ( gentity_t * saberent , int returning )
{ //we're going to cheat and damage players within the saber's radius, just for the sake of doing things more "efficiently" (and because the saber entity has no server g2 instance)
int i = 0 ;
int dist = 0 ;
gentity_t * ent ;
gentity_t * saberOwner = & g_entities [ saberent - > r . ownerNum ] ;
if ( returning & & returning ! = 2 )
{
dist = MIN_SABER_SLICE_RETURN_DISTANCE ;
}
else
{
dist = MIN_SABER_SLICE_DISTANCE ;
}
if ( ! saberOwner | | ! saberOwner - > client )
{
return ;
}
if ( saberOwner - > client - > ps . saberAttackWound > level . time )
{
return ;
}
while ( i < level . num_entities )
{
ent = & g_entities [ i ] ;
CheckThrownSaberDamaged ( saberent , saberOwner , ent , dist , returning , qfalse ) ;
i + + ;
}
}
# define THROWN_SABER_COMP
static GAME_INLINE void saberMoveBack ( gentity_t * ent , qboolean goingBack )
{
vec3_t origin , oldOrg ;
ent - > s . pos . trType = TR_LINEAR ;
VectorCopy ( ent - > r . currentOrigin , oldOrg ) ;
// get current position
BG_EvaluateTrajectory ( & ent - > s . pos , level . time , origin ) ;
//Get current angles?
BG_EvaluateTrajectory ( & ent - > s . apos , level . time , ent - > r . currentAngles ) ;
//compensation test code..
# ifdef THROWN_SABER_COMP
if ( ! goingBack & & ent - > s . pos . trType ! = TR_GRAVITY )
{ //acts as a fallback in case touch code fails, keeps saber from going through things between predictions
float originalLength = 0 ;
int iCompensationLength = 32 ;
trace_t tr ;
vec3_t mins , maxs ;
vec3_t calcComp , compensatedOrigin ;
VectorSet ( mins , - 24.0f , - 24.0f , - 8.0f ) ;
VectorSet ( maxs , 24.0f , 24.0f , 8.0f ) ;
VectorSubtract ( origin , oldOrg , calcComp ) ;
originalLength = VectorLength ( calcComp ) ;
VectorNormalize ( calcComp ) ;
compensatedOrigin [ 0 ] = oldOrg [ 0 ] + calcComp [ 0 ] * ( originalLength + iCompensationLength ) ;
compensatedOrigin [ 1 ] = oldOrg [ 1 ] + calcComp [ 1 ] * ( originalLength + iCompensationLength ) ;
compensatedOrigin [ 2 ] = oldOrg [ 2 ] + calcComp [ 2 ] * ( originalLength + iCompensationLength ) ;
trap_Trace ( & tr , oldOrg , mins , maxs , compensatedOrigin , ent - > r . ownerNum , MASK_PLAYERSOLID ) ;
if ( ( tr . fraction ! = 1 | | tr . startsolid | | tr . allsolid ) & & tr . entityNum ! = ent - > r . ownerNum & & ! ( g_entities [ tr . entityNum ] . r . contents & CONTENTS_LIGHTSABER ) )
{
VectorClear ( ent - > s . pos . trDelta ) ;
//Unfortunately doing this would defeat the purpose of the compensation. We will have to settle for a jerk on the client.
//VectorCopy( origin, ent->r.currentOrigin );
//we'll skip the dist check, since we don't really care about that (we just hit it physically)
CheckThrownSaberDamaged ( ent , & g_entities [ ent - > r . ownerNum ] , & g_entities [ tr . entityNum ] , 256 , 0 , qtrue ) ;
if ( ent - > s . pos . trType = = TR_GRAVITY )
{ //got blocked and knocked away in the damage func
return ;
}
tr . startsolid = 0 ;
if ( tr . entityNum = = ENTITYNUM_NONE )
{ //eh, this is a filthy lie. (obviously it had to hit something or it wouldn't be in here, so we'll say it hit the world)
tr . entityNum = ENTITYNUM_WORLD ;
}
thrownSaberTouch ( ent , & g_entities [ tr . entityNum ] , & tr ) ;
return ;
}
}
# endif
VectorCopy ( origin , ent - > r . currentOrigin ) ;
}
void SaberBounceSound ( gentity_t * self , gentity_t * other , trace_t * trace )
{
VectorCopy ( self - > r . currentAngles , self - > s . apos . trBase ) ;
self - > s . apos . trBase [ PITCH ] = 90 ;
}
void DeadSaberThink ( gentity_t * saberent )
{
if ( saberent - > speed < level . time )
{
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
G_RunObject ( saberent ) ;
}
void MakeDeadSaber ( gentity_t * ent )
{ //spawn a "dead" saber entity here so it looks like the saber fell out of the air.
//This entity will remove itself after a very short time period.
vec3_t startorg ;
vec3_t startang ;
gentity_t * saberent ;
gentity_t * owner = NULL ;
if ( g_gametype . integer = = GT_JEDIMASTER )
{ //never spawn a dead saber in JM, because the only saber on the level is really a world object
//G_Sound(ent, CHAN_AUTO, saberOffSound);
return ;
}
saberent = G_Spawn ( ) ;
VectorCopy ( ent - > r . currentOrigin , startorg ) ;
VectorCopy ( ent - > r . currentAngles , startang ) ;
saberent - > classname = " deadsaber " ;
saberent - > r . svFlags = SVF_USE_CURRENT_ORIGIN ;
saberent - > r . ownerNum = ent - > s . number ;
saberent - > clipmask = MASK_PLAYERSOLID ;
saberent - > r . contents = CONTENTS_TRIGGER ; //0;
VectorSet ( saberent - > r . mins , - 3.0f , - 3.0f , - 1.5f ) ;
VectorSet ( saberent - > r . maxs , 3.0f , 3.0f , 1.5f ) ;
saberent - > touch = SaberBounceSound ;
saberent - > think = DeadSaberThink ;
saberent - > nextthink = level . time ;
VectorCopy ( startorg , saberent - > s . pos . trBase ) ;
VectorCopy ( startang , saberent - > s . apos . trBase ) ;
VectorCopy ( startorg , saberent - > s . origin ) ;
VectorCopy ( startang , saberent - > s . angles ) ;
VectorCopy ( startorg , saberent - > r . currentOrigin ) ;
VectorCopy ( startang , saberent - > r . currentAngles ) ;
saberent - > s . apos . trType = TR_GRAVITY ;
saberent - > s . apos . trDelta [ 0 ] = Q_irand ( 200 , 800 ) ;
saberent - > s . apos . trDelta [ 1 ] = Q_irand ( 200 , 800 ) ;
saberent - > s . apos . trDelta [ 2 ] = Q_irand ( 200 , 800 ) ;
saberent - > s . apos . trTime = level . time - 50 ;
saberent - > s . pos . trType = TR_GRAVITY ;
saberent - > s . pos . trTime = level . time - 50 ;
saberent - > flags = FL_BOUNCE_HALF ;
if ( ent - > r . ownerNum > = 0 & & ent - > r . ownerNum < ENTITYNUM_WORLD )
{
owner = & g_entities [ ent - > r . ownerNum ] ;
if ( owner - > inuse & & owner - > client & &
owner - > client - > saber [ 0 ] . model [ 0 ] )
{
WP_SaberAddG2Model ( saberent , owner - > client - > saber [ 0 ] . model , owner - > client - > saber [ 0 ] . skin ) ;
}
else
{
//WP_SaberAddG2Model( saberent, NULL, 0 );
//argh!!!!
G_FreeEntity ( saberent ) ;
return ;
}
}
saberent - > s . modelGhoul2 = 1 ;
saberent - > s . g2radius = 20 ;
saberent - > s . eType = ET_MISSILE ;
saberent - > s . weapon = WP_SABER ;
saberent - > speed = level . time + 4000 ;
saberent - > bounceCount = 12 ;
//fall off in the direction the real saber was headed
VectorCopy ( ent - > s . pos . trDelta , saberent - > s . pos . trDelta ) ;
saberMoveBack ( saberent , qtrue ) ;
saberent - > s . pos . trType = TR_GRAVITY ;
trap_LinkEntity ( saberent ) ;
}
# define MAX_LEAVE_TIME 20000
void saberReactivate ( gentity_t * saberent , gentity_t * saberOwner ) ;
void saberBackToOwner ( gentity_t * saberent ) ;
void DownedSaberThink ( gentity_t * saberent )
{
gentity_t * saberOwn = NULL ;
qboolean notDisowned = qfalse ;
qboolean pullBack = qfalse ;
saberent - > nextthink = level . time ;
if ( saberent - > r . ownerNum = = ENTITYNUM_NONE )
{
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
saberOwn = & g_entities [ saberent - > r . ownerNum ] ;
if ( ! saberOwn | |
! saberOwn - > inuse | |
! saberOwn - > client | |
saberOwn - > client - > sess . sessionTeam = = TEAM_SPECTATOR | |
( saberOwn - > client - > ps . pm_flags & PMF_FOLLOW ) )
{
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
if ( saberOwn - > client - > ps . saberEntityNum )
{
if ( saberOwn - > client - > ps . saberEntityNum = = saberent - > s . number )
{ //owner shouldn't have this set if we're thinking in here. Must've fallen off a cliff and instantly respawned or something.
notDisowned = qtrue ;
}
else
{ //This should never happen, but just in case..
assert ( ! " ULTRA BAD THING " ) ;
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
}
if ( notDisowned | | saberOwn - > health < 1 | | ! saberOwn - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] )
{ //He's dead, just go back to our normal saber status
saberOwn - > client - > ps . saberEntityNum = saberOwn - > client - > saberStoredIndex ;
//MakeDeadSaber(saberent); //spawn a dead saber on top of where we are now. The "bodyqueue" method.
//Actually this will get taken care of when the thrown saber func sees we're dead.
# ifdef _DEBUG
if ( saberOwn - > client - > saberStoredIndex ! = saberent - > s . number )
{ //I'm paranoid.
assert ( ! " Bad saber index!!! " ) ;
}
# endif
saberReactivate ( saberent , saberOwn ) ;
if ( saberOwn - > health < 1 )
{
saberOwn - > client - > ps . saberInFlight = qfalse ;
MakeDeadSaber ( saberent ) ;
}
saberent - > touch = SaberGotHit ;
saberent - > think = SaberUpdateSelf ;
saberent - > genericValue5 = 0 ;
saberent - > nextthink = level . time ;
saberent - > r . svFlags | = ( SVF_NOCLIENT ) ;
//saberent->r.contents = CONTENTS_LIGHTSABER;
saberent - > s . loopSound = 0 ;
saberent - > s . loopIsSoundset = qfalse ;
if ( saberOwn - > health > 0 )
{ //only set this if he's alive. If dead we want to reflect the lack of saber on the corpse, as he died with his saber out.
saberOwn - > client - > ps . saberInFlight = qfalse ;
WP_SaberRemoveG2Model ( saberent ) ;
}
saberOwn - > client - > ps . saberEntityState = 0 ;
saberOwn - > client - > ps . saberThrowDelay = level . time + 500 ;
saberOwn - > client - > ps . saberCanThrow = qfalse ;
return ;
}
if ( saberOwn - > client - > saberKnockedTime < level . time & & ( saberOwn - > client - > pers . cmd . buttons & BUTTON_ATTACK ) )
{ //He wants us back
pullBack = qtrue ;
}
else if ( ( level . time - saberOwn - > client - > saberKnockedTime ) > MAX_LEAVE_TIME )
{ //Been sitting around for too long, go back no matter what he wants.
pullBack = qtrue ;
}
if ( pullBack )
{ //Get going back to the owner.
saberOwn - > client - > ps . saberEntityNum = saberOwn - > client - > saberStoredIndex ;
# ifdef _DEBUG
if ( saberOwn - > client - > saberStoredIndex ! = saberent - > s . number )
{ //I'm paranoid.
assert ( ! " Bad saber index!!! " ) ;
}
# endif
saberReactivate ( saberent , saberOwn ) ;
saberent - > touch = SaberGotHit ;
saberent - > think = saberBackToOwner ;
saberent - > speed = 0 ;
saberent - > genericValue5 = 0 ;
saberent - > nextthink = level . time ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
G_Sound ( saberOwn , CHAN_BODY , G_SoundIndex ( " sound/weapons/force/pull.wav " ) ) ;
if ( saberOwn - > client - > saber [ 0 ] . soundOn )
{
G_Sound ( saberent , CHAN_BODY , saberOwn - > client - > saber [ 0 ] . soundOn ) ;
}
if ( saberOwn - > client - > saber [ 1 ] . soundOn )
{
G_Sound ( saberOwn , CHAN_BODY , saberOwn - > client - > saber [ 1 ] . soundOn ) ;
}
return ;
}
G_RunObject ( saberent ) ;
saberent - > nextthink = level . time ;
}
void saberReactivate ( gentity_t * saberent , gentity_t * saberOwner )
{
saberent - > s . saberInFlight = qtrue ;
saberent - > s . apos . trType = TR_LINEAR ;
saberent - > s . apos . trDelta [ 0 ] = 0 ;
saberent - > s . apos . trDelta [ 1 ] = 800 ;
saberent - > s . apos . trDelta [ 2 ] = 0 ;
saberent - > s . pos . trType = TR_LINEAR ;
saberent - > s . eType = ET_GENERAL ;
saberent - > s . eFlags = 0 ;
saberent - > parent = saberOwner ;
saberent - > genericValue5 = 0 ;
SetSaberBoxSize ( saberent ) ;
saberent - > touch = thrownSaberTouch ;
saberent - > s . weapon = WP_SABER ;
saberOwner - > client - > ps . saberEntityState = 1 ;
trap_LinkEntity ( saberent ) ;
}
# define SABER_RETRIEVE_DELAY 3000 //3 seconds for now. This will leave you nice and open if you lose your saber.
void saberKnockDown ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other )
{
saberOwner - > client - > ps . saberEntityNum = 0 ; //still stored in client->saberStoredIndex
saberOwner - > client - > saberKnockedTime = level . time + SABER_RETRIEVE_DELAY ;
saberent - > clipmask = MASK_SOLID ;
saberent - > r . contents = CONTENTS_TRIGGER ; //0;
VectorSet ( saberent - > r . mins , - 3.0f , - 3.0f , - 1.5f ) ;
VectorSet ( saberent - > r . maxs , 3.0f , 3.0f , 1.5f ) ;
saberent - > s . apos . trType = TR_GRAVITY ;
saberent - > s . apos . trDelta [ 0 ] = Q_irand ( 200 , 800 ) ;
saberent - > s . apos . trDelta [ 1 ] = Q_irand ( 200 , 800 ) ;
saberent - > s . apos . trDelta [ 2 ] = Q_irand ( 200 , 800 ) ;
saberent - > s . apos . trTime = level . time - 50 ;
saberent - > s . pos . trType = TR_GRAVITY ;
saberent - > s . pos . trTime = level . time - 50 ;
saberent - > flags | = FL_BOUNCE_HALF ;
WP_SaberAddG2Model ( saberent , saberOwner - > client - > saber [ 0 ] . model , saberOwner - > client - > saber [ 0 ] . skin ) ;
saberent - > s . modelGhoul2 = 1 ;
saberent - > s . g2radius = 20 ;
saberent - > s . eType = ET_MISSILE ;
saberent - > s . weapon = WP_SABER ;
saberent - > speed = level . time + 4000 ;
saberent - > bounceCount = - 5 ; //8;
saberMoveBack ( saberent , qtrue ) ;
saberent - > s . pos . trType = TR_GRAVITY ;
saberent - > s . loopSound = 0 ; //kill this in case it was spinning.
saberent - > s . loopIsSoundset = qfalse ;
saberent - > r . svFlags & = ~ ( SVF_NOCLIENT ) ; //make sure the client is getting updates on where it is and such.
saberent - > touch = SaberBounceSound ;
saberent - > think = DownedSaberThink ;
saberent - > nextthink = level . time ;
if ( saberOwner ! = other )
{ //if someone knocked it out of the air and it wasn't turned off, go in the direction they were facing.
if ( other - > inuse & & other - > client )
{
vec3_t otherFwd ;
float deflectSpeed = 200 ;
AngleVectors ( other - > client - > ps . viewangles , otherFwd , 0 , 0 ) ;
saberent - > s . pos . trDelta [ 0 ] = otherFwd [ 0 ] * deflectSpeed ;
saberent - > s . pos . trDelta [ 1 ] = otherFwd [ 1 ] * deflectSpeed ;
saberent - > s . pos . trDelta [ 2 ] = otherFwd [ 2 ] * deflectSpeed ;
}
}
trap_LinkEntity ( saberent ) ;
if ( saberOwner - > client - > saber [ 0 ] . soundOff )
{
G_Sound ( saberent , CHAN_BODY , saberOwner - > client - > saber [ 0 ] . soundOff ) ;
}
if ( saberOwner - > client - > saber [ 1 ] . soundOff & &
saberOwner - > client - > saber [ 1 ] . model [ 0 ] )
{
G_Sound ( saberOwner , CHAN_BODY , saberOwner - > client - > saber [ 1 ] . soundOff ) ;
}
}
//sort of a silly macro I guess. But if I change anything in here I'll probably want it to be everywhere.
# define SABERINVALID (!saberent || !saberOwner || !other || !saberent->inuse || !saberOwner->inuse || !other->inuse || !saberOwner->client || !other->client || !saberOwner->client->ps.saberEntityNum || saberOwner->client->ps.saberLockTime > (level.time-100))
void WP_SaberRemoveG2Model ( gentity_t * saberent )
{
if ( saberent - > ghoul2 )
{
trap_G2API_RemoveGhoul2Models ( & saberent - > ghoul2 ) ;
}
}
void WP_SaberAddG2Model ( gentity_t * saberent , const char * saberModel , qhandle_t saberSkin )
{
WP_SaberRemoveG2Model ( saberent ) ;
if ( saberModel & & saberModel [ 0 ] )
{
saberent - > s . modelindex = G_ModelIndex ( saberModel ) ;
}
else
{
saberent - > s . modelindex = G_ModelIndex ( " models/weapons2/saber/saber_w.glm " ) ;
}
//FIXME: use customSkin?
trap_G2API_InitGhoul2Model ( & saberent - > ghoul2 , saberModel , saberent - > s . modelindex , saberSkin , 0 , 0 , 0 ) ;
}
//Make the saber go flying directly out of the owner's hand in the specified direction
qboolean saberKnockOutOfHand ( gentity_t * saberent , gentity_t * saberOwner , vec3_t velocity )
{
if ( ! saberent | | ! saberOwner | |
! saberent - > inuse | | ! saberOwner - > inuse | |
! saberOwner - > client )
{
return qfalse ;
}
if ( ! saberOwner - > client - > ps . saberEntityNum )
{ //already gone
return qfalse ;
}
if ( ( level . time - saberOwner - > client - > lastSaberStorageTime ) > 50 )
{ //must have a reasonably updated saber base pos
return qfalse ;
}
if ( saberOwner - > client - > ps . saberLockTime > ( level . time - 100 ) )
{
return qfalse ;
}
if ( ( saberOwner - > client - > saber [ 0 ] . saberFlags & SFL_NOT_DISARMABLE ) )
{
return qfalse ;
}
saberOwner - > client - > ps . saberInFlight = qtrue ;
saberOwner - > client - > ps . saberEntityState = 1 ;
saberent - > s . saberInFlight = qfalse ; //qtrue;
saberent - > s . pos . trType = TR_LINEAR ;
saberent - > s . eType = ET_GENERAL ;
saberent - > s . eFlags = 0 ;
WP_SaberAddG2Model ( saberent , saberOwner - > client - > saber [ 0 ] . model , saberOwner - > client - > saber [ 0 ] . skin ) ;
saberent - > s . modelGhoul2 = 127 ;
saberent - > parent = saberOwner ;
saberent - > damage = SABER_THROWN_HIT_DAMAGE ;
saberent - > methodOfDeath = MOD_SABER ;
saberent - > splashMethodOfDeath = MOD_SABER ;
saberent - > s . solid = 2 ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
saberent - > genericValue5 = 0 ;
VectorSet ( saberent - > r . mins , - 24.0f , - 24.0f , - 8.0f ) ;
VectorSet ( saberent - > r . maxs , 24.0f , 24.0f , 8.0f ) ;
saberent - > s . genericenemyindex = saberOwner - > s . number + 1024 ;
saberent - > s . weapon = WP_SABER ;
saberent - > genericValue5 = 0 ;
G_SetOrigin ( saberent , saberOwner - > client - > lastSaberBase_Always ) ; //use this as opposed to the right hand bolt,
//because I don't want to risk reconstructing the skel again to get it here. And it isn't worth storing.
saberKnockDown ( saberent , saberOwner , saberOwner ) ;
VectorCopy ( velocity , saberent - > s . pos . trDelta ) ; //override the velocity on the knocked away saber.
return qtrue ;
}
//Called at the result of a circle lock duel - the loser gets his saber tossed away and is put into a reflected attack anim
qboolean saberCheckKnockdown_DuelLoss ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other )
{
vec3_t dif ;
float totalDistance = 1 ;
float distScale = 6.5f ;
qboolean validMomentum = qtrue ;
int disarmChance = 1 ;
if ( SABERINVALID )
{
return qfalse ;
}
VectorClear ( dif ) ;
if ( ! other - > client - > olderIsValid | | ( level . time - other - > client - > lastSaberStorageTime ) > = 200 )
{ //see if the spots are valid
validMomentum = qfalse ;
}
if ( validMomentum )
{
//Get the difference
VectorSubtract ( other - > client - > lastSaberBase_Always , other - > client - > olderSaberBase , dif ) ;
totalDistance = VectorNormalize ( dif ) ;
if ( ! totalDistance )
{ //fine, try our own
if ( ! saberOwner - > client - > olderIsValid | | ( level . time - saberOwner - > client - > lastSaberStorageTime ) > = 200 )
{
validMomentum = qfalse ;
}
if ( validMomentum )
{
VectorSubtract ( saberOwner - > client - > lastSaberBase_Always , saberOwner - > client - > olderSaberBase , dif ) ;
totalDistance = VectorNormalize ( dif ) ;
}
}
if ( validMomentum )
{
if ( ! totalDistance )
{ //try the difference between the two blades
VectorSubtract ( saberOwner - > client - > lastSaberBase_Always , other - > client - > lastSaberBase_Always , dif ) ;
totalDistance = VectorNormalize ( dif ) ;
}
if ( totalDistance )
{ //if we still have no difference somehow, just let it fall to the ground when the time comes.
if ( totalDistance < 20 )
{
totalDistance = 20 ;
}
VectorScale ( dif , totalDistance * distScale , dif ) ;
}
}
}
saberOwner - > client - > ps . saberMove = LS_V1_BL ; //rwwFIXMEFIXME: Ideally check which lock it was exactly and use the proper anim (same goes for the attacker)
saberOwner - > client - > ps . saberBlocked = BLOCKED_BOUNCE_MOVE ;
if ( other & & other - > client )
{
disarmChance + = other - > client - > saber [ 0 ] . disarmBonus ;
if ( other - > client - > saber [ 1 ] . model
& & other - > client - > saber [ 1 ] . model [ 0 ]
& & ! other - > client - > ps . saberHolstered )
{
other - > client - > saber [ 1 ] . disarmBonus ;
}
}
if ( Q_irand ( 0 , disarmChance ) )
{
return saberKnockOutOfHand ( saberent , saberOwner , dif ) ;
}
else
{
return qfalse ;
}
}
//Called when we want to try knocking the saber out of the owner's hand upon them going into a broken parry.
//Also called on reflected attacks.
qboolean saberCheckKnockdown_BrokenParry ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other )
{
int myAttack ;
int otherAttack ;
qboolean doKnock = qfalse ;
int disarmChance = 1 ;
if ( SABERINVALID )
{
return qfalse ;
}
//Neither gets an advantage based on attack state, when it comes to knocking
//saber out of hand.
myAttack = G_SaberAttackPower ( saberOwner , qfalse ) ;
otherAttack = G_SaberAttackPower ( other , qfalse ) ;
if ( ! other - > client - > olderIsValid | | ( level . time - other - > client - > lastSaberStorageTime ) > = 200 )
{ //if we don't know which way to throw the saber based on momentum between saber positions, just don't throw it
return qfalse ;
}
//only knock the saber out of the hand if they're in a stronger stance I suppose. Makes strong more advantageous.
if ( otherAttack > myAttack + 1 & & Q_irand ( 1 , 10 ) < = 7 )
{ //This would be, say, strong stance against light stance.
doKnock = qtrue ;
}
else if ( otherAttack > myAttack & & Q_irand ( 1 , 10 ) < = 3 )
{ //Strong vs. medium, medium vs. light
doKnock = qtrue ;
}
if ( doKnock )
{
vec3_t dif ;
float totalDistance ;
float distScale = 6.5f ;
VectorSubtract ( other - > client - > lastSaberBase_Always , other - > client - > olderSaberBase , dif ) ;
totalDistance = VectorNormalize ( dif ) ;
if ( ! totalDistance )
{ //fine, try our own
if ( ! saberOwner - > client - > olderIsValid | | ( level . time - saberOwner - > client - > lastSaberStorageTime ) > = 200 )
{ //if we don't know which way to throw the saber based on momentum between saber positions, just don't throw it
return qfalse ;
}
VectorSubtract ( saberOwner - > client - > lastSaberBase_Always , saberOwner - > client - > olderSaberBase , dif ) ;
totalDistance = VectorNormalize ( dif ) ;
}
if ( ! totalDistance )
{ //...forget it then.
return qfalse ;
}
if ( totalDistance < 20 )
{
totalDistance = 20 ;
}
VectorScale ( dif , totalDistance * distScale , dif ) ;
if ( other & & other - > client )
{
disarmChance + = other - > client - > saber [ 0 ] . disarmBonus ;
if ( other - > client - > saber [ 1 ] . model
& & other - > client - > saber [ 1 ] . model [ 0 ]
& & ! other - > client - > ps . saberHolstered )
{
other - > client - > saber [ 1 ] . disarmBonus ;
}
}
if ( Q_irand ( 0 , disarmChance ) )
{
return saberKnockOutOfHand ( saberent , saberOwner , dif ) ;
}
}
return qfalse ;
}
# include "../namespace_begin.h"
qboolean BG_InExtraDefenseSaberMove ( int move ) ;
# include "../namespace_end.h"
//Called upon an enemy actually slashing into a thrown saber
qboolean saberCheckKnockdown_Smashed ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other , int damage )
{
if ( SABERINVALID )
{
return qfalse ;
}
if ( ! saberOwner - > client - > ps . saberInFlight )
{ //can only do this if the saber is already actually in flight
return qfalse ;
}
if ( other
& & other - > inuse
& & other - > client
& & BG_InExtraDefenseSaberMove ( other - > client - > ps . saberMove ) )
{ //make sure the blow was strong enough
saberKnockDown ( saberent , saberOwner , other ) ;
return qtrue ;
}
if ( damage > 10 )
{ //make sure the blow was strong enough
saberKnockDown ( saberent , saberOwner , other ) ;
return qtrue ;
}
return qfalse ;
}
//Called upon blocking a thrown saber. If the throw level compared to the blocker's defense level
//is inferior, or equal and a random factor is met, then the saber will be tossed to the ground.
qboolean saberCheckKnockdown_Thrown ( gentity_t * saberent , gentity_t * saberOwner , gentity_t * other )
{
int throwLevel = 0 ;
int defenLevel = 0 ;
qboolean tossIt = qfalse ;
if ( SABERINVALID )
{
return qfalse ;
}
defenLevel = other - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] ;
throwLevel = saberOwner - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] ;
if ( defenLevel > throwLevel )
{
tossIt = qtrue ;
}
else if ( defenLevel = = throwLevel & & Q_irand ( 1 , 10 ) < = 4 )
{
tossIt = qtrue ;
}
//otherwise don't
if ( tossIt )
{
saberKnockDown ( saberent , saberOwner , other ) ;
return qtrue ;
}
return qfalse ;
}
void saberBackToOwner ( gentity_t * saberent )
{
gentity_t * saberOwner = & g_entities [ saberent - > r . ownerNum ] ;
vec3_t dir ;
float ownerLen ;
if ( saberent - > r . ownerNum = = ENTITYNUM_NONE )
{
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
if ( ! saberOwner - > inuse | |
! saberOwner - > client | |
saberOwner - > client - > sess . sessionTeam = = TEAM_SPECTATOR )
{
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
if ( saberOwner - > health < 1 | | ! saberOwner - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] )
{ //He's dead, just go back to our normal saber status
saberent - > touch = SaberGotHit ;
saberent - > think = SaberUpdateSelf ;
saberent - > genericValue5 = 0 ;
saberent - > nextthink = level . time ;
if ( saberOwner - > client & &
saberOwner - > client - > saber [ 0 ] . soundOff )
{
G_Sound ( saberent , CHAN_AUTO , saberOwner - > client - > saber [ 0 ] . soundOff ) ;
}
MakeDeadSaber ( saberent ) ;
saberent - > r . svFlags | = ( SVF_NOCLIENT ) ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
SetSaberBoxSize ( saberent ) ;
saberent - > s . loopSound = 0 ;
saberent - > s . loopIsSoundset = qfalse ;
WP_SaberRemoveG2Model ( saberent ) ;
saberOwner - > client - > ps . saberInFlight = qfalse ;
saberOwner - > client - > ps . saberEntityState = 0 ;
saberOwner - > client - > ps . saberThrowDelay = level . time + 500 ;
saberOwner - > client - > ps . saberCanThrow = qfalse ;
return ;
}
//make sure this is set alright
assert ( saberOwner - > client - > ps . saberEntityNum = = saberent - > s . number | |
saberOwner - > client - > saberStoredIndex = = saberent - > s . number ) ;
saberOwner - > client - > ps . saberEntityNum = saberent - > s . number ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
VectorSubtract ( saberent - > pos1 , saberent - > r . currentOrigin , dir ) ;
ownerLen = VectorLength ( dir ) ;
if ( saberent - > speed < level . time )
{
float baseSpeed = 900 ;
VectorNormalize ( dir ) ;
saberMoveBack ( saberent , qtrue ) ;
VectorCopy ( saberent - > r . currentOrigin , saberent - > s . pos . trBase ) ;
if ( saberOwner - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] > = FORCE_LEVEL_3 )
{ //allow players with high saber throw rank to control the return speed of the saber
baseSpeed = 900 ;
saberent - > speed = level . time ; // + 200;
}
else
{
baseSpeed = 700 ;
saberent - > speed = level . time + 50 ;
}
//Gradually slow down as it approaches, so it looks smoother coming into the hand.
if ( ownerLen < 64 )
{
VectorScale ( dir , baseSpeed - 200 , saberent - > s . pos . trDelta ) ;
}
else if ( ownerLen < 128 )
{
VectorScale ( dir , baseSpeed - 150 , saberent - > s . pos . trDelta ) ;
}
else if ( ownerLen < 256 )
{
VectorScale ( dir , baseSpeed - 100 , saberent - > s . pos . trDelta ) ;
}
else
{
VectorScale ( dir , baseSpeed , saberent - > s . pos . trDelta ) ;
}
saberent - > s . pos . trTime = level . time ;
}
/*
if ( ownerLen < = 512 )
{
saberent - > s . saberInFlight = qfalse ;
saberent - > s . loopSound = saberHumSound ;
saberent - > s . loopIsSoundset = qfalse ;
}
*/
//I'm just doing this now. I don't really like the spin on the way back. And it does weird stuff with the new saber-knocked-away code.
if ( saberOwner - > client - > ps . saberEntityNum = = saberent - > s . number )
{
if ( ! ( saberOwner - > client - > saber [ 0 ] . saberFlags & SFL_RETURN_DAMAGE )
| | saberOwner - > client - > ps . saberHolstered )
{
saberent - > s . saberInFlight = qfalse ;
}
saberent - > s . loopSound = saberOwner - > client - > saber [ 0 ] . soundLoop ;
saberent - > s . loopIsSoundset = qfalse ;
if ( ownerLen < = 32 )
{
G_Sound ( saberent , CHAN_AUTO , G_SoundIndex ( " sound/weapons/saber/saber_catch.wav " ) ) ;
saberOwner - > client - > ps . saberInFlight = qfalse ;
saberOwner - > client - > ps . saberEntityState = 0 ;
saberOwner - > client - > ps . saberCanThrow = qfalse ;
saberOwner - > client - > ps . saberThrowDelay = level . time + 300 ;
saberent - > touch = SaberGotHit ;
saberent - > think = SaberUpdateSelf ;
saberent - > genericValue5 = 0 ;
saberent - > nextthink = level . time + 50 ;
WP_SaberRemoveG2Model ( saberent ) ;
return ;
}
if ( ! saberent - > s . saberInFlight )
{
saberCheckRadiusDamage ( saberent , 1 ) ;
}
else
{
saberCheckRadiusDamage ( saberent , 2 ) ;
}
saberMoveBack ( saberent , qtrue ) ;
}
saberent - > nextthink = level . time ;
}
void saberFirstThrown ( gentity_t * saberent ) ;
void thrownSaberTouch ( gentity_t * saberent , gentity_t * other , trace_t * trace )
{
gentity_t * hitEnt = other ;
if ( other & & other - > s . number = = saberent - > r . ownerNum )
{
return ;
}
VectorClear ( saberent - > s . pos . trDelta ) ;
saberent - > s . pos . trTime = level . time ;
saberent - > s . apos . trType = TR_LINEAR ;
saberent - > s . apos . trDelta [ 0 ] = 0 ;
saberent - > s . apos . trDelta [ 1 ] = 800 ;
saberent - > s . apos . trDelta [ 2 ] = 0 ;
VectorCopy ( saberent - > r . currentOrigin , saberent - > s . pos . trBase ) ;
saberent - > think = saberBackToOwner ;
saberent - > nextthink = level . time ;
if ( other & & other - > r . ownerNum < MAX_CLIENTS & &
( other - > r . contents & CONTENTS_LIGHTSABER ) & &
g_entities [ other - > r . ownerNum ] . client & &
g_entities [ other - > r . ownerNum ] . inuse )
{
hitEnt = & g_entities [ other - > r . ownerNum ] ;
}
//we'll skip the dist check, since we don't really care about that (we just hit it physically)
CheckThrownSaberDamaged ( saberent , & g_entities [ saberent - > r . ownerNum ] , hitEnt , 256 , 0 , qtrue ) ;
saberent - > speed = 0 ;
}
# define SABER_MAX_THROW_DISTANCE 700
void saberFirstThrown ( gentity_t * saberent )
{
vec3_t vSub ;
float vLen ;
gentity_t * saberOwn = & g_entities [ saberent - > r . ownerNum ] ;
if ( saberent - > r . ownerNum = = ENTITYNUM_NONE )
{
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
if ( ! saberOwn | |
! saberOwn - > inuse | |
! saberOwn - > client | |
saberOwn - > client - > sess . sessionTeam = = TEAM_SPECTATOR )
{
MakeDeadSaber ( saberent ) ;
saberent - > think = G_FreeEntity ;
saberent - > nextthink = level . time ;
return ;
}
if ( saberOwn - > health < 1 | | ! saberOwn - > client - > ps . fd . forcePowerLevel [ FP_SABER_OFFENSE ] )
{ //He's dead, just go back to our normal saber status
saberent - > touch = SaberGotHit ;
saberent - > think = SaberUpdateSelf ;
saberent - > genericValue5 = 0 ;
saberent - > nextthink = level . time ;
if ( saberOwn - > client & &
saberOwn - > client - > saber [ 0 ] . soundOff )
{
G_Sound ( saberent , CHAN_AUTO , saberOwn - > client - > saber [ 0 ] . soundOff ) ;
}
MakeDeadSaber ( saberent ) ;
saberent - > r . svFlags | = ( SVF_NOCLIENT ) ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
SetSaberBoxSize ( saberent ) ;
saberent - > s . loopSound = 0 ;
saberent - > s . loopIsSoundset = qfalse ;
WP_SaberRemoveG2Model ( saberent ) ;
saberOwn - > client - > ps . saberInFlight = qfalse ;
saberOwn - > client - > ps . saberEntityState = 0 ;
saberOwn - > client - > ps . saberThrowDelay = level . time + 500 ;
saberOwn - > client - > ps . saberCanThrow = qfalse ;
return ;
}
if ( ( level . time - saberOwn - > client - > ps . saberDidThrowTime ) > 500 )
{
if ( ! ( saberOwn - > client - > buttons & BUTTON_ALT_ATTACK ) )
{ //If owner releases altattack 500ms or later after throwing saber, it autoreturns
thrownSaberTouch ( saberent , saberent , NULL ) ;
goto runMin ;
}
else if ( ( level . time - saberOwn - > client - > ps . saberDidThrowTime ) > 6000 )
{ //if it's out longer than 6 seconds, return it
thrownSaberTouch ( saberent , saberent , NULL ) ;
goto runMin ;
}
}
if ( BG_HasYsalamiri ( g_gametype . integer , & saberOwn - > client - > ps ) )
{
thrownSaberTouch ( saberent , saberent , NULL ) ;
goto runMin ;
}
if ( ! BG_CanUseFPNow ( g_gametype . integer , & saberOwn - > client - > ps , level . time , FP_SABERTHROW ) )
{
thrownSaberTouch ( saberent , saberent , NULL ) ;
goto runMin ;
}
VectorSubtract ( saberOwn - > client - > ps . origin , saberent - > r . currentOrigin , vSub ) ;
vLen = VectorLength ( vSub ) ;
if ( vLen > = ( SABER_MAX_THROW_DISTANCE * saberOwn - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] ) )
{
thrownSaberTouch ( saberent , saberent , NULL ) ;
goto runMin ;
}
if ( saberOwn - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] > = FORCE_LEVEL_2 & &
saberent - > speed < level . time )
{ //if owner is rank 3 in saber throwing, the saber goes where he points
vec3_t fwd , traceFrom , traceTo , dir ;
trace_t tr ;
AngleVectors ( saberOwn - > client - > ps . viewangles , fwd , 0 , 0 ) ;
VectorCopy ( saberOwn - > client - > ps . origin , traceFrom ) ;
traceFrom [ 2 ] + = saberOwn - > client - > ps . viewheight ;
VectorCopy ( traceFrom , traceTo ) ;
traceTo [ 0 ] + = fwd [ 0 ] * 4096 ;
traceTo [ 1 ] + = fwd [ 1 ] * 4096 ;
traceTo [ 2 ] + = fwd [ 2 ] * 4096 ;
saberMoveBack ( saberent , qfalse ) ;
VectorCopy ( saberent - > r . currentOrigin , saberent - > s . pos . trBase ) ;
if ( saberOwn - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] > = FORCE_LEVEL_3 )
{ //if highest saber throw rank, we can direct the saber toward players directly by looking at them
trap_Trace ( & tr , traceFrom , NULL , NULL , traceTo , saberOwn - > s . number , MASK_PLAYERSOLID ) ;
}
else
{
trap_Trace ( & tr , traceFrom , NULL , NULL , traceTo , saberOwn - > s . number , MASK_SOLID ) ;
}
VectorSubtract ( tr . endpos , saberent - > r . currentOrigin , dir ) ;
VectorNormalize ( dir ) ;
VectorScale ( dir , 500 , saberent - > s . pos . trDelta ) ;
saberent - > s . pos . trTime = level . time ;
if ( saberOwn - > client - > ps . fd . forcePowerLevel [ FP_SABERTHROW ] > = FORCE_LEVEL_3 )
{ //we'll treat them to a quicker update rate if their throw rank is high enough
saberent - > speed = level . time + 100 ;
}
else
{
saberent - > speed = level . time + 400 ;
}
}
runMin :
saberCheckRadiusDamage ( saberent , 0 ) ;
G_RunObject ( saberent ) ;
}
void UpdateClientRenderBolts ( gentity_t * self , vec3_t renderOrigin , vec3_t renderAngles )
{
mdxaBone_t boltMatrix ;
renderInfo_t * ri = & self - > client - > renderInfo ;
if ( ! self - > ghoul2 )
{
VectorCopy ( self - > client - > ps . origin , ri - > headPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > handRPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > handLPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > torsoPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > crotchPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > footRPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > footLPoint ) ;
}
else
{
//head
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > headBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > headPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > headPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > headPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//right hand
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > handRBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > handRPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > handRPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > handRPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//left hand
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > handLBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > handLPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > handLPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > handLPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//chest
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > torsoBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > torsoPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > torsoPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > torsoPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//crotch
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > crotchBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > crotchPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > crotchPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > crotchPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//right foot
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > footRBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > footRPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > footRPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > footRPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//left foot
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > footLBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > footLPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > footLPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > footLPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
}
self - > client - > renderInfo . boltValidityTime = level . time ;
}
void UpdateClientRenderinfo ( gentity_t * self , vec3_t renderOrigin , vec3_t renderAngles )
{
renderInfo_t * ri = & self - > client - > renderInfo ;
if ( ri - > mPCalcTime < level . time )
{
//We're just going to give rough estimates on most of this stuff,
//it's not like most of it matters.
#if 0 //#if 0'd since it's a waste setting all this to 0 each frame.
//Should you wish to make any of this valid then feel free to do so.
ri - > headYawRangeLeft = ri - > headYawRangeRight = ri - > headPitchRangeUp = ri - > headPitchRangeDown = 0 ;
ri - > torsoYawRangeLeft = ri - > torsoYawRangeRight = ri - > torsoPitchRangeUp = ri - > torsoPitchRangeDown = 0 ;
ri - > torsoFpsMod = ri - > legsFpsMod = 0 ;
VectorClear ( ri - > customRGB ) ;
ri - > customAlpha = 0 ;
ri - > renderFlags = 0 ;
ri - > lockYaw = 0 ;
VectorClear ( ri - > headAngles ) ;
VectorClear ( ri - > torsoAngles ) ;
//VectorClear(ri->eyeAngles);
ri - > legsYaw = 0 ;
# endif
if ( self - > ghoul2 & &
self - > ghoul2 ! = ri - > lastG2 )
{ //the g2 instance changed, so update all the bolts.
//rwwFIXMEFIXME: Base on skeleton used? Assuming humanoid currently.
ri - > lastG2 = self - > ghoul2 ;
if ( self - > localAnimIndex < = 1 )
{
ri - > headBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " *head_eyes " ) ;
ri - > handRBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " *r_hand " ) ;
ri - > handLBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " *l_hand " ) ;
ri - > torsoBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " thoracic " ) ;
ri - > crotchBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " pelvis " ) ;
ri - > footRBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " *r_leg_foot " ) ;
ri - > footLBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " *l_leg_foot " ) ;
ri - > motionBolt = trap_G2API_AddBolt ( self - > ghoul2 , 0 , " Motion " ) ;
}
else
{
ri - > headBolt = - 1 ;
ri - > handRBolt = - 1 ;
ri - > handLBolt = - 1 ;
ri - > torsoBolt = - 1 ;
ri - > crotchBolt = - 1 ;
ri - > footRBolt = - 1 ;
ri - > footLBolt = - 1 ;
ri - > motionBolt = - 1 ;
}
ri - > lastG2 = self - > ghoul2 ;
}
VectorCopy ( self - > client - > ps . viewangles , self - > client - > renderInfo . eyeAngles ) ;
//we'll just say the legs/torso are whatever the first frame of our current anim is.
ri - > torsoFrame = bgAllAnims [ self - > localAnimIndex ] . anims [ self - > client - > ps . torsoAnim ] . firstFrame ;
ri - > legsFrame = bgAllAnims [ self - > localAnimIndex ] . anims [ self - > client - > ps . legsAnim ] . firstFrame ;
if ( g_debugServerSkel . integer )
{ //Alright, I was doing this, but it's just too slow to do every frame.
//From now on if we want this data to be valid we're going to have to make a verify call for it before
//accessing it. I'm only doing this now if we want to debug the server skel by drawing lines from bolt
//positions every frame.
mdxaBone_t boltMatrix ;
if ( ! self - > ghoul2 )
{
VectorCopy ( self - > client - > ps . origin , ri - > headPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > handRPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > handLPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > torsoPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > crotchPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > footRPoint ) ;
VectorCopy ( self - > client - > ps . origin , ri - > footLPoint ) ;
}
else
{
//head
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > headBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > headPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > headPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > headPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//right hand
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > handRBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > handRPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > handRPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > handRPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//left hand
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > handLBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > handLPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > handLPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > handLPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//chest
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > torsoBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > torsoPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > torsoPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > torsoPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//crotch
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > crotchBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > crotchPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > crotchPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > crotchPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//right foot
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > footRBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > footRPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > footRPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > footRPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
//left foot
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > footLBolt , & boltMatrix , renderAngles , renderOrigin , level . time , NULL , self - > modelScale ) ;
ri - > footLPoint [ 0 ] = boltMatrix . matrix [ 0 ] [ 3 ] ;
ri - > footLPoint [ 1 ] = boltMatrix . matrix [ 1 ] [ 3 ] ;
ri - > footLPoint [ 2 ] = boltMatrix . matrix [ 2 ] [ 3 ] ;
}
//Now draw the skel for debug
G_TestLine ( ri - > headPoint , ri - > torsoPoint , 0x000000ff , 50 ) ;
G_TestLine ( ri - > torsoPoint , ri - > handRPoint , 0x000000ff , 50 ) ;
G_TestLine ( ri - > torsoPoint , ri - > handLPoint , 0x000000ff , 50 ) ;
G_TestLine ( ri - > torsoPoint , ri - > crotchPoint , 0x000000ff , 50 ) ;
G_TestLine ( ri - > crotchPoint , ri - > footRPoint , 0x000000ff , 50 ) ;
G_TestLine ( ri - > crotchPoint , ri - > footLPoint , 0x000000ff , 50 ) ;
}
//muzzle point calc (we are going to be cheap here)
VectorCopy ( ri - > muzzlePoint , ri - > muzzlePointOld ) ;
VectorCopy ( self - > client - > ps . origin , ri - > muzzlePoint ) ;
VectorCopy ( ri - > muzzleDir , ri - > muzzleDirOld ) ;
AngleVectors ( self - > client - > ps . viewangles , ri - > muzzleDir , 0 , 0 ) ;
ri - > mPCalcTime = level . time ;
VectorCopy ( self - > client - > ps . origin , ri - > eyePoint ) ;
ri - > eyePoint [ 2 ] + = self - > client - > ps . viewheight ;
}
}
# define STAFF_KICK_RANGE 16
extern void G_GetBoltPosition ( gentity_t * self , int boltIndex , vec3_t pos , int modelIndex ) ; //NPC_utils.c
extern qboolean BG_InKnockDown ( int anim ) ;
static qboolean G_KickDownable ( gentity_t * ent )
{
if ( ! d_saberKickTweak . integer )
{
return qtrue ;
}
if ( ! ent | | ! ent - > inuse | | ! ent - > client )
{
return qfalse ;
}
if ( BG_InKnockDown ( ent - > client - > ps . legsAnim ) | |
BG_InKnockDown ( ent - > client - > ps . torsoAnim ) )
{
return qfalse ;
}
if ( ent - > client - > ps . weaponTime < = 0 & &
ent - > client - > ps . weapon = = WP_SABER & &
ent - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE )
{
return qfalse ;
}
return qtrue ;
}
static void G_TossTheMofo ( gentity_t * ent , vec3_t tossDir , float tossStr )
{
if ( ! ent - > inuse | | ! ent - > client )
{ //no good
return ;
}
if ( ent - > s . eType = = ET_NPC & & ent - > s . NPC_class = = CLASS_VEHICLE )
{ //no, silly
return ;
}
VectorMA ( ent - > client - > ps . velocity , tossStr , tossDir , ent - > client - > ps . velocity ) ;
ent - > client - > ps . velocity [ 2 ] = 200 ;
if ( ent - > health > 0 & & ent - > client - > ps . forceHandExtend ! = HANDEXTEND_KNOCKDOWN & &
BG_KnockDownable ( & ent - > client - > ps ) & &
G_KickDownable ( ent ) )
{ //if they are alive, knock them down I suppose
ent - > client - > ps . forceHandExtend = HANDEXTEND_KNOCKDOWN ;
ent - > client - > ps . forceHandExtendTime = level . time + 700 ;
ent - > client - > ps . forceDodgeAnim = 0 ; //this toggles between 1 and 0, when it's 1 we should play the get up anim
//ent->client->ps.quickerGetup = qtrue;
}
}
static gentity_t * G_KickTrace ( gentity_t * ent , vec3_t kickDir , float kickDist , vec3_t kickEnd , int kickDamage , float kickPush )
{
vec3_t traceOrg , traceEnd , kickMins , kickMaxs ;
trace_t trace ;
gentity_t * hitEnt = NULL ;
VectorSet ( kickMins , - 2.0f , - 2.0f , - 2.0f ) ;
VectorSet ( kickMaxs , 2.0f , 2.0f , 2.0f ) ;
//FIXME: variable kick height?
if ( kickEnd & & ! VectorCompare ( kickEnd , vec3_origin ) )
{ //they passed us the end point of the trace, just use that
//this makes the trace flat
VectorSet ( traceOrg , ent - > r . currentOrigin [ 0 ] , ent - > r . currentOrigin [ 1 ] , kickEnd [ 2 ] ) ;
VectorCopy ( kickEnd , traceEnd ) ;
}
else
{ //extrude
VectorSet ( traceOrg , ent - > r . currentOrigin [ 0 ] , ent - > r . currentOrigin [ 1 ] , ent - > r . currentOrigin [ 2 ] + ent - > r . maxs [ 2 ] * 0.5f ) ;
VectorMA ( traceOrg , kickDist , kickDir , traceEnd ) ;
}
if ( d_saberKickTweak . integer )
{
trap_G2Trace ( & trace , traceOrg , kickMins , kickMaxs , traceEnd , ent - > s . number , MASK_SHOT , G2TRFLAG_DOGHOULTRACE | G2TRFLAG_GETSURFINDEX | G2TRFLAG_THICK | G2TRFLAG_HITCORPSES , g_g2TraceLod . integer ) ;
}
else
{
trap_Trace ( & trace , traceOrg , kickMins , kickMaxs , traceEnd , ent - > s . number , MASK_SHOT ) ;
}
//G_TestLine(traceOrg, traceEnd, 0x0000ff, 5000);
if ( trace . fraction < 1.0f & & ! trace . startsolid & & ! trace . allsolid )
{
if ( ent - > client - > jediKickTime > level . time )
{
if ( trace . entityNum = = ent - > client - > jediKickIndex )
{ //we are hitting the same ent we last hit in this same anim, don't hit it again
return NULL ;
}
}
ent - > client - > jediKickIndex = trace . entityNum ;
ent - > client - > jediKickTime = level . time + ent - > client - > ps . legsTimer ;
hitEnt = & g_entities [ trace . entityNum ] ;
//FIXME: regardless of what we hit, do kick hit sound and impact effect
//G_PlayEffect( "misc/kickHit", trace.endpos, trace.plane.normal );
if ( ent - > client - > ps . torsoAnim = = BOTH_A7_HILT )
{
G_Sound ( ent , CHAN_AUTO , G_SoundIndex ( " sound/movers/objects/saber_slam " ) ) ;
}
else
{
G_Sound ( ent , CHAN_AUTO , G_SoundIndex ( va ( " sound/weapons/melee/punch%d " , Q_irand ( 1 , 4 ) ) ) ) ;
}
if ( hitEnt - > inuse )
{ //we hit an entity
//FIXME: don't hit same ent more than once per kick
if ( hitEnt - > takedamage )
{ //hurt it
if ( hitEnt - > client )
{
hitEnt - > client - > ps . otherKiller = ent - > s . number ;
hitEnt - > client - > ps . otherKillerDebounceTime = level . time + 10000 ;
hitEnt - > client - > ps . otherKillerTime = level . time + 10000 ;
hitEnt - > client - > otherKillerMOD = MOD_MELEE ;
hitEnt - > client - > otherKillerVehWeapon = 0 ;
hitEnt - > client - > otherKillerWeaponType = WP_NONE ;
}
if ( d_saberKickTweak . integer )
{
G_Damage ( hitEnt , ent , ent , kickDir , trace . endpos , kickDamage * 0.2f , DAMAGE_NO_KNOCKBACK , MOD_MELEE ) ;
}
else
{
G_Damage ( hitEnt , ent , ent , kickDir , trace . endpos , kickDamage , DAMAGE_NO_KNOCKBACK , MOD_MELEE ) ;
}
}
if ( hitEnt - > client
& & ! ( hitEnt - > client - > ps . pm_flags & PMF_TIME_KNOCKBACK ) //not already flying through air? Intended to stop multiple hits, but...
& & G_CanBeEnemy ( ent , hitEnt ) )
{ //FIXME: this should not always work
if ( hitEnt - > health < = 0 )
{ //we kicked a dead guy
//throw harder - FIXME: no matter how hard I push them, they don't go anywhere... corpses use less physics???
// G_Throw( hitEnt, kickDir, kickPush*4 );
//see if we should play a better looking death on them
// G_ThrownDeathAnimForDeathAnim( hitEnt, trace.endpos );
G_TossTheMofo ( hitEnt , kickDir , kickPush * 4.0f ) ;
}
else
{
/*
G_Throw ( hitEnt , kickDir , kickPush ) ;
if ( kickPush > = 75.0f & & ! Q_irand ( 0 , 2 ) )
{
G_Knockdown ( hitEnt , ent , kickDir , 300 , qtrue ) ;
}
else
{
G_Knockdown ( hitEnt , ent , kickDir , kickPush , qtrue ) ;
}
*/
if ( kickPush > = 75.0f & & ! Q_irand ( 0 , 2 ) )
{
G_TossTheMofo ( hitEnt , kickDir , 300.0f ) ;
}
else
{
G_TossTheMofo ( hitEnt , kickDir , kickPush ) ;
}
}
}
}
}
return ( hitEnt ) ;
}
static void G_KickSomeMofos ( gentity_t * ent )
{
vec3_t kickDir , kickEnd , fwdAngs ;
float animLength = BG_AnimLength ( ent - > localAnimIndex , ( animNumber_t ) ent - > client - > ps . legsAnim ) ;
float elapsedTime = ( float ) ( animLength - ent - > client - > ps . legsTimer ) ;
float remainingTime = ( animLength - elapsedTime ) ;
float kickDist = ( ent - > r . maxs [ 0 ] * 1.5f ) + STAFF_KICK_RANGE + 8.0f ; //fudge factor of 8
int kickDamage = Q_irand ( 10 , 15 ) ; //Q_irand( 3, 8 ); //since it can only hit a guy once now
int kickPush = flrand ( 50.0f , 100.0f ) ;
qboolean doKick = qfalse ;
renderInfo_t * ri = & ent - > client - > renderInfo ;
VectorSet ( kickDir , 0.0f , 0.0f , 0.0f ) ;
VectorSet ( kickEnd , 0.0f , 0.0f , 0.0f ) ;
VectorSet ( fwdAngs , 0.0f , ent - > client - > ps . viewangles [ YAW ] , 0.0f ) ;
//HMM... or maybe trace from origin to footRBolt/footLBolt? Which one? G2 trace? Will do hitLoc, if so...
if ( ent - > client - > ps . torsoAnim = = BOTH_A7_HILT )
{
if ( elapsedTime > = 250 & & remainingTime > = 250 )
{ //front
doKick = qtrue ;
if ( ri - > handRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > handRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > client - > ps . origin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
}
}
else
{
switch ( ent - > client - > ps . legsAnim )
{
case BOTH_GETUP_BROLL_B :
case BOTH_GETUP_BROLL_F :
case BOTH_GETUP_FROLL_B :
case BOTH_GETUP_FROLL_F :
if ( elapsedTime > = 250 & & remainingTime > = 250 )
{ //front
doKick = qtrue ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > client - > ps . origin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
}
break ;
case BOTH_A7_KICK_F_AIR :
case BOTH_A7_KICK_B_AIR :
case BOTH_A7_KICK_R_AIR :
case BOTH_A7_KICK_L_AIR :
if ( elapsedTime > = 100 & & remainingTime > = 250 )
{ //air
doKick = qtrue ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
}
break ;
case BOTH_A7_KICK_F :
//FIXME: push forward?
if ( elapsedTime > = 250 & & remainingTime > = 250 )
{ //front
doKick = qtrue ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
}
break ;
case BOTH_A7_KICK_B :
//FIXME: push back?
if ( elapsedTime > = 250 & & remainingTime > = 250 )
{ //back
doKick = qtrue ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
}
break ;
case BOTH_A7_KICK_R :
//FIXME: push right?
if ( elapsedTime > = 250 & & remainingTime > = 250 )
{ //right
doKick = qtrue ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
}
}
break ;
case BOTH_A7_KICK_L :
//FIXME: push left?
if ( elapsedTime > = 250 & & remainingTime > = 250 )
{ //left
doKick = qtrue ;
if ( ri - > footLBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footLBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
}
break ;
case BOTH_A7_KICK_S :
kickPush = flrand ( 75.0f , 125.0f ) ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
if ( elapsedTime > = 550
& & elapsedTime < = 1050 )
{
doKick = qtrue ;
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
//NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
VectorMA ( kickEnd , 8.0f , kickDir , kickEnd ) ;
}
}
else
{ //guess
if ( elapsedTime > = 400 & & elapsedTime < 500 )
{ //front
doKick = qtrue ;
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
else if ( elapsedTime > = 500 & & elapsedTime < 600 )
{ //front-right?
doKick = qtrue ;
fwdAngs [ YAW ] + = 45 ;
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
else if ( elapsedTime > = 600 & & elapsedTime < 700 )
{ //right
doKick = qtrue ;
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
}
else if ( elapsedTime > = 700 & & elapsedTime < 800 )
{ //back-right?
doKick = qtrue ;
fwdAngs [ YAW ] + = 45 ;
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
}
else if ( elapsedTime > = 800 & & elapsedTime < 900 )
{ //back
doKick = qtrue ;
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
else if ( elapsedTime > = 900 & & elapsedTime < 1000 )
{ //back-left?
doKick = qtrue ;
fwdAngs [ YAW ] + = 45 ;
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
else if ( elapsedTime > = 1000 & & elapsedTime < 1100 )
{ //left
doKick = qtrue ;
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
else if ( elapsedTime > = 1100 & & elapsedTime < 1200 )
{ //front-left?
doKick = qtrue ;
fwdAngs [ YAW ] + = 45 ;
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
}
break ;
case BOTH_A7_KICK_BF :
kickPush = flrand ( 75.0f , 125.0f ) ;
kickDist + = 20.0f ;
if ( elapsedTime < 1500 )
{ //auto-aim!
// overridAngles = PM_AdjustAnglesForBFKick( ent, ucmd, fwdAngs, qboolean(elapsedTime<850) )?qtrue:overridAngles;
//FIXME: if we haven't done the back kick yet and there's no-one there to
// kick anymore, go into some anim that returns us to our base stance
}
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
if ( ( elapsedTime > = 750 & & elapsedTime < 850 )
| | ( elapsedTime > = 1400 & & elapsedTime < 1500 ) )
{ //right, though either would do
doKick = qtrue ;
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
//NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
VectorMA ( kickEnd , 8 , kickDir , kickEnd ) ;
}
}
else
{ //guess
if ( elapsedTime > = 250 & & elapsedTime < 350 )
{ //front
doKick = qtrue ;
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
}
else if ( elapsedTime > = 350 & & elapsedTime < 450 )
{ //back
doKick = qtrue ;
AngleVectors ( fwdAngs , kickDir , NULL , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
}
break ;
case BOTH_A7_KICK_RL :
kickPush = flrand ( 75.0f , 125.0f ) ;
kickDist + = 10.0f ;
//ok, I'm tracing constantly on these things, they NEVER hit otherwise (in MP at least)
//FIXME: auto aim at enemies on the side of us?
//overridAngles = PM_AdjustAnglesForRLKick( ent, ucmd, fwdAngs, qboolean(elapsedTime<850) )?qtrue:overridAngles;
//if ( elapsedTime >= 250 && elapsedTime < 350 )
if ( level . framenum & 1 )
{ //right
doKick = qtrue ;
if ( ri - > footRBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footRBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
//NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
VectorMA ( kickEnd , 8 , kickDir , kickEnd ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
}
}
//else if ( elapsedTime >= 350 && elapsedTime < 450 )
else
{ //left
doKick = qtrue ;
if ( ri - > footLBolt ! = - 1 )
{ //actually trace to a bolt
G_GetBoltPosition ( ent , ri - > footLBolt , kickEnd , 0 ) ;
VectorSubtract ( kickEnd , ent - > r . currentOrigin , kickDir ) ;
kickDir [ 2 ] = 0 ; //ah, flatten it, I guess...
VectorNormalize ( kickDir ) ;
//NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
VectorMA ( kickEnd , 8 , kickDir , kickEnd ) ;
}
else
{ //guess
AngleVectors ( fwdAngs , NULL , kickDir , NULL ) ;
VectorScale ( kickDir , - 1 , kickDir ) ;
}
}
break ;
}
}
if ( doKick )
{
// G_KickTrace( ent, kickDir, kickDist, kickEnd, kickDamage, kickPush );
G_KickTrace ( ent , kickDir , kickDist , NULL , kickDamage , kickPush ) ;
}
}
static GAME_INLINE qboolean G_PrettyCloseIGuess ( float a , float b , float tolerance )
{
if ( ( a - b ) < tolerance & &
( a - b ) > - tolerance )
{
return qtrue ;
}
return qfalse ;
}
static void G_GrabSomeMofos ( gentity_t * self )
{
renderInfo_t * ri = & self - > client - > renderInfo ;
mdxaBone_t boltMatrix ;
vec3_t flatAng ;
vec3_t pos ;
vec3_t grabMins , grabMaxs ;
trace_t trace ;
if ( ! self - > ghoul2 | | ri - > handRBolt = = - 1 )
{ //no good
return ;
}
VectorSet ( flatAng , 0.0f , self - > client - > ps . viewangles [ 1 ] , 0.0f ) ;
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 0 , ri - > handRBolt , & boltMatrix , flatAng , self - > client - > ps . origin ,
level . time , NULL , self - > modelScale ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , ORIGIN , pos ) ;
VectorSet ( grabMins , - 4.0f , - 4.0f , - 4.0f ) ;
VectorSet ( grabMaxs , 4.0f , 4.0f , 4.0f ) ;
//trace from my origin to my hand, if we hit anyone then get 'em
trap_G2Trace ( & trace , self - > client - > ps . origin , grabMins , grabMaxs , pos , self - > s . number , MASK_SHOT , G2TRFLAG_DOGHOULTRACE | G2TRFLAG_GETSURFINDEX | G2TRFLAG_THICK | G2TRFLAG_HITCORPSES , g_g2TraceLod . integer ) ;
if ( trace . fraction ! = 1.0f & &
trace . entityNum < ENTITYNUM_WORLD )
{
gentity_t * grabbed = & g_entities [ trace . entityNum ] ;
if ( grabbed - > inuse & & ( grabbed - > s . eType = = ET_PLAYER | | grabbed - > s . eType = = ET_NPC ) & &
grabbed - > client & & grabbed - > health > 0 & &
G_CanBeEnemy ( self , grabbed ) & &
G_PrettyCloseIGuess ( grabbed - > client - > ps . origin [ 2 ] , self - > client - > ps . origin [ 2 ] , 4.0f ) & &
( ! BG_InGrappleMove ( grabbed - > client - > ps . torsoAnim ) | | grabbed - > client - > ps . torsoAnim = = BOTH_KYLE_GRAB ) & &
( ! BG_InGrappleMove ( grabbed - > client - > ps . legsAnim ) | | grabbed - > client - > ps . legsAnim = = BOTH_KYLE_GRAB ) )
{ //grabbed an active player/npc
int tortureAnim = - 1 ;
int correspondingAnim = - 1 ;
if ( self - > client - > pers . cmd . forwardmove > 0 )
{ //punch grab
tortureAnim = BOTH_KYLE_PA_1 ;
correspondingAnim = BOTH_PLAYER_PA_1 ;
}
else if ( self - > client - > pers . cmd . forwardmove < 0 )
{ //knee-throw
tortureAnim = BOTH_KYLE_PA_2 ;
correspondingAnim = BOTH_PLAYER_PA_2 ;
}
if ( tortureAnim = = - 1 | | correspondingAnim = = - 1 )
{
if ( self - > client - > ps . torsoTimer < 300 & & ! self - > client - > grappleState )
{ //you failed to grab anyone, play the "failed to grab" anim
G_SetAnim ( self , & self - > client - > pers . cmd , SETANIM_BOTH , BOTH_KYLE_MISS , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_MISS )
{ //providing the anim set succeeded..
self - > client - > ps . weaponTime = self - > client - > ps . torsoTimer ;
}
}
return ;
}
self - > client - > grappleIndex = grabbed - > s . number ;
self - > client - > grappleState = 1 ;
grabbed - > client - > grappleIndex = self - > s . number ;
grabbed - > client - > grappleState = 20 ;
//time to crack some heads
G_SetAnim ( self , & self - > client - > pers . cmd , SETANIM_BOTH , tortureAnim , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
if ( self - > client - > ps . torsoAnim = = tortureAnim )
{ //providing the anim set succeeded..
self - > client - > ps . weaponTime = self - > client - > ps . torsoTimer ;
}
G_SetAnim ( grabbed , & grabbed - > client - > pers . cmd , SETANIM_BOTH , correspondingAnim , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
if ( grabbed - > client - > ps . torsoAnim = = correspondingAnim )
{ //providing the anim set succeeded..
if ( grabbed - > client - > ps . weapon = = WP_SABER )
{ //turn it off
if ( ! grabbed - > client - > ps . saberHolstered )
{
grabbed - > client - > ps . saberHolstered = 2 ;
if ( grabbed - > client - > saber [ 0 ] . soundOff )
{
G_Sound ( grabbed , CHAN_AUTO , grabbed - > client - > saber [ 0 ] . soundOff ) ;
}
if ( grabbed - > client - > saber [ 1 ] . soundOff & &
grabbed - > client - > saber [ 1 ] . model [ 0 ] )
{
G_Sound ( grabbed , CHAN_AUTO , grabbed - > client - > saber [ 1 ] . soundOff ) ;
}
}
}
if ( grabbed - > client - > ps . torsoTimer < self - > client - > ps . torsoTimer )
{ //make sure they stay in the anim at least as long as the grabber
grabbed - > client - > ps . torsoTimer = self - > client - > ps . torsoTimer ;
}
grabbed - > client - > ps . weaponTime = grabbed - > client - > ps . torsoTimer ;
}
}
}
if ( self - > client - > ps . torsoTimer < 300 & & ! self - > client - > grappleState )
{ //you failed to grab anyone, play the "failed to grab" anim
G_SetAnim ( self , & self - > client - > pers . cmd , SETANIM_BOTH , BOTH_KYLE_MISS , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_MISS )
{ //providing the anim set succeeded..
self - > client - > ps . weaponTime = self - > client - > ps . torsoTimer ;
}
}
}
void WP_SaberPositionUpdate ( gentity_t * self , usercmd_t * ucmd )
{ //rww - keep the saber position as updated as possible on the server so that we can try to do realistic-looking contact stuff
//Note that this function also does the majority of working in maintaining the server g2 client instance (updating angles/anims/etc)
gentity_t * mySaber = NULL ;
mdxaBone_t boltMatrix ;
vec3_t properAngles , properOrigin ;
vec3_t boltAngles , boltOrigin ;
vec3_t end ;
vec3_t legAxis [ 3 ] ;
vec3_t addVel ;
vec3_t rawAngles ;
float fVSpeed = 0 ;
int returnAfterUpdate = 0 ;
float animSpeedScale = 1.0f ;
int saberNum ;
qboolean clientOverride ;
gentity_t * vehEnt = NULL ;
int rSaberNum = 0 ;
int rBladeNum = 0 ;
# ifdef _DEBUG
if ( g_disableServerG2 . integer )
{
return ;
}
# endif
if ( self & & self - > inuse & & self - > client )
{
if ( self - > client - > saberCycleQueue )
{
self - > client - > ps . fd . saberDrawAnimLevel = self - > client - > saberCycleQueue ;
}
else
{
self - > client - > ps . fd . saberDrawAnimLevel = self - > client - > ps . fd . saberAnimLevel ;
}
}
if ( self & &
self - > inuse & &
self - > client & &
self - > client - > saberCycleQueue & &
( self - > client - > ps . weaponTime < = 0 | | self - > health < 1 ) )
{ //we cycled attack levels while we were busy, so update now that we aren't (even if that means we're dead)
self - > client - > ps . fd . saberAnimLevel = self - > client - > saberCycleQueue ;
self - > client - > saberCycleQueue = 0 ;
}
if ( ! self | |
! self - > inuse | |
! self - > client | |
! self - > ghoul2 | |
! g2SaberInstance )
{
return ;
}
if ( BG_KickingAnim ( self - > client - > ps . legsAnim ) )
{ //do some kick traces and stuff if we're in the appropriate anim
G_KickSomeMofos ( self ) ;
}
else if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_GRAB )
{ //try to grab someone
G_GrabSomeMofos ( self ) ;
}
else if ( self - > client - > grappleState )
{
gentity_t * grappler = & g_entities [ self - > client - > grappleIndex ] ;
if ( ! grappler - > inuse | | ! grappler - > client | | grappler - > client - > grappleIndex ! = self - > s . number | |
! BG_InGrappleMove ( grappler - > client - > ps . torsoAnim ) | | ! BG_InGrappleMove ( grappler - > client - > ps . legsAnim ) | |
! BG_InGrappleMove ( self - > client - > ps . torsoAnim ) | | ! BG_InGrappleMove ( self - > client - > ps . legsAnim ) | |
! self - > client - > grappleState | | ! grappler - > client - > grappleState | |
grappler - > health < 1 | | self - > health < 1 | |
! G_PrettyCloseIGuess ( self - > client - > ps . origin [ 2 ] , grappler - > client - > ps . origin [ 2 ] , 4.0f ) )
{
self - > client - > grappleState = 0 ;
if ( ( BG_InGrappleMove ( self - > client - > ps . torsoAnim ) & & self - > client - > ps . torsoTimer > 100 ) | |
( BG_InGrappleMove ( self - > client - > ps . legsAnim ) & & self - > client - > ps . legsTimer > 100 ) )
{ //if they're pretty far from finishing the anim then shove them into another anim
G_SetAnim ( self , & self - > client - > pers . cmd , SETANIM_BOTH , BOTH_KYLE_MISS , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_MISS )
{ //providing the anim set succeeded..
self - > client - > ps . weaponTime = self - > client - > ps . torsoTimer ;
}
}
}
else
{
vec3_t grapAng ;
VectorSubtract ( grappler - > client - > ps . origin , self - > client - > ps . origin , grapAng ) ;
if ( VectorLength ( grapAng ) > 64.0f )
{ //too far away, break it off
if ( ( BG_InGrappleMove ( self - > client - > ps . torsoAnim ) & & self - > client - > ps . torsoTimer > 100 ) | |
( BG_InGrappleMove ( self - > client - > ps . legsAnim ) & & self - > client - > ps . legsTimer > 100 ) )
{
self - > client - > grappleState = 0 ;
G_SetAnim ( self , & self - > client - > pers . cmd , SETANIM_BOTH , BOTH_KYLE_MISS , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD , 0 ) ;
if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_MISS )
{ //providing the anim set succeeded..
self - > client - > ps . weaponTime = self - > client - > ps . torsoTimer ;
}
}
}
else
{
vectoangles ( grapAng , grapAng ) ;
SetClientViewAngle ( self , grapAng ) ;
if ( self - > client - > grappleState > = 20 )
{ //grapplee
//try to position myself at the correct distance from my grappler
float idealDist ;
vec3_t gFwd , idealSpot ;
trace_t trace ;
if ( grappler - > client - > ps . torsoAnim = = BOTH_KYLE_PA_1 )
{ //grab punch
idealDist = 46.0f ;
}
else
{ //knee-throw
idealDist = 34.0f ;
}
AngleVectors ( grappler - > client - > ps . viewangles , gFwd , 0 , 0 ) ;
VectorMA ( grappler - > client - > ps . origin , idealDist , gFwd , idealSpot ) ;
trap_Trace ( & trace , self - > client - > ps . origin , self - > r . mins , self - > r . maxs , idealSpot , self - > s . number , self - > clipmask ) ;
if ( ! trace . startsolid & & ! trace . allsolid & & trace . fraction = = 1.0f )
{ //go there
G_SetOrigin ( self , idealSpot ) ;
VectorCopy ( idealSpot , self - > client - > ps . origin ) ;
}
}
else if ( self - > client - > grappleState > = 1 )
{ //grappler
if ( grappler - > client - > ps . weapon = = WP_SABER )
{ //make sure their saber is shut off
if ( ! grappler - > client - > ps . saberHolstered )
{
grappler - > client - > ps . saberHolstered = 2 ;
if ( grappler - > client - > saber [ 0 ] . soundOff )
{
G_Sound ( grappler , CHAN_AUTO , grappler - > client - > saber [ 0 ] . soundOff ) ;
}
if ( grappler - > client - > saber [ 1 ] . soundOff & &
grappler - > client - > saber [ 1 ] . model [ 0 ] )
{
G_Sound ( grappler , CHAN_AUTO , grappler - > client - > saber [ 1 ] . soundOff ) ;
}
}
}
//check for smashy events
if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_PA_1 )
{ //grab punch
if ( self - > client - > grappleState = = 1 )
{ //smack
if ( self - > client - > ps . torsoTimer < 3400 )
{
int grapplerAnim = grappler - > client - > ps . torsoAnim ;
int grapplerTime = grappler - > client - > ps . torsoTimer ;
G_Damage ( grappler , self , self , NULL , self - > client - > ps . origin , 10 , 0 , MOD_MELEE ) ;
//G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
//it might try to put them into a pain anim or something, so override it back again
if ( grappler - > health > 0 )
{
grappler - > client - > ps . torsoAnim = grapplerAnim ;
grappler - > client - > ps . torsoTimer = grapplerTime ;
grappler - > client - > ps . legsAnim = grapplerAnim ;
grappler - > client - > ps . legsTimer = grapplerTime ;
grappler - > client - > ps . weaponTime = grapplerTime ;
}
self - > client - > grappleState + + ;
}
}
else if ( self - > client - > grappleState = = 2 )
{ //smack!
if ( self - > client - > ps . torsoTimer < 2550 )
{
int grapplerAnim = grappler - > client - > ps . torsoAnim ;
int grapplerTime = grappler - > client - > ps . torsoTimer ;
G_Damage ( grappler , self , self , NULL , self - > client - > ps . origin , 10 , 0 , MOD_MELEE ) ;
//G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
//it might try to put them into a pain anim or something, so override it back again
if ( grappler - > health > 0 )
{
grappler - > client - > ps . torsoAnim = grapplerAnim ;
grappler - > client - > ps . torsoTimer = grapplerTime ;
grappler - > client - > ps . legsAnim = grapplerAnim ;
grappler - > client - > ps . legsTimer = grapplerTime ;
grappler - > client - > ps . weaponTime = grapplerTime ;
}
self - > client - > grappleState + + ;
}
}
else
{ //SMACK!
if ( self - > client - > ps . torsoTimer < 1300 )
{
vec3_t tossDir ;
G_Damage ( grappler , self , self , NULL , self - > client - > ps . origin , 30 , 0 , MOD_MELEE ) ;
//G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
self - > client - > grappleState = 0 ;
VectorSubtract ( grappler - > client - > ps . origin , self - > client - > ps . origin , tossDir ) ;
VectorNormalize ( tossDir ) ;
VectorScale ( tossDir , 500.0f , tossDir ) ;
tossDir [ 2 ] = 200.0f ;
VectorAdd ( grappler - > client - > ps . velocity , tossDir , grappler - > client - > ps . velocity ) ;
if ( grappler - > health > 0 )
{ //if still alive knock them down
grappler - > client - > ps . forceHandExtend = HANDEXTEND_KNOCKDOWN ;
grappler - > client - > ps . forceHandExtendTime = level . time + 1300 ;
}
}
}
}
else if ( self - > client - > ps . torsoAnim = = BOTH_KYLE_PA_2 )
{ //knee throw
if ( self - > client - > grappleState = = 1 )
{ //knee to the face
if ( self - > client - > ps . torsoTimer < 3200 )
{
int grapplerAnim = grappler - > client - > ps . torsoAnim ;
int grapplerTime = grappler - > client - > ps . torsoTimer ;
G_Damage ( grappler , self , self , NULL , self - > client - > ps . origin , 20 , 0 , MOD_MELEE ) ;
//G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
//it might try to put them into a pain anim or something, so override it back again
if ( grappler - > health > 0 )
{
grappler - > client - > ps . torsoAnim = grapplerAnim ;
grappler - > client - > ps . torsoTimer = grapplerTime ;
grappler - > client - > ps . legsAnim = grapplerAnim ;
grappler - > client - > ps . legsTimer = grapplerTime ;
grappler - > client - > ps . weaponTime = grapplerTime ;
}
self - > client - > grappleState + + ;
}
}
else if ( self - > client - > grappleState = = 2 )
{ //smashed on the ground
if ( self - > client - > ps . torsoTimer < 2000 )
{
//G_Damage(grappler, self, self, NULL, self->client->ps.origin, 10, 0, MOD_MELEE);
//don't do damage on this one, it would look very freaky if they died
G_EntitySound ( grappler , CHAN_VOICE , G_SoundIndex ( " *pain100.wav " ) ) ;
//G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
self - > client - > grappleState + + ;
}
}
else
{ //and another smash
if ( self - > client - > ps . torsoTimer < 1000 )
{
G_Damage ( grappler , self , self , NULL , self - > client - > ps . origin , 30 , 0 , MOD_MELEE ) ;
//G_Sound( grappler, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
//it might try to put them into a pain anim or something, so override it back again
if ( grappler - > health > 0 )
{
grappler - > client - > ps . torsoTimer = 1000 ;
//G_SetAnim(grappler, &grappler->client->pers.cmd, SETANIM_BOTH, BOTH_GETUP3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
grappler - > client - > grappleState = 0 ;
}
else
{ //override death anim
grappler - > client - > ps . torsoAnim = BOTH_DEADFLOP1 ;
grappler - > client - > ps . legsAnim = BOTH_DEADFLOP1 ;
}
self - > client - > grappleState = 0 ;
}
}
}
else
{ //?
}
}
}
}
}
//If this is a listen server (client+server running on same machine),
//then lets try to steal the skeleton/etc data off the client instance
//for this entity to save us processing time.
clientOverride = trap_G2API_OverrideServer ( self - > ghoul2 ) ;
saberNum = self - > client - > ps . saberEntityNum ;
if ( ! saberNum )
{
saberNum = self - > client - > saberStoredIndex ;
}
if ( ! saberNum )
{
returnAfterUpdate = 1 ;
goto nextStep ;
}
mySaber = & g_entities [ saberNum ] ;
if ( self - > health < 1 )
{ //we don't want to waste precious CPU time calculating saber positions for corpses. But we want to avoid the saber ent position lagging on spawn, so..
//I guess it's good to keep the position updated even when contents are 0
if ( mySaber & & ( ( mySaber - > r . contents & CONTENTS_LIGHTSABER ) | | mySaber - > r . contents = = 0 ) & & ! self - > client - > ps . saberInFlight )
{ //Since we haven't got a bolt position, place it on top of the player origin.
VectorCopy ( self - > client - > ps . origin , mySaber - > r . currentOrigin ) ;
}
//I don't want to return now actually, I want to keep g2 instances for corpses up to
//date because I'm doing better corpse hit detection/dismem (particularly for the
//npc's)
//return;
}
if ( BG_SuperBreakWinAnim ( self - > client - > ps . torsoAnim ) )
{
self - > client - > ps . weaponstate = WEAPON_FIRING ;
}
if ( self - > client - > ps . weapon ! = WP_SABER | |
self - > client - > ps . weaponstate = = WEAPON_RAISING | |
self - > client - > ps . weaponstate = = WEAPON_DROPPING | |
self - > health < 1 )
{
if ( ! self - > client - > ps . saberInFlight )
{
returnAfterUpdate = 1 ;
}
}
if ( self - > client - > ps . saberThrowDelay < level . time )
{
if ( ( self - > client - > saber [ 0 ] . saberFlags & SFL_NOT_THROWABLE ) )
{ //cant throw it normally!
if ( ( self - > client - > saber [ 0 ] . saberFlags & SFL_SINGLE_BLADE_THROWABLE ) )
{ //but can throw it if only have 1 blade on
if ( self - > client - > saber [ 0 ] . numBlades > 1
& & self - > client - > ps . saberHolstered = = 1 )
{ //have multiple blades and only one blade on
self - > client - > ps . saberCanThrow = qtrue ; //qfalse;
//huh? want to be able to throw then right?
}
else
{ //multiple blades on, can't throw
self - > client - > ps . saberCanThrow = qfalse ;
}
}
else
{ //never can throw it
self - > client - > ps . saberCanThrow = qfalse ;
}
}
else
{ //can throw it!
self - > client - > ps . saberCanThrow = qtrue ;
}
}
nextStep :
if ( self - > client - > ps . fd . forcePowersActive & ( 1 < < FP_RAGE ) )
{
animSpeedScale = 2 ;
}
VectorCopy ( self - > client - > ps . origin , properOrigin ) ;
//try to predict the origin based on velocity so it's more like what the client is seeing
VectorCopy ( self - > client - > ps . velocity , addVel ) ;
VectorNormalize ( addVel ) ;
if ( self - > client - > ps . velocity [ 0 ] < 0 )
{
fVSpeed + = ( - self - > client - > ps . velocity [ 0 ] ) ;
}
else
{
fVSpeed + = self - > client - > ps . velocity [ 0 ] ;
}
if ( self - > client - > ps . velocity [ 1 ] < 0 )
{
fVSpeed + = ( - self - > client - > ps . velocity [ 1 ] ) ;
}
else
{
fVSpeed + = self - > client - > ps . velocity [ 1 ] ;
}
if ( self - > client - > ps . velocity [ 2 ] < 0 )
{
fVSpeed + = ( - self - > client - > ps . velocity [ 2 ] ) ;
}
else
{
fVSpeed + = self - > client - > ps . velocity [ 2 ] ;
}
//fVSpeed *= 0.08;
fVSpeed * = 1.6f / g_svfps . value ;
//Cap it off at reasonable values so the saber box doesn't go flying ahead of us or
//something if we get a big speed boost from something.
if ( fVSpeed > 70 )
{
fVSpeed = 70 ;
}
if ( fVSpeed < - 70 )
{
fVSpeed = - 70 ;
}
properOrigin [ 0 ] + = addVel [ 0 ] * fVSpeed ;
properOrigin [ 1 ] + = addVel [ 1 ] * fVSpeed ;
properOrigin [ 2 ] + = addVel [ 2 ] * fVSpeed ;
properAngles [ 0 ] = 0 ;
if ( self - > s . number < MAX_CLIENTS & & self - > client - > ps . m_iVehicleNum )
{
vehEnt = & g_entities [ self - > client - > ps . m_iVehicleNum ] ;
if ( vehEnt - > inuse & & vehEnt - > client & & vehEnt - > m_pVehicle )
{
properAngles [ 1 ] = vehEnt - > m_pVehicle - > m_vOrientation [ YAW ] ;
}
else
{
properAngles [ 1 ] = self - > client - > ps . viewangles [ YAW ] ;
vehEnt = NULL ;
}
}
else
{
properAngles [ 1 ] = self - > client - > ps . viewangles [ YAW ] ;
}
properAngles [ 2 ] = 0 ;
AnglesToAxis ( properAngles , legAxis ) ;
UpdateClientRenderinfo ( self , properOrigin , properAngles ) ;
if ( ! clientOverride )
{ //if we get the client instance we don't need to do this
G_G2PlayerAngles ( self , legAxis , properAngles ) ;
}
if ( vehEnt )
{
properAngles [ 1 ] = vehEnt - > m_pVehicle - > m_vOrientation [ YAW ] ;
}
if ( returnAfterUpdate & & saberNum )
{ //We don't even need to do GetBoltMatrix if we're only in here to keep the g2 server instance in sync
//but keep our saber entity in sync too, just copy it over our origin.
//I guess it's good to keep the position updated even when contents are 0
if ( mySaber & & ( ( mySaber - > r . contents & CONTENTS_LIGHTSABER ) | | mySaber - > r . contents = = 0 ) & & ! self - > client - > ps . saberInFlight )
{ //Since we haven't got a bolt position, place it on top of the player origin.
VectorCopy ( self - > client - > ps . origin , mySaber - > r . currentOrigin ) ;
}
goto finalUpdate ;
}
if ( returnAfterUpdate )
{
goto finalUpdate ;
}
//We'll get data for blade 0 first no matter what it is and stick them into
//the constant ("_Always") values. Later we will handle going through each blade.
trap_G2API_GetBoltMatrix ( self - > ghoul2 , 1 , 0 , & boltMatrix , properAngles , properOrigin , level . time , NULL , self - > modelScale ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , ORIGIN , boltOrigin ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , NEGATIVE_Y , boltAngles ) ;
//immediately store these values so we don't have to recalculate this again
if ( self - > client - > lastSaberStorageTime & & ( level . time - self - > client - > lastSaberStorageTime ) < 200 )
{ //alright
VectorCopy ( self - > client - > lastSaberBase_Always , self - > client - > olderSaberBase ) ;
self - > client - > olderIsValid = qtrue ;
}
else
{
self - > client - > olderIsValid = qfalse ;
}
VectorCopy ( boltOrigin , self - > client - > lastSaberBase_Always ) ;
VectorCopy ( boltAngles , self - > client - > lastSaberDir_Always ) ;
self - > client - > lastSaberStorageTime = level . time ;
VectorCopy ( boltAngles , rawAngles ) ;
VectorMA ( boltOrigin , self - > client - > saber [ 0 ] . blade [ 0 ] . lengthMax , boltAngles , end ) ;
if ( self - > client - > ps . saberEntityNum )
{
//I guess it's good to keep the position updated even when contents are 0
if ( mySaber & & ( ( mySaber - > r . contents & CONTENTS_LIGHTSABER ) | | mySaber - > r . contents = = 0 ) & & ! self - > client - > ps . saberInFlight )
{ //place it roughly in the middle of the saber..
VectorMA ( boltOrigin , self - > client - > saber [ 0 ] . blade [ 0 ] . lengthMax , boltAngles , mySaber - > r . currentOrigin ) ;
}
}
boltAngles [ YAW ] = self - > client - > ps . viewangles [ YAW ] ;
/* {
static int lastDTime = 0 ;
if ( lastDTime < level . time )
{
G_TestLine ( boltOrigin , end , 0x0000ff , 200 ) ;
lastDTime = level . time + 200 ;
}
}
*/
if ( self - > client - > ps . saberInFlight )
{ //do the thrown-saber stuff
gentity_t * saberent = & g_entities [ saberNum ] ;
if ( saberent )
{
if ( ! self - > client - > ps . saberEntityState & & self - > client - > ps . saberEntityNum )
{
vec3_t startorg , startang , dir ;
VectorCopy ( boltOrigin , saberent - > r . currentOrigin ) ;
VectorCopy ( boltOrigin , startorg ) ;
VectorCopy ( boltAngles , startang ) ;
//startang[0] = 90;
//Instead of this we'll sort of fake it and slowly tilt it down on the client via
//a perframe method (which doesn't actually affect where or how the saber hits)
saberent - > r . svFlags & = ~ ( SVF_NOCLIENT ) ;
VectorCopy ( startorg , saberent - > s . pos . trBase ) ;
VectorCopy ( startang , saberent - > s . apos . trBase ) ;
VectorCopy ( startorg , saberent - > s . origin ) ;
VectorCopy ( startang , saberent - > s . angles ) ;
saberent - > s . saberInFlight = qtrue ;
saberent - > s . apos . trType = TR_LINEAR ;
saberent - > s . apos . trDelta [ 0 ] = 0 ;
saberent - > s . apos . trDelta [ 1 ] = 800 ;
saberent - > s . apos . trDelta [ 2 ] = 0 ;
saberent - > s . pos . trType = TR_LINEAR ;
saberent - > s . eType = ET_GENERAL ;
saberent - > s . eFlags = 0 ;
WP_SaberAddG2Model ( saberent , self - > client - > saber [ 0 ] . model , self - > client - > saber [ 0 ] . skin ) ;
saberent - > s . modelGhoul2 = 127 ;
saberent - > parent = self ;
self - > client - > ps . saberEntityState = 1 ;
//Projectile stuff:
AngleVectors ( self - > client - > ps . viewangles , dir , NULL , NULL ) ;
saberent - > nextthink = level . time + FRAMETIME ;
saberent - > think = saberFirstThrown ;
saberent - > damage = SABER_THROWN_HIT_DAMAGE ;
saberent - > methodOfDeath = MOD_SABER ;
saberent - > splashMethodOfDeath = MOD_SABER ;
saberent - > s . solid = 2 ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
saberent - > genericValue5 = 0 ;
VectorSet ( saberent - > r . mins , SABERMINS_X , SABERMINS_Y , SABERMINS_Z ) ;
VectorSet ( saberent - > r . maxs , SABERMAXS_X , SABERMAXS_Y , SABERMAXS_Z ) ;
saberent - > s . genericenemyindex = self - > s . number + 1024 ;
saberent - > touch = thrownSaberTouch ;
saberent - > s . weapon = WP_SABER ;
VectorScale ( dir , 400 , saberent - > s . pos . trDelta ) ;
saberent - > s . pos . trTime = level . time ;
if ( self - > client - > saber [ 0 ] . spinSound )
{
saberent - > s . loopSound = self - > client - > saber [ 0 ] . spinSound ;
}
else
{
saberent - > s . loopSound = saberSpinSound ;
}
saberent - > s . loopIsSoundset = qfalse ;
self - > client - > ps . saberDidThrowTime = level . time ;
self - > client - > dangerTime = level . time ;
self - > client - > ps . eFlags & = ~ EF_INVULNERABLE ;
self - > client - > invulnerableTimer = 0 ;
trap_LinkEntity ( saberent ) ;
}
else if ( self - > client - > ps . saberEntityNum ) //only do this stuff if your saber is active and has not been knocked out of the air.
{
VectorCopy ( boltOrigin , saberent - > pos1 ) ;
trap_LinkEntity ( saberent ) ;
if ( saberent - > genericValue5 = = PROPER_THROWN_VALUE )
{ //return to the owner now, this is a bad state to be in for here..
saberent - > genericValue5 = 0 ;
saberent - > think = SaberUpdateSelf ;
saberent - > nextthink = level . time ;
WP_SaberRemoveG2Model ( saberent ) ;
self - > client - > ps . saberInFlight = qfalse ;
self - > client - > ps . saberEntityState = 0 ;
self - > client - > ps . saberThrowDelay = level . time + 500 ;
self - > client - > ps . saberCanThrow = qfalse ;
}
}
}
}
/*
if ( self - > client - > ps . saberInFlight )
{ //if saber is thrown then only do the standard stuff for the left hand saber
rSaberNum = 1 ;
}
*/
if ( ! BG_SabersOff ( & self - > client - > ps ) )
{
gentity_t * saberent = & g_entities [ saberNum ] ;
if ( ! self - > client - > ps . saberInFlight & & saberent )
{
saberent - > r . svFlags | = ( SVF_NOCLIENT ) ;
saberent - > r . contents = CONTENTS_LIGHTSABER ;
SetSaberBoxSize ( saberent ) ;
saberent - > s . loopSound = 0 ;
saberent - > s . loopIsSoundset = qfalse ;
}
if ( self - > client - > ps . saberLockTime > level . time & & self - > client - > ps . saberEntityNum )
{
if ( self - > client - > ps . saberIdleWound < level . time )
{
gentity_t * te ;
vec3_t dir ;
te = G_TempEntity ( g_entities [ saberNum ] . r . currentOrigin , EV_SABER_BLOCK ) ;
VectorSet ( dir , 0 , 1 , 0 ) ;
VectorCopy ( g_entities [ saberNum ] . r . currentOrigin , te - > s . origin ) ;
VectorCopy ( dir , te - > s . angles ) ;
te - > s . eventParm = 1 ;
te - > s . weapon = 0 ; //saberNum
te - > s . legsAnim = 0 ; //bladeNum
self - > client - > ps . saberIdleWound = level . time + Q_irand ( 400 , 600 ) ;
}
while ( rSaberNum < MAX_SABERS )
{
rBladeNum = 0 ;
while ( rBladeNum < self - > client - > saber [ rSaberNum ] . numBlades )
{ //Don't bother updating the bolt for each blade for this, it's just a very rough fallback method for during saberlocks
VectorCopy ( boltOrigin , self - > client - > saber [ saberNum ] . blade [ rBladeNum ] . trail . base ) ;
VectorCopy ( end , self - > client - > saber [ saberNum ] . blade [ rBladeNum ] . trail . tip ) ;
self - > client - > saber [ saberNum ] . blade [ rBladeNum ] . trail . lastTime = level . time ;
rBladeNum + + ;
}
rSaberNum + + ;
}
self - > client - > hasCurrentPosition = qtrue ;
self - > client - > ps . saberBlocked = BLOCKED_NONE ;
goto finalUpdate ;
}
//reset it in case we used it for cycling before
rSaberNum = rBladeNum = 0 ;
if ( self - > client - > ps . saberInFlight )
{ //if saber is thrown then only do the standard stuff for the left hand saber
if ( ! self - > client - > ps . saberEntityNum )
{ //however, if saber is not in flight but rather knocked away, our left saber is off, and thus we may do nothing.
rSaberNum = 1 ; //was 2?
}
else
{ //thrown saber still in flight, so do damage
rSaberNum = 0 ; //was 1?
}
}
WP_SaberClearDamage ( ) ;
saberDoClashEffect = qfalse ;
//Now cycle through each saber and each blade on the saber and do damage traces.
while ( rSaberNum < MAX_SABERS )
{
if ( ! self - > client - > saber [ rSaberNum ] . model [ 0 ] )
{
rSaberNum + + ;
continue ;
}
/*
if ( rSaberNum = = 0 & & ( self - > client - > ps . brokenLimbs & ( 1 < < BROKENLIMB_RARM ) ) )
{ //don't do saber 0 is the right arm is broken
rSaberNum + + ;
continue ;
}
*/
//for now I'm keeping a broken right arm swingable, it will just look and act damaged
//but still be useable
if ( rSaberNum = = 1 & & ( self - > client - > ps . brokenLimbs & ( 1 < < BROKENLIMB_LARM ) ) )
{ //don't to saber 1 if the left arm is broken
break ;
}
if ( rSaberNum > 0
& & self - > client - > saber [ 1 ] . model
& & self - > client - > saber [ 1 ] . model [ 0 ]
& & self - > client - > ps . saberHolstered = = 1 )
{ //don't to saber 2 if it's off
break ;
}
rBladeNum = 0 ;
while ( rBladeNum < self - > client - > saber [ rSaberNum ] . numBlades )
{
//update muzzle data for the blade
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzlePoint , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzlePointOld ) ;
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzleDir , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzleDirOld ) ;
if ( rBladeNum > 0 //more than one blade
& & ( ! self - > client - > saber [ 1 ] . model | | ! self - > client - > saber [ 1 ] . model [ 0 ] ) //not using dual blades
& & self - > client - > saber [ rSaberNum ] . numBlades > 1 //using a multi-bladed saber
& & self - > client - > ps . saberHolstered = = 1 ) //
{ //don't to extra blades if they're off
break ;
}
//get the new data
//then update the bolt pos/dir. rBladeNum corresponds to the bolt index because blade bolts are added in order.
if ( rSaberNum = = 0 & & self - > client - > ps . saberInFlight )
{
if ( ! self - > client - > ps . saberEntityNum )
{ //dropped it... shouldn't get here, but...
//assert(0);
//FIXME: It's getting here a lot actually....
rSaberNum + + ;
rBladeNum = 0 ;
continue ;
}
else
{
gentity_t * saberEnt = & g_entities [ self - > client - > ps . saberEntityNum ] ;
vec3_t saberOrg , saberAngles ;
if ( ! saberEnt
| | ! saberEnt - > inuse
| | ! saberEnt - > ghoul2 )
{ //wtf?
rSaberNum + + ;
rBladeNum = 0 ;
continue ;
}
if ( saberent - > s . saberInFlight )
{ //spinning
BG_EvaluateTrajectory ( & saberEnt - > s . pos , level . time + 50 , saberOrg ) ;
BG_EvaluateTrajectory ( & saberEnt - > s . apos , level . time + 50 , saberAngles ) ;
}
else
{ //coming right back
vec3_t saberDir ;
BG_EvaluateTrajectory ( & saberEnt - > s . pos , level . time , saberOrg ) ;
VectorSubtract ( self - > r . currentOrigin , saberOrg , saberDir ) ;
vectoangles ( saberDir , saberAngles ) ;
}
trap_G2API_GetBoltMatrix ( saberEnt - > ghoul2 , 0 , rBladeNum , & boltMatrix , saberAngles , saberOrg , level . time , NULL , self - > modelScale ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , ORIGIN , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzlePoint ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , NEGATIVE_Y , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzleDir ) ;
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzlePoint , boltOrigin ) ;
VectorMA ( boltOrigin , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . lengthMax , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzleDir , end ) ;
}
}
else
{
trap_G2API_GetBoltMatrix ( self - > ghoul2 , rSaberNum + 1 , rBladeNum , & boltMatrix , properAngles , properOrigin , level . time , NULL , self - > modelScale ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , ORIGIN , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzlePoint ) ;
BG_GiveMeVectorFromMatrix ( & boltMatrix , NEGATIVE_Y , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzleDir ) ;
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzlePoint , boltOrigin ) ;
VectorMA ( boltOrigin , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . lengthMax , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . muzzleDir , end ) ;
}
self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . storageTime = level . time ;
if ( self - > client - > hasCurrentPosition & & d_saberInterpolate . integer )
{
if ( self - > client - > ps . weaponTime < = 0 )
{ //rww - 07/17/02 - don't bother doing the extra stuff unless actually attacking. This is in attempt to save CPU.
CheckSaberDamage ( self , rSaberNum , rBladeNum , boltOrigin , end , qfalse , ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) , qfalse ) ;
}
else if ( d_saberInterpolate . integer = = 1 )
{
int trMask = CONTENTS_LIGHTSABER | CONTENTS_BODY ;
int sN = 0 ;
qboolean gotHit = qfalse ;
qboolean clientUnlinked [ MAX_CLIENTS ] ;
qboolean skipSaberTrace = qfalse ;
if ( ! g_saberTraceSaberFirst . integer )
{
skipSaberTrace = qtrue ;
}
else if ( g_saberTraceSaberFirst . integer > = 2 & &
g_gametype . integer ! = GT_DUEL & &
g_gametype . integer ! = GT_POWERDUEL & &
! self - > client - > ps . duelInProgress )
{ //if value is >= 2, and not in a duel, skip
skipSaberTrace = qtrue ;
}
if ( skipSaberTrace )
{ //skip the saber-contents-only trace and get right to the full trace
trMask = ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) ;
}
else
{
while ( sN < MAX_CLIENTS )
{
if ( g_entities [ sN ] . inuse & & g_entities [ sN ] . client & & g_entities [ sN ] . r . linked & & g_entities [ sN ] . health > 0 & & ( g_entities [ sN ] . r . contents & CONTENTS_BODY ) )
{ //Take this mask off before the saber trace, because we want to hit the saber first
g_entities [ sN ] . r . contents & = ~ CONTENTS_BODY ;
clientUnlinked [ sN ] = qtrue ;
}
else
{
clientUnlinked [ sN ] = qfalse ;
}
sN + + ;
}
}
while ( ! gotHit )
{
if ( ! CheckSaberDamage ( self , rSaberNum , rBladeNum , boltOrigin , end , qfalse , trMask , qfalse ) )
{
if ( ! CheckSaberDamage ( self , rSaberNum , rBladeNum , boltOrigin , end , qtrue , trMask , qfalse ) )
{
vec3_t oldSaberStart ;
vec3_t oldSaberEnd ;
vec3_t saberAngleNow ;
vec3_t saberAngleBefore ;
vec3_t saberMidDir ;
vec3_t saberMidAngle ;
vec3_t saberMidPoint ;
vec3_t saberMidEnd ;
vec3_t saberSubBase ;
float deltaX , deltaY , deltaZ ;
if ( ( level . time - self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . lastTime ) > 100 )
{ //no valid last pos, use current
VectorCopy ( boltOrigin , oldSaberStart ) ;
VectorCopy ( end , oldSaberEnd ) ;
}
else
{ //trace from last pos
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . base , oldSaberStart ) ;
VectorCopy ( self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . tip , oldSaberEnd ) ;
}
VectorSubtract ( oldSaberEnd , oldSaberStart , saberAngleBefore ) ;
vectoangles ( saberAngleBefore , saberAngleBefore ) ;
VectorSubtract ( end , boltOrigin , saberAngleNow ) ;
vectoangles ( saberAngleNow , saberAngleNow ) ;
deltaX = AngleDelta ( saberAngleBefore [ 0 ] , saberAngleNow [ 0 ] ) ;
deltaY = AngleDelta ( saberAngleBefore [ 1 ] , saberAngleNow [ 1 ] ) ;
deltaZ = AngleDelta ( saberAngleBefore [ 2 ] , saberAngleNow [ 2 ] ) ;
if ( ( deltaX ! = 0 | | deltaY ! = 0 | | deltaZ ! = 0 ) & & deltaX < 180 & & deltaY < 180 & & deltaZ < 180 & & ( BG_SaberInAttack ( self - > client - > ps . saberMove ) | | PM_SaberInTransition ( self - > client - > ps . saberMove ) ) )
{ //don't go beyond here if we aren't attacking/transitioning or the angle is too large.
//and don't bother if the angle is the same
saberMidAngle [ 0 ] = saberAngleBefore [ 0 ] + ( deltaX / 2 ) ;
saberMidAngle [ 1 ] = saberAngleBefore [ 1 ] + ( deltaY / 2 ) ;
saberMidAngle [ 2 ] = saberAngleBefore [ 2 ] + ( deltaZ / 2 ) ;
//Now that I have the angle, I'll just say the base for it is the difference between the two start
//points (even though that's quite possibly completely false)
VectorSubtract ( boltOrigin , oldSaberStart , saberSubBase ) ;
saberMidPoint [ 0 ] = boltOrigin [ 0 ] + ( saberSubBase [ 0 ] * 0.5 ) ;
saberMidPoint [ 1 ] = boltOrigin [ 1 ] + ( saberSubBase [ 1 ] * 0.5 ) ;
saberMidPoint [ 2 ] = boltOrigin [ 2 ] + ( saberSubBase [ 2 ] * 0.5 ) ;
AngleVectors ( saberMidAngle , saberMidDir , 0 , 0 ) ;
saberMidEnd [ 0 ] = saberMidPoint [ 0 ] + saberMidDir [ 0 ] * self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . lengthMax ;
saberMidEnd [ 1 ] = saberMidPoint [ 1 ] + saberMidDir [ 1 ] * self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . lengthMax ;
saberMidEnd [ 2 ] = saberMidPoint [ 2 ] + saberMidDir [ 2 ] * self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . lengthMax ;
//I'll just trace straight out and not even trace between positions to save speed.
if ( CheckSaberDamage ( self , rSaberNum , rBladeNum , saberMidPoint , saberMidEnd , qfalse , trMask , qfalse ) )
{
gotHit = qtrue ;
}
}
}
else
{
gotHit = qtrue ;
}
}
else
{
gotHit = qtrue ;
}
if ( g_saberTraceSaberFirst . integer )
{
sN = 0 ;
while ( sN < MAX_CLIENTS )
{
if ( clientUnlinked [ sN ] )
{ //Make clients clip properly again.
if ( g_entities [ sN ] . inuse & & g_entities [ sN ] . health > 0 )
{
g_entities [ sN ] . r . contents | = CONTENTS_BODY ;
}
}
sN + + ;
}
}
if ( ! gotHit )
{
if ( trMask ! = ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) )
{
trMask = ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) ;
}
else
{
gotHit = qtrue ; //break out of the loop
}
}
}
}
else if ( d_saberInterpolate . integer ) //anything but 0 or 1, use the old plain method.
{
if ( ! CheckSaberDamage ( self , rSaberNum , rBladeNum , boltOrigin , end , qfalse , ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) , qfalse ) )
{
CheckSaberDamage ( self , rSaberNum , rBladeNum , boltOrigin , end , qtrue , ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) , qfalse ) ;
}
}
}
else if ( d_saberSPStyleDamage . integer )
{
G_SPSaberDamageTraceLerped ( self , rSaberNum , rBladeNum , boltOrigin , end , ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) ) ;
}
else
{
CheckSaberDamage ( self , rSaberNum , rBladeNum , boltOrigin , end , qfalse , ( MASK_PLAYERSOLID | CONTENTS_LIGHTSABER | MASK_SHOT ) , qfalse ) ;
}
VectorCopy ( boltOrigin , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . base ) ;
VectorCopy ( end , self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . tip ) ;
self - > client - > saber [ rSaberNum ] . blade [ rBladeNum ] . trail . lastTime = level . time ;
//VectorCopy(boltOrigin, self->client->lastSaberBase);
//VectorCopy(end, self->client->lastSaberTip);
self - > client - > hasCurrentPosition = qtrue ;
//do hit effects
WP_SaberDoHit ( self , rSaberNum , rBladeNum ) ;
WP_SaberDoClash ( self , rSaberNum , rBladeNum ) ;
rBladeNum + + ;
}
rSaberNum + + ;
}
WP_SaberApplyDamage ( self ) ;
//NOTE: doing one call like this after the 2 loops above is a bit cheaper, tempentity-wise... but won't use the correct saber and blade numbers...
//now actually go through and apply all the damage we did
//WP_SaberDoHit( self, 0, 0 );
//WP_SaberDoClash( self, 0, 0 );
if ( mySaber & & mySaber - > inuse )
{
trap_LinkEntity ( mySaber ) ;
}
if ( ! self - > client - > ps . saberInFlight )
{
self - > client - > ps . saberEntityState = 0 ;
}
}
finalUpdate :
if ( clientOverride )
{ //if we get the client instance we don't even need to bother setting anims and stuff
return ;
}
G_UpdateClientAnims ( self , animSpeedScale ) ;
}
int WP_MissileBlockForBlock ( int saberBlock )
{
switch ( saberBlock )
{
case BLOCKED_UPPER_RIGHT :
return BLOCKED_UPPER_RIGHT_PROJ ;
break ;
case BLOCKED_UPPER_LEFT :
return BLOCKED_UPPER_LEFT_PROJ ;
break ;
case BLOCKED_LOWER_RIGHT :
return BLOCKED_LOWER_RIGHT_PROJ ;
break ;
case BLOCKED_LOWER_LEFT :
return BLOCKED_LOWER_LEFT_PROJ ;
break ;
case BLOCKED_TOP :
return BLOCKED_TOP_PROJ ;
break ;
}
return saberBlock ;
}
void WP_SaberBlockNonRandom ( gentity_t * self , vec3_t hitloc , qboolean missileBlock )
{
vec3_t diff , fwdangles = { 0 , 0 , 0 } , right ;
vec3_t clEye ;
float rightdot ;
float zdiff ;
VectorCopy ( self - > client - > ps . origin , clEye ) ;
clEye [ 2 ] + = self - > client - > ps . viewheight ;
VectorSubtract ( hitloc , clEye , diff ) ;
diff [ 2 ] = 0 ;
VectorNormalize ( diff ) ;
fwdangles [ 1 ] = self - > client - > ps . viewangles [ 1 ] ;
// Ultimately we might care if the shot was ahead or behind, but for now, just quadrant is fine.
AngleVectors ( fwdangles , NULL , right , NULL ) ;
rightdot = DotProduct ( right , diff ) ;
zdiff = hitloc [ 2 ] - clEye [ 2 ] ;
if ( zdiff > 0 )
{
if ( rightdot > 0.3 )
{
self - > client - > ps . saberBlocked = BLOCKED_UPPER_RIGHT ;
}
else if ( rightdot < - 0.3 )
{
self - > client - > ps . saberBlocked = BLOCKED_UPPER_LEFT ;
}
else
{
self - > client - > ps . saberBlocked = BLOCKED_TOP ;
}
}
else if ( zdiff > - 20 ) //20 )
{
if ( zdiff < - 10 ) //30 )
{ //hmm, pretty low, but not low enough to use the low block, so we need to duck
}
if ( rightdot > 0.1 )
{
self - > client - > ps . saberBlocked = BLOCKED_UPPER_RIGHT ;
}
else if ( rightdot < - 0.1 )
{
self - > client - > ps . saberBlocked = BLOCKED_UPPER_LEFT ;
}
else
{
self - > client - > ps . saberBlocked = BLOCKED_TOP ;
}
}
else
{
if ( rightdot > = 0 )
{
self - > client - > ps . saberBlocked = BLOCKED_LOWER_RIGHT ;
}
else
{
self - > client - > ps . saberBlocked = BLOCKED_LOWER_LEFT ;
}
}
if ( missileBlock )
{
self - > client - > ps . saberBlocked = WP_MissileBlockForBlock ( self - > client - > ps . saberBlocked ) ;
}
}
void WP_SaberBlock ( gentity_t * playerent , vec3_t hitloc , qboolean missileBlock )
{
vec3_t diff , fwdangles = { 0 , 0 , 0 } , right ;
float rightdot ;
float zdiff ;
VectorSubtract ( hitloc , playerent - > client - > ps . origin , diff ) ;
VectorNormalize ( diff ) ;
fwdangles [ 1 ] = playerent - > client - > ps . viewangles [ 1 ] ;
// Ultimately we might care if the shot was ahead or behind, but for now, just quadrant is fine.
AngleVectors ( fwdangles , NULL , right , NULL ) ;
rightdot = DotProduct ( right , diff ) + RandFloat ( - 0.2f , 0.2f ) ;
zdiff = hitloc [ 2 ] - playerent - > client - > ps . origin [ 2 ] + Q_irand ( - 8 , 8 ) ;
// Figure out what quadrant the block was in.
if ( zdiff > 24 )
{ // Attack from above
if ( Q_irand ( 0 , 1 ) )
{
playerent - > client - > ps . saberBlocked = BLOCKED_TOP ;
}
else
{
playerent - > client - > ps . saberBlocked = BLOCKED_UPPER_LEFT ;
}
}
else if ( zdiff > 13 )
{ // The upper half has three viable blocks...
if ( rightdot > 0.25 )
{ // In the right quadrant...
if ( Q_irand ( 0 , 1 ) )
{
playerent - > client - > ps . saberBlocked = BLOCKED_UPPER_LEFT ;
}
else
{
playerent - > client - > ps . saberBlocked = BLOCKED_LOWER_LEFT ;
}
}
else
{
switch ( Q_irand ( 0 , 3 ) )
{
case 0 :
playerent - > client - > ps . saberBlocked = BLOCKED_UPPER_RIGHT ;
break ;
case 1 :
case 2 :
playerent - > client - > ps . saberBlocked = BLOCKED_LOWER_RIGHT ;
break ;
case 3 :
playerent - > client - > ps . saberBlocked = BLOCKED_TOP ;
break ;
}
}
}
else
{ // The lower half is a bit iffy as far as block coverage. Pick one of the "low" ones at random.
if ( Q_irand ( 0 , 1 ) )
{
playerent - > client - > ps . saberBlocked = BLOCKED_LOWER_RIGHT ;
}
else
{
playerent - > client - > ps . saberBlocked = BLOCKED_LOWER_LEFT ;
}
}
if ( missileBlock )
{
playerent - > client - > ps . saberBlocked = WP_MissileBlockForBlock ( playerent - > client - > ps . saberBlocked ) ;
}
}
int WP_SaberCanBlock ( gentity_t * self , vec3_t point , int dflags , int mod , qboolean projectile , int attackStr )
{
qboolean thrownSaber = qfalse ;
float blockFactor = 0 ;
if ( ! self | | ! self - > client | | ! point )
{
return 0 ;
}
if ( attackStr = = 999 )
{
attackStr = 0 ;
thrownSaber = qtrue ;
}
if ( BG_SaberInAttack ( self - > client - > ps . saberMove ) )
{
return 0 ;
}
if ( PM_InSaberAnim ( self - > client - > ps . torsoAnim ) & & ! self - > client - > ps . saberBlocked & &
self - > client - > ps . saberMove ! = LS_READY & & self - > client - > ps . saberMove ! = LS_NONE )
{
if ( self - > client - > ps . saberMove < LS_PARRY_UP | | self - > client - > ps . saberMove > LS_REFLECT_LL )
{
return 0 ;
}
}
if ( PM_SaberInBrokenParry ( self - > client - > ps . saberMove ) )
{
return 0 ;
}
if ( ! self - > client - > ps . saberEntityNum )
{ //saber is knocked away
return 0 ;
}
if ( BG_SabersOff ( & self - > client - > ps ) )
{
return 0 ;
}
if ( self - > client - > ps . weapon ! = WP_SABER )
{
return 0 ;
}
if ( self - > client - > ps . weaponstate = = WEAPON_RAISING )
{
return 0 ;
}
if ( self - > client - > ps . saberInFlight )
{
return 0 ;
}
if ( ( self - > client - > pers . cmd . buttons & BUTTON_ATTACK ) /* &&
( projectile | | attackStr = = FORCE_LEVEL_3 ) */ )
{ //don't block when the player is trying to slash, if it's a projectile or he's doing a very strong attack
return 0 ;
}
//Removed this for now, the new broken parry stuff should handle it. This is how
//blocks were decided before the 1.03 patch (as you can see, it was STUPID.. for the most part)
/*
if ( attackStr = = FORCE_LEVEL_3 )
{
if ( self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] > = FORCE_LEVEL_3 )
{
if ( Q_irand ( 1 , 10 ) < 3 )
{
return 0 ;
}
}
else
{
return 0 ;
}
}
if ( attackStr = = FORCE_LEVEL_2 & & Q_irand ( 1 , 10 ) < 3 )
{
if ( self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] > = FORCE_LEVEL_3 )
{
//do nothing for now
}
else if ( self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] > = FORCE_LEVEL_2 )
{
if ( Q_irand ( 1 , 10 ) < 5 )
{
return 0 ;
}
}
else
{
return 0 ;
}
}
if ( attackStr = = FORCE_LEVEL_1 & & ! self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] & &
Q_irand ( 1 , 40 ) < 3 )
{ //if I have no defense level at all then I might be unable to block a level 1 attack (but very rarely)
return 0 ;
}
*/
if ( SaberAttacking ( self ) )
{ //attacking, can't block now
return 0 ;
}
if ( self - > client - > ps . saberMove ! = LS_READY & &
! self - > client - > ps . saberBlocking )
{
return 0 ;
}
if ( self - > client - > ps . saberBlockTime > = level . time )
{
return 0 ;
}
if ( self - > client - > ps . forceHandExtend ! = HANDEXTEND_NONE )
{
return 0 ;
}
if ( self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] = = FORCE_LEVEL_3 )
{
if ( d_saberGhoul2Collision . integer )
{
blockFactor = 0.3f ;
}
else
{
blockFactor = 0.05f ;
}
}
else if ( self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] = = FORCE_LEVEL_2 )
{
blockFactor = 0.6f ;
}
else if ( self - > client - > ps . fd . forcePowerLevel [ FP_SABER_DEFENSE ] = = FORCE_LEVEL_1 )
{
blockFactor = 0.9f ;
}
else
{ //for now we just don't get to autoblock with no def
return 0 ;
}
if ( thrownSaber )
{
blockFactor - = 0.25f ;
}
if ( attackStr )
{ //blocking a saber, not a projectile.
blockFactor - = 0.25f ;
}
if ( ! InFront ( point , self - > client - > ps . origin , self - > client - > ps . viewangles , blockFactor ) ) //orig 0.2f
{
return 0 ;
}
if ( projectile )
{
WP_SaberBlockNonRandom ( self , point , projectile ) ;
}
return 1 ;
}
qboolean HasSetSaberOnly ( void )
{
int i = 0 ;
int wDisable = 0 ;
if ( g_gametype . integer = = GT_JEDIMASTER )
{ //set to 0
return qfalse ;
}
if ( g_gametype . integer = = GT_DUEL | | g_gametype . integer = = GT_POWERDUEL )
{
wDisable = g_duelWeaponDisable . integer ;
}
else
{
wDisable = g_weaponDisable . integer ;
}
while ( i < WP_NUM_WEAPONS )
{
if ( ! ( wDisable & ( 1 < < i ) ) & &
i ! = WP_SABER & & i ! = WP_NONE )
{
return qfalse ;
}
i + + ;
}
return qtrue ;
}