00001 #include "g_local.h"
00002 #include "bg_local.h"
00003 #include "w_saber.h"
00004 #include "ai_main.h"
00005 #include "../ghoul2/G2.h"
00006
00007 #define SABER_BOX_SIZE 16.0f
00008 extern bot_state_t *botstates[MAX_CLIENTS];
00009 extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold );
00010 extern void G_TestLine(vec3_t start, vec3_t end, int color, int time);
00011
00012 extern vmCvar_t g_saberRealisticCombat;
00013 extern vmCvar_t d_saberSPStyleDamage;
00014 extern vmCvar_t g_debugSaberLocks;
00015
00016 extern vmCvar_t g_saberWallDamageScale;
00017
00018 int saberSpinSound = 0;
00019
00020
00021 #include "../namespace_begin.h"
00022 qboolean PM_SaberInTransition( int move );
00023 qboolean PM_SaberInDeflect( int move );
00024 qboolean PM_SaberInBrokenParry( int move );
00025 qboolean PM_SaberInBounce( int move );
00026 qboolean BG_SaberInReturn( int move );
00027 qboolean BG_InKnockDownOnGround( playerState_t *ps );
00028 qboolean BG_StabDownAnim( int anim );
00029 qboolean BG_SabersOff( playerState_t *ps );
00030 qboolean BG_SaberInTransitionAny( int move );
00031 qboolean BG_SaberInAttackPure( int move );
00032 qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeNum );
00033 qboolean WP_SaberBladeDoTransitionDamage( saberInfo_t *saber, int bladeNum );
00034 #include "../namespace_end.h"
00035
00036 void WP_SaberAddG2Model( gentity_t *saberent, const char *saberModel, qhandle_t saberSkin );
00037 void WP_SaberRemoveG2Model( gentity_t *saberent );
00038
00039 float RandFloat(float min, float max) {
00040 return ((rand() * (max - min)) / 32768.0F) + min;
00041 }
00042
00043 #ifdef DEBUG_SABER_BOX
00044 void G_DebugBoxLines(vec3_t mins, vec3_t maxs, int duration)
00045 {
00046 vec3_t start;
00047 vec3_t end;
00048
00049 float x = maxs[0] - mins[0];
00050 float y = maxs[1] - mins[1];
00051
00052
00053 VectorCopy(maxs, start);
00054 VectorCopy(maxs, end);
00055 start[0] -= x;
00056 G_TestLine(start, end, 0x00000ff, duration);
00057 end[0] = start[0];
00058 end[1] -= y;
00059 G_TestLine(start, end, 0x00000ff, duration);
00060 start[1] = end[1];
00061 start[0] += x;
00062 G_TestLine(start, end, 0x00000ff, duration);
00063 G_TestLine(start, maxs, 0x00000ff, duration);
00064
00065 VectorCopy(mins, start);
00066 VectorCopy(mins, end);
00067 start[0] += x;
00068 G_TestLine(start, end, 0x00000ff, duration);
00069 end[0] = start[0];
00070 end[1] += y;
00071 G_TestLine(start, end, 0x00000ff, duration);
00072 start[1] = end[1];
00073 start[0] -= x;
00074 G_TestLine(start, end, 0x00000ff, duration);
00075 G_TestLine(start, mins, 0x00000ff, duration);
00076 }
00077 #endif
00078
00079
00080 qboolean G_CanBeEnemy(gentity_t *self, gentity_t *enemy)
00081 {
00082 if (!self->inuse || !enemy->inuse || !self->client || !enemy->client)
00083 {
00084 return qfalse;
00085 }
00086
00087 if (self->client->ps.duelInProgress && self->client->ps.duelIndex != enemy->s.number)
00088 {
00089 return qfalse;
00090 }
00091
00092 if (enemy->client->ps.duelInProgress && enemy->client->ps.duelIndex != self->s.number)
00093 {
00094 return qfalse;
00095 }
00096
00097 if (g_gametype.integer < GT_TEAM)
00098 {
00099 return qtrue;
00100 }
00101
00102 if (g_friendlyFire.integer)
00103 {
00104 return qtrue;
00105 }
00106
00107 if (OnSameTeam(self, enemy))
00108 {
00109 return qfalse;
00110 }
00111
00112 return qtrue;
00113 }
00114
00115
00116
00117
00118 static GAME_INLINE int G_SaberAttackPower(gentity_t *ent, qboolean attacking)
00119 {
00120 int baseLevel;
00121 assert(ent && ent->client);
00122
00123 baseLevel = ent->client->ps.fd.saberAnimLevel;
00124
00125
00126 if (baseLevel == SS_DUAL)
00127 {
00128 baseLevel = 2;
00129 }
00130 else if (baseLevel == SS_STAFF)
00131 {
00132 baseLevel = 2;
00133 }
00134
00135 if (attacking)
00136 {
00137
00138 baseLevel *= 2;
00139
00140 baseLevel++;
00141
00142
00143
00144 if (ent->client->lastSaberStorageTime >= (level.time-50) &&
00145 ent->client->olderIsValid)
00146 {
00147 vec3_t vSub;
00148 int swingDist;
00149 int toleranceAmt;
00150
00151
00152
00153
00154 switch (ent->client->ps.fd.saberAnimLevel)
00155 {
00156 case SS_STRONG:
00157 toleranceAmt = 8;
00158 break;
00159 case SS_MEDIUM:
00160 toleranceAmt = 16;
00161 break;
00162 case SS_FAST:
00163 toleranceAmt = 24;
00164 break;
00165 default:
00166 toleranceAmt = 16;
00167 break;
00168 }
00169
00170 VectorSubtract(ent->client->lastSaberBase_Always, ent->client->olderSaberBase, vSub);
00171 swingDist = (int)VectorLength(vSub);
00172
00173 while (swingDist > 0)
00174 {
00175 baseLevel++;
00176 swingDist -= toleranceAmt;
00177 }
00178 }
00179
00180 #ifndef FINAL_BUILD
00181 if (g_saberDebugPrint.integer > 1)
00182 {
00183 Com_Printf("Client %i: ATT STR: %i\n", ent->s.number, baseLevel);
00184 }
00185 #endif
00186 }
00187
00188 if ((ent->client->ps.brokenLimbs & (1 << BROKENLIMB_RARM)) ||
00189 (ent->client->ps.brokenLimbs & (1 << BROKENLIMB_LARM)))
00190 {
00191 baseLevel *= 0.3;
00192 }
00193
00194
00195 if (baseLevel < 1)
00196 {
00197 baseLevel = 1;
00198 }
00199 else if (baseLevel > 16)
00200 {
00201 baseLevel = 16;
00202 }
00203
00204 if (g_gametype.integer == GT_POWERDUEL &&
00205 ent->client->sess.duelTeam == DUELTEAM_LONE)
00206 {
00207 return baseLevel*2;
00208 }
00209 else if (attacking && g_gametype.integer == GT_SIEGE)
00210 {
00211 return baseLevel*3;
00212 }
00213
00214 return baseLevel;
00215 }
00216
00217 void WP_DeactivateSaber( gentity_t *self, qboolean clearLength )
00218 {
00219 if ( !self || !self->client )
00220 {
00221 return;
00222 }
00223
00224 if ( !self->client->ps.saberHolstered )
00225 {
00226 self->client->ps.saberHolstered = 2;
00227
00228
00229
00230
00231
00232
00233
00234 if (self->client->saber[0].soundOff)
00235 {
00236 G_Sound(self, CHAN_WEAPON, self->client->saber[0].soundOff);
00237 }
00238
00239 if (self->client->saber[1].soundOff &&
00240 self->client->saber[1].model[0])
00241 {
00242 G_Sound(self, CHAN_WEAPON, self->client->saber[1].soundOff);
00243 }
00244
00245 }
00246 }
00247
00248 void WP_ActivateSaber( gentity_t *self )
00249 {
00250 if ( !self || !self->client )
00251 {
00252 return;
00253 }
00254
00255 if (self->NPC &&
00256 self->client->ps.forceHandExtend == HANDEXTEND_JEDITAUNT &&
00257 (self->client->ps.forceHandExtendTime - level.time) > 200)
00258 {
00259 self->client->ps.forceHandExtend = HANDEXTEND_NONE;
00260 self->client->ps.forceHandExtendTime = 0;
00261 }
00262 else if (self->client->ps.fd.forceGripCripple)
00263 {
00264 return;
00265 }
00266
00267 if ( self->client->ps.saberHolstered )
00268 {
00269 self->client->ps.saberHolstered = 0;
00270 if (self->client->saber[0].soundOn)
00271 {
00272 G_Sound(self, CHAN_WEAPON, self->client->saber[0].soundOn);
00273 }
00274
00275 if (self->client->saber[1].soundOn)
00276 {
00277 G_Sound(self, CHAN_WEAPON, self->client->saber[1].soundOn);
00278 }
00279 }
00280 }
00281
00282 #define PROPER_THROWN_VALUE 999 //Ah, well..
00283
00284 void SaberUpdateSelf(gentity_t *ent)
00285 {
00286 if (ent->r.ownerNum == ENTITYNUM_NONE)
00287 {
00288 ent->think = G_FreeEntity;
00289 ent->nextthink = level.time;
00290 return;
00291 }
00292
00293 if (!g_entities[ent->r.ownerNum].inuse ||
00294 !g_entities[ent->r.ownerNum].client
00295 )
00296 {
00297 ent->think = G_FreeEntity;
00298 ent->nextthink = level.time;
00299 return;
00300 }
00301
00302 if (g_entities[ent->r.ownerNum].client->ps.saberInFlight && g_entities[ent->r.ownerNum].health > 0)
00303 {
00304 ent->nextthink = level.time;
00305 ent->genericValue5 = PROPER_THROWN_VALUE;
00306 return;
00307 }
00308
00309 ent->genericValue5 = 0;
00310
00311 if (g_entities[ent->r.ownerNum].client->ps.weapon != WP_SABER ||
00312 (g_entities[ent->r.ownerNum].client->ps.pm_flags & PMF_FOLLOW) ||
00313
00314 g_entities[ent->r.ownerNum].client->sess.sessionTeam == TEAM_SPECTATOR ||
00315 g_entities[ent->r.ownerNum].client->tempSpectate >= level.time ||
00316
00317 g_entities[ent->r.ownerNum].health < 1 ||
00318 BG_SabersOff( &g_entities[ent->r.ownerNum].client->ps ) ||
00319 (!g_entities[ent->r.ownerNum].client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] && g_entities[ent->r.ownerNum].s.eType != ET_NPC))
00320 {
00321 ent->r.contents = 0;
00322 ent->clipmask = 0;
00323 }
00324 else
00325 {
00326 #ifdef DEBUG_SABER_BOX
00327 if (g_saberDebugBox.integer == 1|| g_saberDebugBox.integer == 4)
00328 {
00329 vec3_t dbgMins;
00330 vec3_t dbgMaxs;
00331
00332 VectorAdd( ent->r.currentOrigin, ent->r.mins, dbgMins );
00333 VectorAdd( ent->r.currentOrigin, ent->r.maxs, dbgMaxs );
00334
00335 G_DebugBoxLines(dbgMins, dbgMaxs, (10.0f/(float)g_svfps.integer)*100);
00336 }
00337 #endif
00338 if (ent->r.contents != CONTENTS_LIGHTSABER)
00339 {
00340 if ((level.time - g_entities[ent->r.ownerNum].client->lastSaberStorageTime) <= 200)
00341 {
00342 ent->r.contents = CONTENTS_LIGHTSABER;
00343 ent->clipmask = MASK_PLAYERSOLID | CONTENTS_LIGHTSABER;
00344 }
00345 }
00346 else
00347 {
00348 ent->r.contents = CONTENTS_LIGHTSABER;
00349 ent->clipmask = MASK_PLAYERSOLID | CONTENTS_LIGHTSABER;
00350 }
00351 }
00352
00353 trap_LinkEntity(ent);
00354
00355 ent->nextthink = level.time;
00356 }
00357
00358 void SaberGotHit( gentity_t *self, gentity_t *other, trace_t *trace )
00359 {
00360 gentity_t *own = &g_entities[self->r.ownerNum];
00361
00362 if (!own || !own->client)
00363 {
00364 return;
00365 }
00366
00367
00368 }
00369
00370 #include "../namespace_begin.h"
00371 qboolean BG_SuperBreakLoseAnim( int anim );
00372 #include "../namespace_end.h"
00373
00374 static GAME_INLINE void SetSaberBoxSize(gentity_t *saberent)
00375 {
00376 gentity_t *owner = NULL;
00377 vec3_t saberOrg, saberTip;
00378 int i;
00379 int j = 0;
00380 int k = 0;
00381 qboolean dualSabers = qfalse;
00382 qboolean alwaysBlock[MAX_SABERS][MAX_BLADES];
00383 qboolean forceBlock = qfalse;
00384
00385 assert(saberent && saberent->inuse);
00386
00387 if (saberent->r.ownerNum < MAX_CLIENTS && saberent->r.ownerNum >= 0)
00388 {
00389 owner = &g_entities[saberent->r.ownerNum];
00390 }
00391 else if (saberent->r.ownerNum >= 0 && saberent->r.ownerNum < ENTITYNUM_WORLD &&
00392 g_entities[saberent->r.ownerNum].s.eType == ET_NPC)
00393 {
00394 owner = &g_entities[saberent->r.ownerNum];
00395 }
00396
00397 if (!owner || !owner->inuse || !owner->client)
00398 {
00399 assert(!"Saber with no owner?");
00400 return;
00401 }
00402
00403 if ( owner->client->saber[1].model
00404 && owner->client->saber[1].model[0] )
00405 {
00406 dualSabers = qtrue;
00407 }
00408
00409 if ( PM_SaberInBrokenParry(owner->client->ps.saberMove)
00410 || BG_SuperBreakLoseAnim( owner->client->ps.torsoAnim ) )
00411 {
00412 for ( i = 0; i < MAX_SABERS; i++ )
00413 {
00414 if ( i > 0 && !dualSabers )
00415 {
00416 for ( j = 0; j < MAX_BLADES; j++ )
00417 {
00418 alwaysBlock[i][j] = qfalse;
00419 }
00420 }
00421 else
00422 {
00423 if ( (owner->client->saber[i].saberFlags2&SFL2_ALWAYS_BLOCK) )
00424 {
00425 for ( j = 0; j < owner->client->saber[i].numBlades; j++ )
00426 {
00427 alwaysBlock[i][j] = qtrue;
00428 forceBlock = qtrue;
00429 }
00430 }
00431 if ( owner->client->saber[i].bladeStyle2Start > 0 )
00432 {
00433 for ( j = owner->client->saber[i].bladeStyle2Start; j < owner->client->saber[i].numBlades; j++ )
00434 {
00435 if ( (owner->client->saber[i].saberFlags2&SFL2_ALWAYS_BLOCK2) )
00436 {
00437 alwaysBlock[i][j] = qtrue;
00438 forceBlock = qtrue;
00439 }
00440 else
00441 {
00442 alwaysBlock[i][j] = qfalse;
00443 }
00444 }
00445 }
00446 }
00447 }
00448 if ( !forceBlock )
00449 {
00450 VectorSet( saberent->r.mins, 0, 0, 0 );
00451 VectorSet( saberent->r.maxs, 0, 0, 0 );
00452 #ifndef FINAL_BUILD
00453 if (g_saberDebugPrint.integer > 1)
00454 {
00455 Com_Printf("Client %i in broken parry, saber box 0\n", owner->s.number);
00456 }
00457 #endif
00458 return;
00459 }
00460 }
00461
00462 if ((level.time - owner->client->lastSaberStorageTime) > 200 ||
00463 (level.time - owner->client->saber[j].blade[k].storageTime) > 100)
00464 {
00465 VectorSet( saberent->r.mins, -SABER_BOX_SIZE, -SABER_BOX_SIZE, -SABER_BOX_SIZE );
00466 VectorSet( saberent->r.maxs, SABER_BOX_SIZE, SABER_BOX_SIZE, SABER_BOX_SIZE );
00467 return;
00468 }
00469
00470 if ( dualSabers
00471 || owner->client->saber[0].numBlades > 1 )
00472 {
00473 if ( owner->client->ps.saberHolstered > 1 )
00474 {
00475
00476 VectorSet( saberent->r.mins, 0, 0, 0 );
00477 VectorSet( saberent->r.maxs, 0, 0, 0 );
00478 return;
00479 }
00480 }
00481 else
00482 {
00483 if ( owner->client->ps.saberHolstered )
00484 {
00485
00486 VectorSet( saberent->r.mins, 0, 0, 0 );
00487 VectorSet( saberent->r.maxs, 0, 0, 0 );
00488 return;
00489 }
00490 }
00491
00492
00493 VectorCopy(saberent->r.currentOrigin, saberent->r.mins);
00494 VectorCopy(saberent->r.currentOrigin, saberent->r.maxs);
00495
00496 for (i = 0; i < 3; i++)
00497 {
00498 for (j = 0; j < MAX_SABERS; j++)
00499 {
00500 if (!owner->client->saber[j].model[0])
00501 {
00502 break;
00503 }
00504 if ( dualSabers
00505 && owner->client->ps.saberHolstered == 1
00506 && j == 1 )
00507 {
00508 j++;
00509 continue;
00510 }
00511 for (k = 0; k < owner->client->saber[j].numBlades; k++)
00512 {
00513 if ( k > 0 )
00514 {
00515 if ( !dualSabers )
00516 {
00517 if ( owner->client->saber[j].numBlades > 1 )
00518 {
00519 if( owner->client->ps.saberHolstered == 1 )
00520 {
00521 break;
00522 }
00523 }
00524 }
00525 }
00526 if ( forceBlock )
00527 {
00528 if ( !alwaysBlock[j][k] )
00529 {
00530 continue;
00531 }
00532 }
00533
00534 VectorCopy(owner->client->saber[j].blade[k].muzzlePoint, saberOrg);
00535 VectorMA(owner->client->saber[j].blade[k].muzzlePoint, owner->client->saber[j].blade[k].lengthMax, owner->client->saber[j].blade[k].muzzleDir, saberTip);
00536
00537 if (saberOrg[i] < saberent->r.mins[i])
00538 {
00539 saberent->r.mins[i] = saberOrg[i];
00540 }
00541 if (saberTip[i] < saberent->r.mins[i])
00542 {
00543 saberent->r.mins[i] = saberTip[i];
00544 }
00545
00546 if (saberOrg[i] > saberent->r.maxs[i])
00547 {
00548 saberent->r.maxs[i] = saberOrg[i];
00549 }
00550 if (saberTip[i] > saberent->r.maxs[i])
00551 {
00552 saberent->r.maxs[i] = saberTip[i];
00553 }
00554
00555
00556 }
00557 }
00558 }
00559
00560 VectorSubtract(saberent->r.mins, saberent->r.currentOrigin, saberent->r.mins);
00561 VectorSubtract(saberent->r.maxs, saberent->r.currentOrigin, saberent->r.maxs);
00562 }
00563
00564 void WP_SaberInitBladeData( gentity_t *ent )
00565 {
00566 gentity_t *saberent = NULL;
00567 gentity_t *checkEnt;
00568 int i = 0;
00569
00570 while (i < level.num_entities)
00571 {
00572 checkEnt = &g_entities[i];
00573
00574 if (checkEnt->inuse && checkEnt->neverFree &&
00575 checkEnt->r.ownerNum == ent->s.number &&
00576 checkEnt->classname && checkEnt->classname[0] &&
00577 !Q_stricmp(checkEnt->classname, "lightsaber"))
00578 {
00579 if (saberent)
00580 {
00581 checkEnt->neverFree = qfalse;
00582 checkEnt->think = G_FreeEntity;
00583 checkEnt->nextthink = level.time;
00584 }
00585 else
00586 {
00587
00588 checkEnt->s.modelGhoul2 = 0;
00589 G_FreeEntity(checkEnt);
00590
00591
00592 G_InitGentity(checkEnt);
00593 saberent = checkEnt;
00594 }
00595 }
00596
00597 i++;
00598 }
00599
00600
00601
00602 if (!saberent)
00603 {
00604 saberent = G_Spawn();
00605 }
00606 ent->client->ps.saberEntityNum = ent->client->saberStoredIndex = saberent->s.number;
00607 saberent->classname = "lightsaber";
00608
00609 saberent->neverFree = qtrue;
00610
00611 saberent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
00612 saberent->r.ownerNum = ent->s.number;
00613
00614 saberent->clipmask = MASK_PLAYERSOLID | CONTENTS_LIGHTSABER;
00615 saberent->r.contents = CONTENTS_LIGHTSABER;
00616
00617 SetSaberBoxSize(saberent);
00618
00619 saberent->mass = 10;
00620
00621 saberent->s.eFlags |= EF_NODRAW;
00622 saberent->r.svFlags |= SVF_NOCLIENT;
00623
00624 saberent->s.modelGhoul2 = 1;
00625
00626
00627
00628 saberent->touch = SaberGotHit;
00629
00630 saberent->think = SaberUpdateSelf;
00631 saberent->genericValue5 = 0;
00632 saberent->nextthink = level.time + 50;
00633
00634 saberSpinSound = G_SoundIndex("sound/weapons/saber/saberspin.wav");
00635 }
00636
00637 #define LOOK_DEFAULT_SPEED 0.15f
00638 #define LOOK_TALKING_SPEED 0.15f
00639
00640 static GAME_INLINE qboolean G_CheckLookTarget( gentity_t *ent, vec3_t lookAngles, float *lookingSpeed )
00641 {
00642
00643
00644
00645
00646 if (ent->s.eType == ET_NPC &&
00647 ent->s.m_iVehicleNum &&
00648 ent->s.NPC_class != CLASS_VEHICLE )
00649 {
00650 if ( TIMER_Done( ent, "lookAround" ) )
00651 {
00652 ent->NPC->shootAngles[YAW] = flrand(0,360);
00653 TIMER_Set( ent, "lookAround", Q_irand( 500, 3000 ) );
00654 }
00655 VectorSet( lookAngles, 0, ent->NPC->shootAngles[YAW], 0 );
00656 return qtrue;
00657 }
00658
00659 if ( ent->client->renderInfo.lookTarget >= 0 && ent->client->renderInfo.lookTarget < ENTITYNUM_WORLD )
00660 {
00661 vec3_t lookDir, lookOrg, eyeOrg;
00662 int i;
00663
00664 if ( ent->client->renderInfo.lookMode == LM_ENT )
00665 {
00666 gentity_t *lookCent = &g_entities[ent->client->renderInfo.lookTarget];
00667 if ( lookCent )
00668 {
00669 if ( lookCent != ent->enemy )
00670 {
00671 *lookingSpeed = LOOK_DEFAULT_SPEED;
00672 }
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682 if ( lookCent->client )
00683 {
00684 VectorCopy( lookCent->client->renderInfo.eyePoint, lookOrg );
00685 }
00686 else if ( lookCent->inuse && !VectorCompare( lookCent->r.currentOrigin, vec3_origin ) )
00687 {
00688 VectorCopy( lookCent->r.currentOrigin, lookOrg );
00689 }
00690 else
00691 {
00692 return qfalse;
00693 }
00694
00695 }
00696 }
00697 else if ( ent->client->renderInfo.lookMode == LM_INTEREST && ent->client->renderInfo.lookTarget > -1 && ent->client->renderInfo.lookTarget < MAX_INTEREST_POINTS )
00698 {
00699 VectorCopy( level.interestPoints[ent->client->renderInfo.lookTarget].origin, lookOrg );
00700 }
00701 else
00702 {
00703 return qfalse;
00704 }
00705
00706 VectorCopy( ent->client->renderInfo.eyePoint, eyeOrg );
00707
00708 VectorSubtract( lookOrg, eyeOrg, lookDir );
00709
00710 vectoangles( lookDir, lookAngles );
00711
00712 for ( i = 0; i < 3; i++ )
00713 {
00714 lookAngles[i] = AngleNormalize180( lookAngles[i] );
00715 ent->client->renderInfo.eyeAngles[i] = AngleNormalize180( ent->client->renderInfo.eyeAngles[i] );
00716 }
00717 AnglesSubtract( lookAngles, ent->client->renderInfo.eyeAngles, lookAngles );
00718 return qtrue;
00719 }
00720
00721 return qfalse;
00722 }
00723
00724
00725
00726
00727
00728
00729 void NPC_SetBoneAngles(gentity_t *ent, char *bone, vec3_t angles);
00730 static GAME_INLINE void G_G2NPCAngles(gentity_t *ent, vec3_t legs[3], vec3_t angles)
00731 {
00732 char *craniumBone = "cranium";
00733 char *thoracicBone = "thoracic";
00734 qboolean looking = qfalse;
00735 vec3_t viewAngles;
00736 vec3_t lookAngles;
00737
00738 if ( ent->client )
00739 {
00740 if ( (ent->client->NPC_class == CLASS_PROBE )
00741 || (ent->client->NPC_class == CLASS_R2D2 )
00742 || (ent->client->NPC_class == CLASS_R5D2)
00743 || (ent->client->NPC_class == CLASS_ATST) )
00744 {
00745 vec3_t trailingLegsAngles;
00746
00747 if (ent->s.eType == ET_NPC &&
00748 ent->s.m_iVehicleNum &&
00749 ent->s.NPC_class != CLASS_VEHICLE )
00750 {
00751 VectorCopy(ent->r.currentAngles, angles);
00752 }
00753 else
00754 {
00755 VectorCopy( ent->client->ps.viewangles, angles );
00756 angles[PITCH] = 0;
00757 }
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771 VectorCopy( ent->client->ps.viewangles, viewAngles );
00772
00773 viewAngles[PITCH] *= 0.5;
00774 VectorCopy( viewAngles, lookAngles );
00775
00776 lookAngles[1] = 0;
00777
00778 if ( ent->client->NPC_class == CLASS_ATST )
00779 {
00780 NPC_SetBoneAngles(ent, thoracicBone, lookAngles);
00781
00782 }
00783
00784 VectorCopy( viewAngles, lookAngles );
00785
00786 if ( ent && ent->client && ent->client->NPC_class == CLASS_ATST )
00787 {
00788
00789 AnglesToAxis( trailingLegsAngles, legs );
00790 }
00791 else
00792 {
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811 }
00812
00813
00814
00815
00816
00817
00818 {
00819
00820 float lookingSpeed = 0.3f;
00821 looking = G_CheckLookTarget( ent, lookAngles, &lookingSpeed );
00822 lookAngles[PITCH] = lookAngles[ROLL] = 0;
00823 if ( looking )
00824 {
00825 ent->client->renderInfo.lookingDebounceTime = level.time + 1000;
00826 }
00827 }
00828 if ( ent->client->renderInfo.lookingDebounceTime > level.time )
00829 {
00830 vec3_t oldLookAngles;
00831
00832 lookAngles[YAW] -= 0;
00833
00834
00835
00836 lookAngles[YAW] = AngleNormalize180( lookAngles[YAW] );
00837
00838
00839
00840 VectorCopy( ent->client->renderInfo.lastHeadAngles, oldLookAngles );
00841 if( VectorCompare( oldLookAngles, lookAngles ) == qfalse )
00842 {
00843
00844
00845 lookAngles[YAW] = oldLookAngles[YAW]+(lookAngles[YAW]-oldLookAngles[YAW])*0.4f;
00846 }
00847
00848 VectorCopy( lookAngles, ent->client->renderInfo.lastHeadAngles );
00849 }
00850 else
00851 {
00852 VectorCopy( lookAngles, ent->client->renderInfo.lastHeadAngles );
00853 }
00854 if ( ent->client->NPC_class == CLASS_ATST )
00855 {
00856 VectorCopy( ent->client->ps.viewangles, lookAngles );
00857 lookAngles[0] = lookAngles[2] = 0;
00858 lookAngles[YAW] -= trailingLegsAngles[YAW];
00859 }
00860 else
00861 {
00862 lookAngles[PITCH] = lookAngles[ROLL] = 0;
00863 lookAngles[YAW] -= ent->client->ps.viewangles[YAW];
00864 }
00865
00866 NPC_SetBoneAngles(ent, craniumBone, lookAngles);
00867
00868
00869 }
00870 else
00871 {
00872
00873
00874
00875 }
00876 }
00877 }
00878
00879 static GAME_INLINE void G_G2PlayerAngles( gentity_t *ent, vec3_t legs[3], vec3_t legsAngles)
00880 {
00881 qboolean tPitching = qfalse,
00882 tYawing = qfalse,
00883 lYawing = qfalse;
00884 float tYawAngle = ent->client->ps.viewangles[YAW],
00885 tPitchAngle = 0,
00886 lYawAngle = ent->client->ps.viewangles[YAW];
00887
00888 int ciLegs = ent->client->ps.legsAnim;
00889 int ciTorso = ent->client->ps.torsoAnim;
00890
00891 vec3_t turAngles;
00892 vec3_t lerpOrg, lerpAng;
00893
00894 if (ent->s.eType == ET_NPC && ent->client)
00895 {
00896 int i = 0;
00897 gentity_t *clEnt;
00898
00899
00900 while (i < MAX_CLIENTS)
00901 {
00902 clEnt = &g_entities[i];
00903
00904 if (clEnt && clEnt->inuse && clEnt->client &&
00905 trap_InPVS(clEnt->client->ps.origin, ent->client->ps.origin))
00906 {
00907 break;
00908 }
00909
00910 i++;
00911 }
00912
00913 if (i == MAX_CLIENTS)
00914 {
00915 return;
00916 }
00917 }
00918
00919 VectorCopy(ent->client->ps.origin, lerpOrg);
00920 VectorCopy(ent->client->ps.viewangles, lerpAng);
00921
00922 if (ent->localAnimIndex <= 1)
00923 {
00924 vec3_t lookAngles;
00925 entityState_t *emplaced = NULL;
00926
00927 if (ent->client->ps.hasLookTarget)
00928 {
00929 VectorSubtract(g_entities[ent->client->ps.lookTarget].r.currentOrigin, ent->client->ps.origin, lookAngles);
00930 vectoangles(lookAngles, lookAngles);
00931 ent->client->lookTime = level.time + 1000;
00932 }
00933 else
00934 {
00935 VectorCopy(ent->client->ps.origin, lookAngles);
00936 }
00937 lookAngles[PITCH] = 0;
00938
00939 if (ent->client->ps.emplacedIndex)
00940 {
00941 emplaced = &g_entities[ent->client->ps.emplacedIndex].s;
00942 }
00943
00944 BG_G2PlayerAngles(ent->ghoul2, ent->client->renderInfo.motionBolt, &ent->s, level.time, lerpOrg, lerpAng, legs,
00945 legsAngles, &tYawing, &tPitching, &lYawing, &tYawAngle, &tPitchAngle, &lYawAngle, FRAMETIME, turAngles,
00946 ent->modelScale, ciLegs, ciTorso, &ent->client->corrTime, lookAngles, ent->client->lastHeadAngles,
00947 ent->client->lookTime, emplaced, NULL);
00948
00949 if (ent->client->ps.heldByClient && ent->client->ps.heldByClient <= MAX_CLIENTS)
00950 {
00951
00952 int heldByIndex = ent->client->ps.heldByClient-1;
00953 gentity_t *other = &g_entities[heldByIndex];
00954 int lHandBolt = 0;
00955
00956 if (other && other->inuse && other->client && other->ghoul2)
00957 {
00958 lHandBolt = trap_G2API_AddBolt(other->ghoul2, 0, "*l_hand");
00959 }
00960 else
00961 {
00962 ent->client->ps.heldByClient = 0;
00963 return;
00964 }
00965
00966 if (lHandBolt)
00967 {
00968 mdxaBone_t boltMatrix;
00969 vec3_t boltOrg;
00970 vec3_t tAngles;
00971
00972 VectorCopy(other->client->ps.viewangles, tAngles);
00973 tAngles[PITCH] = tAngles[ROLL] = 0;
00974
00975 trap_G2API_GetBoltMatrix(other->ghoul2, 0, lHandBolt, &boltMatrix, tAngles, other->client->ps.origin, level.time, 0, other->modelScale);
00976 boltOrg[0] = boltMatrix.matrix[0][3];
00977 boltOrg[1] = boltMatrix.matrix[1][3];
00978 boltOrg[2] = boltMatrix.matrix[2][3];
00979
00980 BG_IK_MoveArm(ent->ghoul2, lHandBolt, level.time, &ent->s, ent->client->ps.torsoAnim, boltOrg, &ent->client->ikStatus,
00981 ent->client->ps.origin, ent->client->ps.viewangles, ent->modelScale, 500, qfalse);
00982 }
00983 }
00984 else if (ent->client->ikStatus)
00985 {
00986 int lHandBolt = 0;
00987
00988 if (ent && ent->inuse && ent->client && ent->ghoul2)
00989 {
00990 lHandBolt = trap_G2API_AddBolt(ent->ghoul2, 0, "*l_hand");
00991 }
00992 else
00993 {
00994 ent->client->ikStatus = qfalse;
00995 }
00996
00997 if (lHandBolt)
00998 {
00999 BG_IK_MoveArm(ent->ghoul2, lHandBolt, level.time, &ent->s,
01000 ent->client->ps.torsoAnim, vec3_origin, &ent->client->ikStatus, ent->client->ps.origin, ent->client->ps.viewangles, ent->modelScale, 500, qtrue);
01001 }
01002 }
01003 }
01004 else if ( ent->m_pVehicle && ent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
01005 {
01006 vec3_t lookAngles;
01007
01008 VectorCopy(ent->client->ps.viewangles, legsAngles);
01009 legsAngles[PITCH] = 0;
01010 AnglesToAxis( legsAngles, legs );
01011
01012 VectorCopy(ent->client->ps.viewangles, lookAngles);
01013 lookAngles[YAW] = lookAngles[ROLL] = 0;
01014
01015 BG_G2ATSTAngles( ent->ghoul2, level.time, lookAngles );
01016 }
01017 else if (ent->NPC)
01018 {
01019 if (ent->s.eType == ET_NPC &&
01020 ent->s.NPC_class == CLASS_VEHICLE &&
01021 ent->m_pVehicle &&
01022 ent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
01023 {
01024 VectorCopy(ent->client->ps.viewangles, legsAngles);
01025 AnglesToAxis( legsAngles, legs );
01026 }
01027 else
01028 {
01029 G_G2NPCAngles(ent, legs, legsAngles);
01030 }
01031 }
01032 }
01033
01034 static GAME_INLINE qboolean SaberAttacking(gentity_t *self)
01035 {
01036 if (PM_SaberInParry(self->client->ps.saberMove))
01037 {
01038 return qfalse;
01039 }
01040 if (PM_SaberInBrokenParry(self->client->ps.saberMove))
01041 {
01042 return qfalse;
01043 }
01044 if (PM_SaberInDeflect(self->client->ps.saberMove))
01045 {
01046 return qfalse;
01047 }
01048 if (PM_SaberInBounce(self->client->ps.saberMove))
01049 {
01050 return qfalse;
01051 }
01052 if (PM_SaberInKnockaway(self->client->ps.saberMove))
01053 {
01054 return qfalse;
01055 }
01056
01057 if (BG_SaberInAttack(self->client->ps.saberMove))
01058 {
01059 if (self->client->ps.weaponstate == WEAPON_FIRING && self->client->ps.saberBlocked == BLOCKED_NONE)
01060 {
01061 return qtrue;
01062 }
01063 }
01064
01065 if (BG_SaberInSpecial(self->client->ps.saberMove))
01066 {
01067 return qtrue;
01068 }
01069
01070 return qfalse;
01071 }
01072
01073 typedef enum
01074 {
01075 LOCK_FIRST = 0,
01076 LOCK_TOP = LOCK_FIRST,
01077 LOCK_DIAG_TR,
01078 LOCK_DIAG_TL,
01079 LOCK_DIAG_BR,
01080 LOCK_DIAG_BL,
01081 LOCK_R,
01082 LOCK_L,
01083 LOCK_RANDOM
01084 } sabersLockMode_t;
01085
01086 #define LOCK_IDEAL_DIST_TOP 32.0f
01087 #define LOCK_IDEAL_DIST_CIRCLE 48.0f
01088
01089 #define SABER_HITDAMAGE 35
01090 void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlock );
01091
01092 int G_SaberLockAnim( int attackerSaberStyle, int defenderSaberStyle, int topOrSide, int lockOrBreakOrSuperBreak, int winOrLose )
01093 {
01094 int baseAnim = -1;
01095 if ( lockOrBreakOrSuperBreak == SABERLOCK_LOCK )
01096 {
01097 if ( attackerSaberStyle == defenderSaberStyle
01098 || (attackerSaberStyle>=SS_FAST&&attackerSaberStyle<=SS_TAVION&&defenderSaberStyle>=SS_FAST&&defenderSaberStyle<=SS_TAVION) )
01099 {
01100 if ( winOrLose == SABERLOCK_LOSE )
01101 {
01102 switch ( defenderSaberStyle )
01103 {
01104 case SS_DUAL:
01105 if ( topOrSide == SABERLOCK_TOP )
01106 {
01107 baseAnim = BOTH_LK_DL_DL_T_L_2;
01108 }
01109 else
01110 {
01111 baseAnim = BOTH_LK_DL_DL_S_L_2;
01112 }
01113 break;
01114 case SS_STAFF:
01115 if ( topOrSide == SABERLOCK_TOP )
01116 {
01117 baseAnim = BOTH_LK_ST_ST_T_L_2;
01118 }
01119 else
01120 {
01121 baseAnim = BOTH_LK_ST_ST_S_L_2;
01122 }
01123 break;
01124 default:
01125 if ( topOrSide == SABERLOCK_TOP )
01126 {
01127 baseAnim = BOTH_LK_S_S_T_L_2;
01128 }
01129 else
01130 {
01131 baseAnim = BOTH_LK_S_S_S_L_2;
01132 }
01133 break;
01134 }
01135 }
01136 }
01137 }
01138 if ( baseAnim == -1 )
01139 {
01140 switch ( attackerSaberStyle )
01141 {
01142 case SS_DUAL:
01143 switch ( defenderSaberStyle )
01144 {
01145 case SS_DUAL:
01146 baseAnim = BOTH_LK_DL_DL_S_B_1_L;
01147 break;
01148 case SS_STAFF:
01149 baseAnim = BOTH_LK_DL_ST_S_B_1_L;
01150 break;
01151 default:
01152 baseAnim = BOTH_LK_DL_S_S_B_1_L;
01153 break;
01154 }
01155 break;
01156 case SS_STAFF:
01157 switch ( defenderSaberStyle )
01158 {
01159 case SS_DUAL:
01160 baseAnim = BOTH_LK_ST_DL_S_B_1_L;
01161 break;
01162 case SS_STAFF:
01163 baseAnim = BOTH_LK_ST_ST_S_B_1_L;
01164 break;
01165 default:
01166 baseAnim = BOTH_LK_ST_S_S_B_1_L;
01167 break;
01168 }
01169 break;
01170 default:
01171 switch ( defenderSaberStyle )
01172 {
01173 case SS_DUAL:
01174 baseAnim = BOTH_LK_S_DL_S_B_1_L;
01175 break;
01176 case SS_STAFF:
01177 baseAnim = BOTH_LK_S_ST_S_B_1_L;
01178 break;
01179 default:
01180 baseAnim = BOTH_LK_S_S_S_B_1_L;
01181 break;
01182 }
01183 break;
01184 }
01185
01186 if ( topOrSide == SABERLOCK_TOP )
01187 {
01188 baseAnim += 5;
01189 }
01190
01191 if ( lockOrBreakOrSuperBreak == SABERLOCK_LOCK )
01192 {
01193 baseAnim += 2;
01194 }
01195 else
01196 {
01197 if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK )
01198 {
01199 baseAnim += 3;
01200 }
01201
01202 if ( winOrLose == SABERLOCK_WIN )
01203 {
01204 baseAnim += 1;
01205 }
01206 }
01207 }
01208 return baseAnim;
01209 }
01210
01211 #include "../namespace_begin.h"
01212 extern qboolean BG_CheckIncrementLockAnim( int anim, int winOrLose );
01213 #include "../namespace_end.h"
01214 #define LOCK_IDEAL_DIST_JKA 46.0f//all of the new saberlocks are 46.08 from each other because Richard Lico is da MAN
01215
01216 static GAME_INLINE qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLockMode_t lockMode )
01217 {
01218 int attAnim, defAnim = 0;
01219 float attStart = 0.5f, defStart = 0.5f;
01220 float idealDist = 48.0f;
01221 vec3_t attAngles, defAngles, defDir;
01222 vec3_t newOrg;
01223 vec3_t attDir;
01224 float diff = 0;
01225 trace_t trace;
01226
01227
01228 if ( lockMode == LOCK_RANDOM )
01229 {
01230 lockMode = (sabersLockMode_t)Q_irand( (int)LOCK_FIRST, (int)(LOCK_RANDOM)-1 );
01231 }
01232 if ( attacker->client->ps.fd.saberAnimLevel >= SS_FAST
01233 && attacker->client->ps.fd.saberAnimLevel <= SS_TAVION
01234 && defender->client->ps.fd.saberAnimLevel >= SS_FAST
01235 && defender->client->ps.fd.saberAnimLevel <= SS_TAVION )
01236 {
01237 switch ( lockMode )
01238 {
01239 case LOCK_TOP:
01240 attAnim = BOTH_BF2LOCK;
01241 defAnim = BOTH_BF1LOCK;
01242 attStart = defStart = 0.5f;
01243 idealDist = LOCK_IDEAL_DIST_TOP;
01244 break;
01245 case LOCK_DIAG_TR:
01246 attAnim = BOTH_CCWCIRCLELOCK;
01247 defAnim = BOTH_CWCIRCLELOCK;
01248 attStart = defStart = 0.5f;
01249 idealDist = LOCK_IDEAL_DIST_CIRCLE;
01250 break;
01251 case LOCK_DIAG_TL:
01252 attAnim = BOTH_CWCIRCLELOCK;
01253 defAnim = BOTH_CCWCIRCLELOCK;
01254 attStart = defStart = 0.5f;
01255 idealDist = LOCK_IDEAL_DIST_CIRCLE;
01256 break;
01257 case LOCK_DIAG_BR:
01258 attAnim = BOTH_CWCIRCLELOCK;
01259 defAnim = BOTH_CCWCIRCLELOCK;
01260 attStart = defStart = 0.85f;
01261 idealDist = LOCK_IDEAL_DIST_CIRCLE;
01262 break;
01263 case LOCK_DIAG_BL:
01264 attAnim = BOTH_CCWCIRCLELOCK;
01265 defAnim = BOTH_CWCIRCLELOCK;
01266 attStart = defStart = 0.85f;
01267 idealDist = LOCK_IDEAL_DIST_CIRCLE;
01268 break;
01269 case LOCK_R:
01270 attAnim = BOTH_CCWCIRCLELOCK;
01271 defAnim = BOTH_CWCIRCLELOCK;
01272 attStart = defStart = 0.75f;
01273 idealDist = LOCK_IDEAL_DIST_CIRCLE;
01274 break;
01275 case LOCK_L:
01276 attAnim = BOTH_CWCIRCLELOCK;
01277 defAnim = BOTH_CCWCIRCLELOCK;
01278 attStart = defStart = 0.75f;
01279 idealDist = LOCK_IDEAL_DIST_CIRCLE;
01280 break;
01281 default:
01282 return qfalse;
01283 break;
01284 }
01285 }
01286 else
01287 {
01288 idealDist = LOCK_IDEAL_DIST_JKA;
01289 if ( lockMode == LOCK_TOP )
01290 {
01291 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_TOP, SABERLOCK_LOCK, SABERLOCK_WIN );
01292 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_TOP, SABERLOCK_LOCK, SABERLOCK_LOSE );
01293 attStart = defStart = 0.5f;
01294 }
01295 else
01296 {
01297 switch ( lockMode )
01298 {
01299 case LOCK_DIAG_TR:
01300 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_WIN );
01301 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_LOSE );
01302 attStart = defStart = 0.5f;
01303 break;
01304 case LOCK_DIAG_TL:
01305 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_LOSE );
01306 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_WIN );
01307 attStart = defStart = 0.5f;
01308 break;
01309 case LOCK_DIAG_BR:
01310 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_WIN );
01311 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_LOSE );
01312 if ( BG_CheckIncrementLockAnim( attAnim, SABERLOCK_WIN ) )
01313 {
01314 attStart = 0.85f;
01315 }
01316 else
01317 {
01318 attStart = 0.15f;
01319 }
01320 if ( BG_CheckIncrementLockAnim( defAnim, SABERLOCK_LOSE ) )
01321 {
01322 defStart = 0.85f;
01323 }
01324 else
01325 {
01326 defStart = 0.15f;
01327 }
01328 break;
01329 case LOCK_DIAG_BL:
01330 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_LOSE );
01331 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_WIN );
01332 if ( BG_CheckIncrementLockAnim( attAnim, SABERLOCK_WIN ) )
01333 {
01334 attStart = 0.85f;
01335 }
01336 else
01337 {
01338 attStart = 0.15f;
01339 }
01340 if ( BG_CheckIncrementLockAnim( defAnim, SABERLOCK_LOSE ) )
01341 {
01342 defStart = 0.85f;
01343 }
01344 else
01345 {
01346 defStart = 0.15f;
01347 }
01348 break;
01349 case LOCK_R:
01350 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_LOSE );
01351 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_WIN );
01352 if ( BG_CheckIncrementLockAnim( attAnim, SABERLOCK_WIN ) )
01353 {
01354 attStart = 0.75f;
01355 }
01356 else
01357 {
01358 attStart = 0.25f;
01359 }
01360 if ( BG_CheckIncrementLockAnim( defAnim, SABERLOCK_LOSE ) )
01361 {
01362 defStart = 0.75f;
01363 }
01364 else
01365 {
01366 defStart = 0.25f;
01367 }
01368 break;
01369 case LOCK_L:
01370 attAnim = G_SaberLockAnim( attacker->client->ps.fd.saberAnimLevel, defender->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_WIN );
01371 defAnim = G_SaberLockAnim( defender->client->ps.fd.saberAnimLevel, attacker->client->ps.fd.saberAnimLevel, SABERLOCK_SIDE, SABERLOCK_LOCK, SABERLOCK_LOSE );
01372
01373 if ( BG_CheckIncrementLockAnim( attAnim, SABERLOCK_WIN ) )
01374 {
01375 attStart = 0.75f;
01376 }
01377 else
01378 {
01379 attStart = 0.25f;
01380 }
01381 if ( BG_CheckIncrementLockAnim( defAnim, SABERLOCK_LOSE ) )
01382 {
01383 defStart = 0.75f;
01384 }
01385 else
01386 {
01387 defStart = 0.25f;
01388 }
01389 break;
01390 default:
01391 return qfalse;
01392 break;
01393 }
01394 }
01395 }
01396
01397 G_SetAnim(attacker, NULL, SETANIM_BOTH, attAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01398 attacker->client->ps.saberLockFrame = bgAllAnims[attacker->localAnimIndex].anims[attAnim].firstFrame+(bgAllAnims[attacker->localAnimIndex].anims[attAnim].numFrames*attStart);
01399
01400 G_SetAnim(defender, NULL, SETANIM_BOTH, defAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
01401 defender->client->ps.saberLockFrame = bgAllAnims[defender->localAnimIndex].anims[defAnim].firstFrame+(bgAllAnims[defender->localAnimIndex].anims[defAnim].numFrames*defStart);
01402
01403 attacker->client->ps.saberLockHits = 0;
01404 defender->client->ps.saberLockHits = 0;
01405
01406 attacker->client->ps.saberLockAdvance = qfalse;
01407 defender->client->ps.saberLockAdvance = qfalse;
01408
01409 VectorClear( attacker->client->ps.velocity );
01410 VectorClear( defender->client->ps.velocity );
01411 attacker->client->ps.saberLockTime = defender->client->ps.saberLockTime = level.time + 10000;
01412 attacker->client->ps.saberLockEnemy = defender->s.number;
01413 defender->client->ps.saberLockEnemy = attacker->s.number;
01414 attacker->client->ps.weaponTime = defender->client->ps.weaponTime = Q_irand( 1000, 3000 );
01415
01416 VectorSubtract( defender->r.currentOrigin, attacker->r.currentOrigin, defDir );
01417 VectorCopy( attacker->client->ps.viewangles, attAngles );
01418 attAngles[YAW] = vectoyaw( defDir );
01419 SetClientViewAngle( attacker, attAngles );
01420 defAngles[PITCH] = attAngles[PITCH]*-1;
01421 defAngles[YAW] = AngleNormalize180( attAngles[YAW] + 180);
01422 defAngles[ROLL] = 0;
01423 SetClientViewAngle( defender, defAngles );
01424
01425
01426 diff = VectorNormalize( defDir ) - idealDist;
01427
01428 VectorMA( attacker->r.currentOrigin, diff*0.5f, defDir, newOrg );
01429
01430 trap_Trace( &trace, attacker->r.currentOrigin, attacker->r.mins, attacker->r.maxs, newOrg, attacker->s.number, attacker->clipmask );
01431 if ( !trace.startsolid && !trace.allsolid )
01432 {
01433 G_SetOrigin( attacker, trace.endpos );
01434 if (attacker->client)
01435 {
01436 VectorCopy(trace.endpos, attacker->client->ps.origin);
01437 }
01438 trap_LinkEntity( attacker );
01439 }
01440
01441 VectorSubtract( attacker->r.currentOrigin, defender->r.currentOrigin, attDir );
01442 diff = VectorNormalize( attDir ) - idealDist;
01443
01444 VectorMA( defender->r.currentOrigin, diff, attDir, newOrg );
01445 trap_Trace( &trace, defender->r.currentOrigin, defender->r.mins, defender->r.maxs, newOrg, defender->s.number, defender->clipmask );
01446 if ( !trace.startsolid && !trace.allsolid )
01447 {
01448 if (defender->client)
01449 {
01450 VectorCopy(trace.endpos, defender->client->ps.origin);
01451 }
01452 G_SetOrigin( defender, trace.endpos );
01453 trap_LinkEntity( defender );
01454 }
01455
01456
01457 return qtrue;
01458 }
01459
01460 qboolean WP_SabersCheckLock( gentity_t *ent1, gentity_t *ent2 )
01461 {
01462 float dist;
01463 qboolean ent1BlockingPlayer = qfalse;
01464 qboolean ent2BlockingPlayer = qfalse;
01465
01466 if ( g_debugSaberLocks.integer )
01467 {
01468 WP_SabersCheckLock2( ent1, ent2, LOCK_RANDOM );
01469 return qtrue;
01470 }
01471
01472
01473 if (g_gametype.integer == GT_POWERDUEL)
01474 {
01475 return qfalse;
01476 }
01477
01478 if (!g_saberLocking.integer)
01479 {
01480 return qfalse;
01481 }
01482
01483 if (!ent1->client || !ent2->client)
01484 {
01485 return qfalse;
01486 }
01487
01488 if (ent1->s.eType == ET_NPC ||
01489 ent2->s.eType == ET_NPC)
01490 {
01491 if (ent1->client->playerTeam == ent2->client->playerTeam)
01492 {
01493 return qfalse;
01494 }
01495 }
01496
01497 if (!ent1->client->ps.saberEntityNum ||
01498 !ent2->client->ps.saberEntityNum ||
01499 ent1->client->ps.saberInFlight ||
01500 ent2->client->ps.saberInFlight)
01501 {
01502 return qfalse;
01503 }
01504
01505 if (ent1->s.eType != ET_NPC && ent2->s.eType != ET_NPC)
01506 {
01507 if (!ent1->client->ps.duelInProgress ||
01508 !ent2->client->ps.duelInProgress ||
01509 ent1->client->ps.duelIndex != ent2->s.number ||
01510 ent2->client->ps.duelIndex != ent1->s.number)
01511 {
01512 if (g_gametype.integer != GT_DUEL && g_gametype.integer != GT_POWERDUEL)
01513 {
01514 return qfalse;
01515 }
01516 }
01517 }
01518
01519 if ( fabs( ent1->r.currentOrigin[2]-ent2->r.currentOrigin[2] ) > 16 )
01520 {
01521 return qfalse;
01522 }
01523 if ( ent1->client->ps.groundEntityNum == ENTITYNUM_NONE ||
01524 ent2->client->ps.groundEntityNum == ENTITYNUM_NONE )
01525 {
01526 return qfalse;
01527 }
01528 dist = DistanceSquared(ent1->r.currentOrigin,ent2->r.currentOrigin);
01529 if ( dist < 64 || dist > 6400 )
01530 {
01531 return qfalse;
01532 }
01533
01534 if (BG_InSpecialJump(ent1->client->ps.legsAnim))
01535 {
01536 return qfalse;
01537 }
01538 if (BG_InSpecialJump(ent2->client->ps.legsAnim))
01539 {
01540 return qfalse;
01541 }
01542
01543 if (BG_InRoll(&ent1->client->ps, ent1->client->ps.legsAnim))
01544 {
01545 return qfalse;
01546 }
01547 if (BG_InRoll(&ent2->client->ps, ent2->client->ps.legsAnim))
01548 {
01549 return qfalse;
01550 }
01551
01552 if (ent1->client->ps.forceHandExtend != HANDEXTEND_NONE ||
01553 ent2->client->ps.forceHandExtend != HANDEXTEND_NONE)
01554 {
01555 return qfalse;
01556 }
01557
01558 if ((ent1->client->ps.pm_flags & PMF_DUCKED) ||
01559 (ent2->client->ps.pm_flags & PMF_DUCKED))
01560 {
01561 return qfalse;
01562 }
01563
01564 if ( (ent1->client->saber[0].saberFlags&SFL_NOT_LOCKABLE)
01565 || (ent2->client->saber[0].saberFlags&SFL_NOT_LOCKABLE) )
01566 {
01567 return qfalse;
01568 }
01569 if ( ent1->client->saber[1].model
01570 && ent1->client->saber[1].model[0]
01571 && !ent1->client->ps.saberHolstered
01572 && (ent1->client->saber[1].saberFlags&SFL_NOT_LOCKABLE) )
01573 {
01574 return qfalse;
01575 }
01576 if ( ent2->client->saber[1].model
01577 && ent2->client->saber[1].model[0]
01578 && !ent2->client->ps.saberHolstered
01579 && (ent2->client->saber[1].saberFlags&SFL_NOT_LOCKABLE) )
01580 {
01581 return qfalse;
01582 }
01583
01584 if (!InFront( ent1->client->ps.origin, ent2->client->ps.origin, ent2->client->ps.viewangles, 0.4f ))
01585 {
01586 return qfalse;
01587 }
01588 if (!InFront( ent2->client->ps.origin, ent1->client->ps.origin, ent1->client->ps.viewangles, 0.4f ))
01589 {
01590 return qfalse;
01591 }
01592
01593
01594 if ( ent1->client->ps.torsoAnim == BOTH_A1_T__B_ ||
01595 ent1->client->ps.torsoAnim == BOTH_A2_T__B_ ||
01596 ent1->client->ps.torsoAnim == BOTH_A3_T__B_ ||
01597 ent1->client->ps.torsoAnim == BOTH_A4_T__B_ ||
01598 ent1->client->ps.torsoAnim == BOTH_A5_T__B_ ||
01599 ent1->client->ps.torsoAnim == BOTH_A6_T__B_ ||
01600 ent1->client->ps.torsoAnim == BOTH_A7_T__B_)
01601 {
01602 return WP_SabersCheckLock2( ent1, ent2, LOCK_TOP );
01603 }
01604
01605 if ( ent2->client->ps.torsoAnim == BOTH_A1_T__B_ ||
01606 ent2->client->ps.torsoAnim == BOTH_A2_T__B_ ||
01607 ent2->client->ps.torsoAnim == BOTH_A3_T__B_ ||
01608 ent2->client->ps.torsoAnim == BOTH_A4_T__B_ ||
01609 ent2->client->ps.torsoAnim == BOTH_A5_T__B_ ||
01610 ent2->client->ps.torsoAnim == BOTH_A6_T__B_ ||
01611 ent2->client->ps.torsoAnim == BOTH_A7_T__B_)
01612 {
01613 return WP_SabersCheckLock2( ent2, ent1, LOCK_TOP );
01614 }
01615
01616 if ( ent1->s.number == 0 &&
01617 ent1->client->ps.saberBlocking == BLK_WIDE && ent1->client->ps.weaponTime <= 0 )
01618 {
01619 ent1BlockingPlayer = qtrue;
01620 }
01621 if ( ent2->s.number == 0 &&
01622 ent2->client->ps.saberBlocking == BLK_WIDE && ent2->client->ps.weaponTime <= 0 )
01623 {
01624 ent2BlockingPlayer = qtrue;
01625 }
01626
01627
01628 if ( ent1->client->ps.torsoAnim == BOTH_A1_TR_BL ||
01629 ent1->client->ps.torsoAnim == BOTH_A2_TR_BL ||
01630 ent1->client->ps.torsoAnim == BOTH_A3_TR_BL ||
01631 ent1->client->ps.torsoAnim == BOTH_A4_TR_BL ||
01632 ent1->client->ps.torsoAnim == BOTH_A5_TR_BL ||
01633 ent1->client->ps.torsoAnim == BOTH_A6_TR_BL ||
01634 ent1->client->ps.torsoAnim == BOTH_A7_TR_BL)
01635 {
01636 if ( ent2BlockingPlayer )
01637 {
01638 return WP_SabersCheckLock2( ent1, ent2, LOCK_DIAG_TR );
01639 }
01640 if ( ent2->client->ps.torsoAnim == BOTH_A1_TR_BL ||
01641 ent2->client->ps.torsoAnim == BOTH_A2_TR_BL ||
01642 ent2->client->ps.torsoAnim == BOTH_A3_TR_BL ||
01643 ent2->client->ps.torsoAnim == BOTH_A4_TR_BL ||
01644 ent2->client->ps.torsoAnim == BOTH_A5_TR_BL ||
01645 ent2->client->ps.torsoAnim == BOTH_A6_TR_BL ||
01646 ent2->client->ps.torsoAnim == BOTH_A7_TR_BL ||
01647 ent2->client->ps.torsoAnim == BOTH_P1_S1_TL )
01648 {
01649 return WP_SabersCheckLock2( ent1, ent2, LOCK_DIAG_TR );
01650 }
01651 if ( ent2->client->ps.torsoAnim == BOTH_A1_BR_TL ||
01652 ent2->client->ps.torsoAnim == BOTH_A2_BR_TL ||
01653 ent2->client->ps.torsoAnim == BOTH_A3_BR_TL ||
01654 ent2->client->ps.torsoAnim == BOTH_A4_BR_TL ||
01655 ent2->client->ps.torsoAnim == BOTH_A5_BR_TL ||
01656 ent2->client->ps.torsoAnim == BOTH_A6_BR_TL ||
01657 ent2->client->ps.torsoAnim == BOTH_A7_BR_TL ||
01658 ent2->client->ps.torsoAnim == BOTH_P1_S1_BL )
01659 {
01660 return WP_SabersCheckLock2( ent1, ent2, LOCK_DIAG_BL );
01661 }
01662 return qfalse;
01663 }
01664
01665 if ( ent2->client->ps.torsoAnim == BOTH_A1_TR_BL ||
01666 ent2->client->ps.torsoAnim == BOTH_A2_TR_BL ||
01667 ent2->client->ps.torsoAnim == BOTH_A3_TR_BL ||
01668 ent2->client->ps.torsoAnim == BOTH_A4_TR_BL ||
01669 ent2->client->ps.torsoAnim == BOTH_A5_TR_BL ||
01670 ent2->client->ps.torsoAnim == BOTH_A6_TR_BL ||
01671 ent2->client->ps.torsoAnim == BOTH_A7_TR_BL)
01672 {
01673 if ( ent1BlockingPlayer )
01674 {
01675 return WP_SabersCheckLock2( ent2, ent1, LOCK_DIAG_TR );
01676 }
01677 if ( ent1->client->ps.torsoAnim == BOTH_A1_TR_BL ||
01678 ent1->client->ps.torsoAnim == BOTH_A2_TR_BL ||
01679 ent1->client->ps.torsoAnim == BOTH_A3_TR_BL ||
01680 ent1->client->ps.torsoAnim == BOTH_A4_TR_BL ||
01681 ent1->client->ps.torsoAnim == BOTH_A5_TR_BL ||
01682 ent1->client->ps.torsoAnim == BOTH_A6_TR_BL ||
01683 ent1->client->ps.torsoAnim == BOTH_A7_TR_BL ||
01684 ent1->client->ps.torsoAnim == BOTH_P1_S1_TL )
01685 {
01686 return WP_SabersCheckLock2( ent2, ent1, LOCK_DIAG_TR );
01687 }
01688 if ( ent1->client->ps.torsoAnim == BOTH_A1_BR_TL ||
01689 ent1->client->ps.torsoAnim == BOTH_A2_BR_TL ||
01690 ent1->client->ps.torsoAnim == BOTH_A3_BR_TL ||
01691 ent1->client->ps.torsoAnim == BOTH_A4_BR_TL ||
01692 ent1->client->ps.torsoAnim == BOTH_A5_BR_TL ||
01693 ent1->client->ps.torsoAnim == BOTH_A6_BR_TL ||
01694 ent1->client->ps.torsoAnim == BOTH_A7_BR_TL ||
01695 ent1->client->ps.torsoAnim == BOTH_P1_S1_BL )
01696 {
01697 return WP_SabersCheckLock2( ent2, ent1, LOCK_DIAG_BL );
01698 }
01699 return qfalse;
01700 }
01701
01702
01703 if ( ent1->client->ps.torsoAnim == BOTH_A1_TL_BR ||
01704 ent1->client->ps.torsoAnim == BOTH_A2_TL_BR ||
01705 ent1->client->ps.torsoAnim == BOTH_A3_TL_BR ||
01706 ent1->client->ps.torsoAnim == BOTH_A4_TL_BR ||
01707 ent1->client->ps.torsoAnim == BOTH_A5_TL_BR ||
01708 ent1->client->ps.torsoAnim == BOTH_A6_TL_BR ||
01709 ent1->client->ps.torsoAnim == BOTH_A7_TL_BR)
01710 {
01711 if ( ent2BlockingPlayer )
01712 {
01713 return WP_SabersCheckLock2( ent1, ent2, LOCK_DIAG_TL );
01714 }
01715 if ( ent2->client->ps.torsoAnim == BOTH_A1_TL_BR ||
01716 ent2->client->ps.torsoAnim == BOTH_A2_TL_BR ||
01717 ent2->client->ps.torsoAnim == BOTH_A3_TL_BR ||
01718 ent2->client->ps.torsoAnim == BOTH_A4_TL_BR ||
01719 ent2->client->ps.torsoAnim == BOTH_A5_TL_BR ||
01720 ent2->client->ps.torsoAnim == BOTH_A6_TL_BR ||
01721 ent2->client->ps.torsoAnim == BOTH_A7_TL_BR ||
01722 ent2->client->ps.torsoAnim == BOTH_P1_S1_TR )
01723 {
01724 return WP_SabersCheckLock2( ent1, ent2, LOCK_DIAG_TL );
01725 }
01726 if ( ent2->client->ps.torsoAnim == BOTH_A1_BL_TR ||
01727 ent2->client->ps.torsoAnim == BOTH_A2_BL_TR ||
01728 ent2->client->ps.torsoAnim == BOTH_A3_BL_TR ||
01729 ent2->client->ps.torsoAnim == BOTH_A4_BL_TR ||
01730 ent2->client->ps.torsoAnim == BOTH_A5_BL_TR ||
01731 ent2->client->ps.torsoAnim == BOTH_A6_BL_TR ||
01732 ent2->client->ps.torsoAnim == BOTH_A7_BL_TR ||
01733 ent2->client->ps.torsoAnim == BOTH_P1_S1_BR )
01734 {
01735 return WP_SabersCheckLock2( ent1, ent2, LOCK_DIAG_BR );
01736 }
01737 return qfalse;
01738 }
01739
01740 if ( ent2->client->ps.torsoAnim == BOTH_A1_TL_BR ||
01741 ent2->client->ps.torsoAnim == BOTH_A2_TL_BR ||
01742 ent2->client->ps.torsoAnim == BOTH_A3_TL_BR ||
01743 ent2->client->ps.torsoAnim == BOTH_A4_TL_BR ||
01744 ent2->client->ps.torsoAnim == BOTH_A5_TL_BR ||
01745 ent2->client->ps.torsoAnim == BOTH_A6_TL_BR ||
01746 ent2->client->ps.torsoAnim == BOTH_A7_TL_BR)
01747 {
01748 if ( ent1BlockingPlayer )
01749 {
01750 return WP_SabersCheckLock2( ent2, ent1, LOCK_DIAG_TL );
01751 }
01752 if ( ent1->client->ps.torsoAnim == BOTH_A1_TL_BR ||
01753 ent1->client->ps.torsoAnim == BOTH_A2_TL_BR ||
01754 ent1->client->ps.torsoAnim == BOTH_A3_TL_BR ||
01755 ent1->client->ps.torsoAnim == BOTH_A4_TL_BR ||
01756 ent1->client->ps.torsoAnim == BOTH_A5_TL_BR ||
01757 ent1->client->ps.torsoAnim == BOTH_A6_TL_BR ||
01758 ent1->client->ps.torsoAnim == BOTH_A7_TL_BR ||
01759 ent1->client->ps.torsoAnim == BOTH_P1_S1_TR )
01760 {
01761 return WP_SabersCheckLock2( ent2, ent1, LOCK_DIAG_TL );
01762 }
01763 if ( ent1->client->ps.torsoAnim == BOTH_A1_BL_TR ||
01764 ent1->client->ps.torsoAnim == BOTH_A2_BL_TR ||
01765 ent1->client->ps.torsoAnim == BOTH_A3_BL_TR ||
01766 ent1->client->ps.torsoAnim == BOTH_A4_BL_TR ||
01767 ent1->client->ps.torsoAnim == BOTH_A5_BL_TR ||
01768 ent1->client->ps.torsoAnim == BOTH_A6_BL_TR ||
01769 ent1->client->ps.torsoAnim == BOTH_A7_BL_TR ||
01770 ent1->client->ps.torsoAnim == BOTH_P1_S1_BR )
01771 {
01772 return WP_SabersCheckLock2( ent2, ent1, LOCK_DIAG_BR );
01773 }
01774 return qfalse;
01775 }
01776
01777 if ( ent1->client->ps.torsoAnim == BOTH_A1__L__R ||
01778 ent1->client->ps.torsoAnim == BOTH_A2__L__R ||
01779 ent1->client->ps.torsoAnim == BOTH_A3__L__R ||
01780 ent1->client->ps.torsoAnim == BOTH_A4__L__R ||
01781 ent1->client->ps.torsoAnim == BOTH_A5__L__R ||
01782 ent1->client->ps.torsoAnim == BOTH_A6__L__R ||
01783 ent1->client->ps.torsoAnim == BOTH_A7__L__R)
01784 {
01785 if ( ent2BlockingPlayer )
01786 {
01787 return WP_SabersCheckLock2( ent1, ent2, LOCK_L );
01788 }
01789 if ( ent2->client->ps.torsoAnim == BOTH_A1_TL_BR ||
01790 ent2->client->ps.torsoAnim == BOTH_A2_TL_BR ||
01791 ent2->client->ps.torsoAnim == BOTH_A3_TL_BR ||
01792 ent2->client->ps.torsoAnim == BOTH_A4_TL_BR ||
01793 ent2->client->ps.torsoAnim == BOTH_A5_TL_BR ||
01794 ent2->client->ps.torsoAnim == BOTH_A6_TL_BR ||
01795 ent2->client->ps.torsoAnim == BOTH_A7_TL_BR ||
01796 ent2->client->ps.torsoAnim == BOTH_P1_S1_TR ||
01797 ent2->client->ps.torsoAnim == BOTH_P1_S1_BL )
01798 {
01799 return WP_SabersCheckLock2( ent1, ent2, LOCK_L );
01800 }
01801 return qfalse;
01802 }
01803 if ( ent2->client->ps.torsoAnim == BOTH_A1__L__R ||
01804 ent2->client->ps.torsoAnim == BOTH_A2__L__R ||
01805 ent2->client->ps.torsoAnim == BOTH_A3__L__R ||
01806 ent2->client->ps.torsoAnim == BOTH_A4__L__R ||
01807 ent2->client->ps.torsoAnim == BOTH_A5__L__R ||
01808 ent2->client->ps.torsoAnim == BOTH_A6__L__R ||
01809 ent2->client->ps.torsoAnim == BOTH_A7__L__R)
01810 {
01811 if ( ent1BlockingPlayer )
01812 {
01813 return WP_SabersCheckLock2( ent2, ent1, LOCK_L );
01814 }
01815 if ( ent1->client->ps.torsoAnim == BOTH_A1_TL_BR ||
01816 ent1->client->ps.torsoAnim == BOTH_A2_TL_BR ||
01817 ent1->client->ps.torsoAnim == BOTH_A3_TL_BR ||
01818 ent1->client->ps.torsoAnim == BOTH_A4_TL_BR ||
01819 ent1->client->ps.torsoAnim == BOTH_A5_TL_BR ||
01820 ent1->client->ps.torsoAnim == BOTH_A6_TL_BR ||
01821 ent1->client->ps.torsoAnim == BOTH_A7_TL_BR ||
01822 ent1->client->ps.torsoAnim == BOTH_P1_S1_TR ||
01823 ent1->client->ps.torsoAnim == BOTH_P1_S1_BL )
01824 {
01825 return WP_SabersCheckLock2( ent2, ent1, LOCK_L );
01826 }
01827 return qfalse;
01828 }
01829
01830 if ( ent1->client->ps.torsoAnim == BOTH_A1__R__L ||
01831 ent1->client->ps.torsoAnim == BOTH_A2__R__L ||
01832 ent1->client->ps.torsoAnim == BOTH_A3__R__L ||
01833 ent1->client->ps.torsoAnim == BOTH_A4__R__L ||
01834 ent1->client->ps.torsoAnim == BOTH_A5__R__L ||
01835 ent1->client->ps.torsoAnim == BOTH_A6__R__L ||
01836 ent1->client->ps.torsoAnim == BOTH_A7__R__L)
01837 {
01838 if ( ent2BlockingPlayer )
01839 {
01840 return WP_SabersCheckLock2( ent1, ent2, LOCK_R );
01841 }
01842 if ( ent2->client->ps.torsoAnim == BOTH_A1_TR_BL ||
01843 ent2->client->ps.torsoAnim == BOTH_A2_TR_BL ||
01844 ent2->client->ps.torsoAnim == BOTH_A3_TR_BL ||
01845 ent2->client->ps.torsoAnim == BOTH_A4_TR_BL ||
01846 ent2->client->ps.torsoAnim == BOTH_A5_TR_BL ||
01847 ent2->client->ps.torsoAnim == BOTH_A6_TR_BL ||
01848 ent2->client->ps.torsoAnim == BOTH_A7_TR_BL ||
01849 ent2->client->ps.torsoAnim == BOTH_P1_S1_TL ||
01850 ent2->client->ps.torsoAnim == BOTH_P1_S1_BR )
01851 {
01852 return WP_SabersCheckLock2( ent1, ent2, LOCK_R );
01853 }
01854 return qfalse;
01855 }
01856 if ( ent2->client->ps.torsoAnim == BOTH_A1__R__L ||
01857 ent2->client->ps.torsoAnim == BOTH_A2__R__L ||
01858 ent2->client->ps.torsoAnim == BOTH_A3__R__L ||
01859 ent2->client->ps.torsoAnim == BOTH_A4__R__L ||
01860 ent2->client->ps.torsoAnim == BOTH_A5__R__L ||
01861 ent2->client->ps.torsoAnim == BOTH_A6__R__L ||
01862 ent2->client->ps.torsoAnim == BOTH_A7__R__L)
01863 {
01864 if ( ent1BlockingPlayer )
01865 {
01866 return WP_SabersCheckLock2( ent2, ent1, LOCK_R );
01867 }
01868 if ( ent1->client->ps.torsoAnim == BOTH_A1_TR_BL ||
01869 ent1->client->ps.torsoAnim == BOTH_A2_TR_BL ||
01870 ent1->client->ps.torsoAnim == BOTH_A3_TR_BL ||
01871 ent1->client->ps.torsoAnim == BOTH_A4_TR_BL ||
01872 ent1->client->ps.torsoAnim == BOTH_A5_TR_BL ||
01873 ent1->client->ps.torsoAnim == BOTH_A6_TR_BL ||
01874 ent1->client->ps.torsoAnim == BOTH_A7_TR_BL ||
01875 ent1->client->ps.torsoAnim == BOTH_P1_S1_TL ||
01876 ent1->client->ps.torsoAnim == BOTH_P1_S1_BR )
01877 {
01878 return WP_SabersCheckLock2( ent2, ent1, LOCK_R );
01879 }
01880 return qfalse;
01881 }
01882 if ( !Q_irand( 0, 10 ) )
01883 {
01884 return WP_SabersCheckLock2( ent1, ent2, LOCK_RANDOM );
01885 }
01886 return qfalse;
01887 }
01888
01889 static GAME_INLINE int G_GetParryForBlock(int block)
01890 {
01891 switch (block)
01892 {
01893 case BLOCKED_UPPER_RIGHT:
01894 return LS_PARRY_UR;
01895 break;
01896 case BLOCKED_UPPER_RIGHT_PROJ:
01897 return LS_REFLECT_UR;
01898 break;
01899 case BLOCKED_UPPER_LEFT:
01900 return LS_PARRY_UL;
01901 break;
01902 case BLOCKED_UPPER_LEFT_PROJ:
01903 return LS_REFLECT_UL;
01904 break;
01905 case BLOCKED_LOWER_RIGHT:
01906 return LS_PARRY_LR;
01907 break;
01908 case BLOCKED_LOWER_RIGHT_PROJ:
01909 return LS_REFLECT_LR;
01910 break;
01911 case BLOCKED_LOWER_LEFT:
01912 return LS_PARRY_LL;
01913 break;
01914 case BLOCKED_LOWER_LEFT_PROJ:
01915 return LS_REFLECT_LL;
01916 break;
01917 case BLOCKED_TOP:
01918 return LS_PARRY_UP;
01919 break;
01920 case BLOCKED_TOP_PROJ:
01921 return LS_REFLECT_UP;
01922 break;
01923 default:
01924 break;
01925 }
01926
01927 return LS_NONE;
01928 }
01929
01930 #include "../namespace_begin.h"
01931 int PM_SaberBounceForAttack( int move );
01932 int PM_SaberDeflectionForQuad( int quad );
01933 #include "../namespace_end.h"
01934
01935 extern stringID_table_t animTable[MAX_ANIMATIONS+1];
01936 static GAME_INLINE qboolean WP_GetSaberDeflectionAngle( gentity_t *attacker, gentity_t *defender, float saberHitFraction )
01937 {
01938 qboolean animBasedDeflection = qtrue;
01939 int attSaberLevel, defSaberLevel;
01940
01941 if ( !attacker || !attacker->client || !attacker->ghoul2 )
01942 {
01943 return qfalse;
01944 }
01945 if ( !defender || !defender->client || !defender->ghoul2 )
01946 {
01947 return qfalse;
01948 }
01949
01950 if ((level.time - attacker->client->lastSaberStorageTime) > 500)
01951 {
01952 return qfalse;
01953 }
01954 if ((level.time - defender->client->lastSaberStorageTime) > 500)
01955 {
01956 return qfalse;
01957 }
01958
01959 attSaberLevel = G_SaberAttackPower(attacker, SaberAttacking(attacker));
01960 defSaberLevel = G_SaberAttackPower(defender, SaberAttacking(defender));
01961
01962 if ( animBasedDeflection )
01963 {
01964
01965 int attQuadStart = saberMoveData[attacker->client->ps.saberMove].startQuad;
01966 int attQuadEnd = saberMoveData[attacker->client->ps.saberMove].endQuad;
01967 int defQuad = saberMoveData[defender->client->ps.saberMove].endQuad;
01968 int quadDiff = fabs((float)(defQuad-attQuadStart));
01969
01970 if ( defender->client->ps.saberMove == LS_READY )
01971 {
01972
01973
01974
01975
01976
01977
01978
01979 return qfalse;
01980 }
01981
01982
01983 switch ( defQuad )
01984 {
01985 case Q_BR:
01986 defQuad = Q_BL;
01987 break;
01988 case Q_R:
01989 defQuad = Q_L;
01990 break;
01991 case Q_TR:
01992 defQuad = Q_TL;
01993 break;
01994 case Q_TL:
01995 defQuad = Q_TR;
01996 break;
01997 case Q_L:
01998 defQuad = Q_R;
01999 break;
02000 case Q_BL:
02001 defQuad = Q_BR;
02002 break;
02003 }
02004
02005 if ( quadDiff > 4 )
02006 {
02007 quadDiff = 4 - (quadDiff - 4);
02008 }
02009
02010 if ( (!quadDiff || (quadDiff == 1 && Q_irand(0,1)))
02011 && (defSaberLevel == attSaberLevel || Q_irand( 0, defSaberLevel-attSaberLevel ) >= 0) )
02012 {
02013
02014 #ifndef FINAL_BUILD
02015 int attMove = attacker->client->ps.saberMove;
02016 #endif
02017 attacker->client->ps.saberMove = PM_SaberBounceForAttack( attacker->client->ps.saberMove );
02018 #ifndef FINAL_BUILD
02019 if (g_saberDebugPrint.integer)
02020 {
02021 Com_Printf( "attack %s vs. parry %s bounced to %s\n",
02022 animTable[saberMoveData[attMove].animToUse].name,
02023 animTable[saberMoveData[defender->client->ps.saberMove].animToUse].name,
02024 animTable[saberMoveData[attacker->client->ps.saberMove].animToUse].name );
02025 }
02026 #endif
02027 attacker->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
02028 return qfalse;
02029 }
02030 else
02031 {
02032 int newQuad;
02033 quadDiff = defQuad - attQuadEnd;
02034
02035 if ( quadDiff > 4 )
02036 {
02037 quadDiff = 4 - (quadDiff - 4);
02038 }
02039 else if ( quadDiff < -4 )
02040 {
02041 quadDiff = -4 + (quadDiff + 4);
02042 }
02043 newQuad = attQuadEnd + ceil( ((float)quadDiff)/2.0f );
02044 if ( newQuad < Q_BR )
02045 {
02046 newQuad = Q_B + newQuad;
02047 }
02048 if ( newQuad == attQuadStart )
02049 {
02050 if ( Q_irand(0, 1) )
02051 {
02052 newQuad--;
02053 }
02054 else
02055 {
02056 newQuad++;
02057 }
02058 if ( newQuad < Q_BR )
02059 {
02060 newQuad = Q_B;
02061 }
02062 else if ( newQuad > Q_B )
02063 {
02064 newQuad = Q_BR;
02065 }
02066 }
02067 if ( newQuad == defQuad )
02068 {
02069 #ifndef FINAL_BUILD
02070 int attMove = attacker->client->ps.saberMove;
02071 #endif
02072 attacker->client->ps.saberMove = PM_SaberBounceForAttack( attacker->client->ps.saberMove );
02073 #ifndef FINAL_BUILD
02074 if (g_saberDebugPrint.integer)
02075 {
02076 Com_Printf( "attack %s vs. parry %s bounced to %s\n",
02077 animTable[saberMoveData[attMove].animToUse].name,
02078 animTable[saberMoveData[defender->client->ps.saberMove].animToUse].name,
02079 animTable[saberMoveData[attacker->client->ps.saberMove].animToUse].name );
02080 }
02081 #endif
02082 attacker->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
02083 return qfalse;
02084 }
02085
02086 else
02087 {
02088 #ifndef FINAL_BUILD
02089 int attMove = attacker->client->ps.saberMove;
02090 #endif
02091 attacker->client->ps.saberMove = PM_SaberDeflectionForQuad( newQuad );
02092 #ifndef FINAL_BUILD
02093 if (g_saberDebugPrint.integer)
02094 {
02095 Com_Printf( "attack %s vs. parry %s deflected to %s\n",
02096 animTable[saberMoveData[attMove].animToUse].name,
02097 animTable[saberMoveData[defender->client->ps.saberMove].animToUse].name,
02098 animTable[saberMoveData[attacker->client->ps.saberMove].animToUse].name );
02099 }
02100 #endif
02101 attacker->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
02102 return qtrue;
02103 }
02104 }
02105 }
02106 else
02107 {
02108 vec3_t att_HitDir, def_BladeDir, temp;
02109 float hitDot;
02110
02111 VectorCopy(attacker->client->lastSaberBase_Always, temp);
02112
02113 AngleVectors(attacker->client->lastSaberDir_Always, att_HitDir, 0, 0);
02114
02115 AngleVectors(defender->client->lastSaberDir_Always, def_BladeDir, 0, 0);
02116
02117
02118 hitDot = DotProduct( att_HitDir, def_BladeDir );
02119 if ( hitDot < 0.25f && hitDot > -0.25f )
02120 {
02121 attacker->client->ps.saberMove = PM_SaberBounceForAttack( attacker->client->ps.saberMove );
02122 attacker->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
02123 return qfalse;
02124 }
02125 else
02126 {
02127 vec3_t att_Right, att_Up, att_DeflectionDir;
02128 float swingRDot, swingUDot;
02129
02130
02131 VectorScale( def_BladeDir, hitDot, att_DeflectionDir );
02132
02133 VectorScale( att_HitDir, -1.0f, temp );
02134
02135 VectorAdd( att_DeflectionDir, temp, att_DeflectionDir );
02136
02137 VectorNormalize( att_DeflectionDir );
02138
02139
02140 VectorSet( temp, 0, attacker->client->ps.viewangles[YAW], 0 );
02141 AngleVectors( temp, NULL, att_Right, att_Up );
02142 swingRDot = DotProduct( att_Right, att_DeflectionDir );
02143 swingUDot = DotProduct( att_Up, att_DeflectionDir );
02144
02145 if ( swingRDot > 0.25f )
02146 {
02147 if ( swingUDot > 0.25f )
02148 {
02149 attacker->client->ps.saberMove = LS_D1_TR;
02150 }
02151 else if ( swingUDot < -0.25f )
02152 {
02153 attacker->client->ps.saberMove = LS_D1_BR;
02154 }
02155 else
02156 {
02157 attacker->client->ps.saberMove = LS_D1__R;
02158 }
02159 }
02160 else if ( swingRDot < -0.25f )
02161 {
02162 if ( swingUDot > 0.25f )
02163 {
02164 attacker->client->ps.saberMove = LS_D1_TL;
02165 }
02166 else if ( swingUDot < -0.25f )
02167 {
02168 attacker->client->ps.saberMove = LS_D1_BL;
02169 }
02170 else
02171 {
02172 attacker->client->ps.saberMove = LS_D1__L;
02173 }
02174 }
02175 else
02176 {
02177 if ( swingUDot > 0.25f )
02178 {
02179 attacker->client->ps.saberMove = LS_D1_T_;
02180 }
02181 else if ( swingUDot < -0.25f )
02182 {
02183 attacker->client->ps.saberMove = LS_D1_B_;
02184 }
02185 else
02186 {
02187 if ( swingRDot > 0 )
02188 {
02189 attacker->client->ps.saberMove = LS_D1_TR;
02190 }
02191 else if ( swingRDot < 0 )
02192 {
02193 attacker->client->ps.saberMove = LS_D1_TL;
02194 }
02195 else
02196 {
02197 attacker->client->ps.saberMove = LS_D1_T_;
02198 }
02199 }
02200 }
02201
02202 attacker->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
02203 return qtrue;
02204 }
02205 }
02206 }
02207
02208 int G_KnockawayForParry( int move )
02209 {
02210
02211
02212 switch ( move )
02213 {
02214 case LS_PARRY_UP:
02215 return LS_K1_T_;
02216 break;
02217 case LS_PARRY_UR:
02218 default:
02219 return LS_K1_TR;
02220 break;
02221 case LS_PARRY_UL:
02222 return LS_K1_TL;
02223 break;
02224 case LS_PARRY_LR:
02225 return LS_K1_BR;
02226 break;
02227 case LS_PARRY_LL:
02228 return LS_K1_BL;
02229 break;
02230 }
02231 }
02232
02233 #define SABER_NONATTACK_DAMAGE 1
02234
02235
02236 static GAME_INLINE int G_GetAttackDamage(gentity_t *self, int minDmg, int maxDmg, float multPoint)
02237 {
02238 int peakDif = 0;
02239 int speedDif = 0;
02240 int totalDamage = maxDmg;
02241 float peakPoint = 0;
02242 float attackAnimLength = bgAllAnims[self->localAnimIndex].anims[self->client->ps.torsoAnim].numFrames * fabs((float)(bgAllAnims[self->localAnimIndex].anims[self->client->ps.torsoAnim].frameLerp));
02243 float currentPoint = 0;
02244 float damageFactor = 0;
02245 float animSpeedFactor = 1.0f;
02246
02247
02248 BG_SaberStartTransAnim(self->s.number, self->client->ps.fd.saberAnimLevel, self->client->ps.weapon, self->client->ps.torsoAnim, &animSpeedFactor, self->client->ps.brokenLimbs);
02249 speedDif = attackAnimLength - (attackAnimLength * animSpeedFactor);
02250 attackAnimLength += speedDif;
02251 peakPoint = attackAnimLength;
02252 peakPoint -= attackAnimLength*multPoint;
02253
02254
02255 currentPoint = self->client->ps.torsoTimer;
02256
02257 if (peakPoint > currentPoint)
02258 {
02259 peakDif = (peakPoint - currentPoint);
02260 }
02261 else
02262 {
02263 peakDif = (currentPoint - peakPoint);
02264 }
02265
02266 damageFactor = (float)((currentPoint/peakPoint));
02267 if (damageFactor > 1)
02268 {
02269 damageFactor = (2.0f - damageFactor);
02270 }
02271
02272 totalDamage *= damageFactor;
02273 if (totalDamage < minDmg)
02274 {
02275 totalDamage = minDmg;
02276 }
02277 if (totalDamage > maxDmg)
02278 {
02279 totalDamage = maxDmg;
02280 }
02281
02282
02283
02284 return totalDamage;
02285 }
02286
02287
02288 static GAME_INLINE float G_GetAnimPoint(gentity_t *self)
02289 {
02290 int speedDif = 0;
02291 float attackAnimLength = bgAllAnims[self->localAnimIndex].anims[self->client->ps.torsoAnim].numFrames * fabs((float)(bgAllAnims[self->localAnimIndex].anims[self->client->ps.torsoAnim].frameLerp));
02292 float currentPoint = 0;
02293 float animSpeedFactor = 1.0f;
02294 float animPercentage = 0;
02295
02296
02297 BG_SaberStartTransAnim(self->s.number, self->client->ps.fd.saberAnimLevel, self->client->ps.weapon, self->client->ps.torsoAnim, &animSpeedFactor, self->client->ps.brokenLimbs);
02298 speedDif = attackAnimLength - (attackAnimLength * animSpeedFactor);
02299 attackAnimLength += speedDif;
02300
02301 currentPoint = self->client->ps.torsoTimer;
02302
02303 animPercentage = currentPoint/attackAnimLength;
02304
02305
02306
02307 return animPercentage;
02308 }
02309
02310 static GAME_INLINE qboolean G_ClientIdleInWorld(gentity_t *ent)
02311 {
02312 if (ent->s.eType == ET_NPC)
02313 {
02314 return qfalse;
02315 }
02316
02317 if (!ent->client->pers.cmd.upmove &&
02318 !ent->client->pers.cmd.forwardmove &&
02319 !ent->client->pers.cmd.rightmove &&
02320 !(ent->client->pers.cmd.buttons & BUTTON_GESTURE) &&
02321 !(ent->client->pers.cmd.buttons & BUTTON_FORCEGRIP) &&
02322 !(ent->client->pers.cmd.buttons & BUTTON_ALT_ATTACK) &&
02323 !(ent->client->pers.cmd.buttons & BUTTON_FORCEPOWER) &&
02324 !(ent->client->pers.cmd.buttons & BUTTON_FORCE_LIGHTNING) &&
02325 !(ent->client->pers.cmd.buttons & BUTTON_FORCE_DRAIN) &&
02326 !(ent->client->pers.cmd.buttons & BUTTON_ATTACK))
02327 {
02328 return qtrue;
02329 }
02330
02331 return qfalse;
02332 }
02333
02334 static GAME_INLINE qboolean G_G2TraceCollide(trace_t *tr, vec3_t lastValidStart, vec3_t lastValidEnd, vec3_t traceMins, vec3_t traceMaxs)
02335 {
02336 G2Trace_t G2Trace;
02337 gentity_t *g2Hit;
02338 vec3_t angles;
02339 int tN = 0;
02340 float fRadius = 0;
02341
02342 if (!d_saberGhoul2Collision.integer)
02343 {
02344 return qfalse;
02345 }
02346
02347 if (!g_entities[tr->entityNum].inuse
02348 )
02349 {
02350 return qfalse;
02351 }
02352
02353 if (traceMins[0] ||
02354 traceMins[1] ||
02355 traceMins[2] ||
02356 traceMaxs[0] ||
02357 traceMaxs[1] ||
02358 traceMaxs[2])
02359 {
02360 fRadius=(traceMaxs[0]-traceMins[0])/2.0f;
02361 }
02362
02363 memset (&G2Trace, 0, sizeof(G2Trace));
02364
02365 while (tN < MAX_G2_COLLISIONS)
02366 {
02367 G2Trace[tN].mEntityNum = -1;
02368 tN++;
02369 }
02370 g2Hit = &g_entities[tr->entityNum];
02371
02372 if (g2Hit && g2Hit->inuse && g2Hit->ghoul2)
02373 {
02374 vec3_t g2HitOrigin;
02375
02376 angles[ROLL] = angles[PITCH] = 0;
02377
02378 if (g2Hit->client)
02379 {
02380 VectorCopy(g2Hit->client->ps.origin, g2HitOrigin);
02381 angles[YAW] = g2Hit->client->ps.viewangles[YAW];
02382 }
02383 else
02384 {
02385 VectorCopy(g2Hit->r.currentOrigin, g2HitOrigin);
02386 angles[YAW] = g2Hit->r.currentAngles[YAW];
02387 }
02388
02389 if (g_optvehtrace.integer &&
02390 g2Hit->s.eType == ET_NPC &&
02391 g2Hit->s.NPC_class == CLASS_VEHICLE &&
02392 g2Hit->m_pVehicle)
02393 {
02394 trap_G2API_CollisionDetectCache ( G2Trace, g2Hit->ghoul2, angles, g2HitOrigin, level.time, g2Hit->s.number, lastValidStart, lastValidEnd, g2Hit->modelScale, 0, g_g2TraceLod.integer, fRadius );
02395 }
02396 else
02397 {
02398 trap_G2API_CollisionDetect ( G2Trace, g2Hit->ghoul2, angles, g2HitOrigin, level.time, g2Hit->s.number, lastValidStart, lastValidEnd, g2Hit->modelScale, 0, g_g2TraceLod.integer, fRadius );
02399 }
02400
02401 if (G2Trace[0].mEntityNum != g2Hit->s.number)
02402 {
02403 tr->fraction = 1.0f;
02404 tr->entityNum = ENTITYNUM_NONE;
02405 tr->startsolid = 0;
02406 tr->allsolid = 0;
02407 return qfalse;
02408 }
02409 else
02410 {
02411 VectorCopy(G2Trace[0].mCollisionPosition, tr->endpos);
02412 VectorCopy(G2Trace[0].mCollisionNormal, tr->plane.normal);
02413
02414 if (g2Hit->client)
02415 {
02416 g2Hit->client->g2LastSurfaceHit = G2Trace[0].mSurfaceIndex;
02417 g2Hit->client->g2LastSurfaceTime = level.time;
02418 }
02419 return qtrue;
02420 }
02421 }
02422
02423 return qfalse;
02424 }
02425
02426 static GAME_INLINE qboolean G_SaberInBackAttack(int move)
02427 {
02428 switch (move)
02429 {
02430 case LS_A_BACK:
02431 case LS_A_BACK_CR:
02432 case LS_A_BACKSTAB:
02433 return qtrue;
02434 }
02435
02436 return qfalse;
02437 }
02438
02439 qboolean saberCheckKnockdown_Thrown(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other);
02440 qboolean saberCheckKnockdown_Smashed(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other, int damage);
02441 qboolean saberCheckKnockdown_BrokenParry(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other);
02442
02443
02444 typedef struct saberFace_s
02445 {
02446 vec3_t v1;
02447 vec3_t v2;
02448 vec3_t v3;
02449 } saberFace_t;
02450
02451
02452 static GAME_INLINE void G_BuildSaberFaces(vec3_t base, vec3_t tip, float radius, vec3_t fwd,
02453 vec3_t right, int *fNum, saberFace_t **fList)
02454 {
02455 static saberFace_t faces[12];
02456 int i = 0;
02457 float *d1 = NULL, *d2 = NULL;
02458 vec3_t invFwd;
02459 vec3_t invRight;
02460
02461 VectorCopy(fwd, invFwd);
02462 VectorInverse(invFwd);
02463 VectorCopy(right, invRight);
02464 VectorInverse(invRight);
02465
02466 while (i < 8)
02467 {
02468
02469 if (i < 2)
02470 {
02471 d1 = &fwd[0];
02472 d2 = &invRight[0];
02473 }
02474 else if (i < 4)
02475 {
02476 d1 = &fwd[0];
02477 d2 = &right[0];
02478 }
02479 else if (i < 6)
02480 {
02481 d1 = &right[0];
02482 d2 = &fwd[0];
02483 }
02484 else if (i < 8)
02485 {
02486 d1 = &right[0];
02487 d2 = &invFwd[0];
02488 }
02489
02490
02491 VectorMA(base, radius/2.0f, d1, faces[i].v1);
02492 VectorMA(faces[i].v1, radius/2.0f, d2, faces[i].v1);
02493
02494 VectorMA(tip, radius/2.0f, d1, faces[i].v2);
02495 VectorMA(faces[i].v2, radius/2.0f, d2, faces[i].v2);
02496
02497 VectorMA(tip, -radius/2.0f, d1, faces[i].v3);
02498 VectorMA(faces[i].v3, radius/2.0f, d2, faces[i].v3);
02499
02500 i++;
02501
02502
02503 VectorMA(tip, -radius/2.0f, d1, faces[i].v1);
02504 VectorMA(faces[i].v1, radius/2.0f, d2, faces[i].v1);
02505
02506 VectorMA(base, radius/2.0f, d1, faces[i].v2);
02507 VectorMA(faces[i].v2, radius/2.0f, d2, faces[i].v2);
02508
02509 VectorMA(base, -radius/2.0f, d1, faces[i].v3);
02510 VectorMA(faces[i].v3, radius/2.0f, d2, faces[i].v3);
02511
02512 i++;
02513 }
02514
02515
02516
02517 VectorMA(tip, radius/2.0f, fwd, faces[i].v1);
02518 VectorMA(faces[i].v1, -radius/2.0f, right, faces[i].v1);
02519
02520 VectorMA(tip, radius/2.0f, fwd, faces[i].v2);
02521 VectorMA(faces[i].v2, radius/2.0f, right, faces[i].v2);
02522
02523 VectorMA(tip, -radius/2.0f, fwd, faces[i].v3);
02524 VectorMA(faces[i].v3, -radius/2.0f, right, faces[i].v3);
02525
02526 i++;
02527
02528
02529 VectorMA(tip, radius/2.0f, fwd, faces[i].v1);
02530 VectorMA(faces[i].v1, radius/2.0f, right, faces[i].v1);
02531
02532 VectorMA(tip, -radius/2.0f, fwd, faces[i].v2);
02533 VectorMA(faces[i].v2, -radius/2.0f, right, faces[i].v2);
02534
02535 VectorMA(tip, -radius/2.0f, fwd, faces[i].v3);
02536 VectorMA(faces[i].v3, radius/2.0f, right, faces[i].v3);
02537
02538 i++;
02539
02540
02541
02542 VectorMA(base, radius/2.0f, fwd, faces[i].v1);
02543 VectorMA(faces[i].v1, -radius/2.0f, right, faces[i].v1);
02544
02545 VectorMA(base, radius/2.0f, fwd, faces[i].v2);
02546 VectorMA(faces[i].v2, radius/2.0f, right, faces[i].v2);
02547
02548 VectorMA(base, -radius/2.0f, fwd, faces[i].v3);
02549 VectorMA(faces[i].v3, -radius/2.0f, right, faces[i].v3);
02550
02551 i++;
02552
02553
02554 VectorMA(base, radius/2.0f, fwd, faces[i].v1);
02555 VectorMA(faces[i].v1, radius/2.0f, right, faces[i].v1);
02556
02557 VectorMA(base, -radius/2.0f, fwd, faces[i].v2);
02558 VectorMA(faces[i].v2, -radius/2.0f, right, faces[i].v2);
02559
02560 VectorMA(base, -radius/2.0f, fwd, faces[i].v3);
02561 VectorMA(faces[i].v3, radius/2.0f, right, faces[i].v3);
02562
02563 i++;
02564
02565
02566 *fNum = i;
02567 *fList = &faces[0];
02568 }
02569
02570
02571 static GAME_INLINE void G_SabCol_CalcPlaneEq(vec3_t x, vec3_t y, vec3_t z, float *planeEq)
02572 {
02573 planeEq[0] = x[1]*(y[2]-z[2]) + y[1]*(z[2]-x[2]) + z[1]*(x[2]-y[2]);
02574 planeEq[1] = x[2]*(y[0]-z[0]) + y[2]*(z[0]-x[0]) + z[2]*(x[0]-y[0]);
02575 planeEq[2] = x[0]*(y[1]-z[1]) + y[0]*(z[1]-x[1]) + z[0]*(x[1]-y[1]);
02576 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]) );
02577 }
02578
02579
02580 static GAME_INLINE int G_SabCol_PointRelativeToPlane(vec3_t pos, float *side, float *planeEq)
02581 {
02582 *side = planeEq[0]*pos[0] + planeEq[1]*pos[1] + planeEq[2]*pos[2] + planeEq[3];
02583
02584 if (*side > 0.0f)
02585 {
02586 return 1;
02587 }
02588 else if (*side < 0.0f)
02589 {
02590 return -1;
02591 }
02592
02593 return 0;
02594 }
02595
02596
02597 static GAME_INLINE qboolean G_SaberFaceCollisionCheck(int fNum, saberFace_t *fList, vec3_t atkStart,
02598 vec3_t atkEnd, vec3_t atkMins, vec3_t atkMaxs, vec3_t impactPoint)
02599 {
02600 static float planeEq[4];
02601 static float side, side2, dist;
02602 static vec3_t dir;
02603 static vec3_t point;
02604 int i = 0;
02605
02606 if (VectorCompare(atkMins, vec3_origin) && VectorCompare(atkMaxs, vec3_origin))
02607 {
02608 VectorSet(atkMins, -1.0f, -1.0f, -1.0f);
02609 VectorSet(atkMaxs, 1.0f, 1.0f, 1.0f);
02610 }
02611
02612 VectorSubtract(atkEnd, atkStart, dir);
02613
02614 while (i < fNum)
02615 {
02616 G_SabCol_CalcPlaneEq(fList->v1, fList->v2, fList->v3, planeEq);
02617
02618 if (G_SabCol_PointRelativeToPlane(atkStart, &side, planeEq) !=
02619 G_SabCol_PointRelativeToPlane(atkEnd, &side2, planeEq))
02620 {
02621 static vec3_t extruded;
02622 static vec3_t minPoint, maxPoint;
02623 static vec3_t planeNormal;
02624 static int facing;
02625
02626 VectorCopy(&planeEq[0], planeNormal);
02627 side2 = planeNormal[0]*dir[0] + planeNormal[1]*dir[1] + planeNormal[2]*dir[2];
02628
02629 dist = side/side2;
02630 VectorMA(atkStart, -dist, dir, point);
02631
02632 VectorAdd(point, atkMins, minPoint);
02633 VectorAdd(point, atkMaxs, maxPoint);
02634
02635
02636
02637 VectorMA(fList->v1, -2.0f, planeNormal, extruded);
02638 G_SabCol_CalcPlaneEq(fList->v1, fList->v2, extruded, planeEq);
02639 facing = G_SabCol_PointRelativeToPlane(point, &side, planeEq);
02640
02641 if (facing < 0)
02642 {
02643 facing = G_SabCol_PointRelativeToPlane(minPoint, &side, planeEq);
02644 if (facing < 0)
02645 {
02646 facing = G_SabCol_PointRelativeToPlane(maxPoint, &side, planeEq);
02647 }
02648 }
02649
02650 if (facing >= 0)
02651 {
02652 VectorMA(fList->v2, -2.0f, planeNormal, extruded);
02653 G_SabCol_CalcPlaneEq(fList->v2, fList->v3, extruded, planeEq);
02654 facing = G_SabCol_PointRelativeToPlane(point, &side, planeEq);
02655
02656 if (facing < 0)
02657 {
02658 facing = G_SabCol_PointRelativeToPlane(minPoint, &side, planeEq);
02659 if (facing < 0)
02660 {
02661 facing = G_SabCol_PointRelativeToPlane(maxPoint, &side, planeEq);
02662 }
02663 }
02664
02665 if (facing >= 0)
02666 {
02667 VectorMA(fList->v3, -2.0f, planeNormal, extruded);
02668 G_SabCol_CalcPlaneEq(fList->v3, fList->v1, extruded, planeEq);
02669 facing = G_SabCol_PointRelativeToPlane(point, &side, planeEq);
02670
02671 if (facing < 0)
02672 {
02673 facing = G_SabCol_PointRelativeToPlane(minPoint, &side, planeEq);
02674 if (facing < 0)
02675 {
02676 facing = G_SabCol_PointRelativeToPlane(maxPoint, &side, planeEq);
02677 }
02678 }
02679
02680 if (facing >= 0)
02681 {
02682 VectorCopy(point, impactPoint);
02683 return qtrue;
02684 }
02685 }
02686 }
02687 }
02688
02689 i++;
02690 fList++;
02691 }
02692
02693
02694 return qfalse;
02695 }
02696
02697
02698 static GAME_INLINE qboolean G_SaberCollide(gentity_t *atk, gentity_t *def, vec3_t atkStart,
02699 vec3_t atkEnd, vec3_t atkMins, vec3_t atkMaxs, vec3_t impactPoint)
02700 {
02701 static int i, j;
02702
02703 if (!g_saberBladeFaces.integer)
02704 {
02705 return qtrue;
02706 }
02707
02708 if (!atk->inuse || !atk->client || !def->inuse || !def->client)
02709 {
02710 return qfalse;
02711 }
02712
02713 i = 0;
02714 while (i < MAX_SABERS)
02715 {
02716 j = 0;
02717 if (def->client->saber[i].model && def->client->saber[i].model[0])
02718 {
02719 bladeInfo_t *blade;
02720 vec3_t v, fwd, right, base, tip;
02721 int fNum;
02722 saberFace_t *fList;
02723
02724
02725 while (j < def->client->saber[i].numBlades)
02726 {
02727 blade = &def->client->saber[i].blade[j];
02728
02729 if ((level.time-blade->storageTime) < 200)
02730 {
02731
02732 VectorCopy(blade->muzzlePoint, base);
02733 VectorMA(base, blade->lengthMax, blade->muzzleDir, tip);
02734
02735
02736 VectorSubtract(tip, base, v);
02737 vectoangles(v, v);
02738 AngleVectors(v, NULL, right, fwd);
02739
02740
02741 G_BuildSaberFaces(base, tip, blade->radius*3.0f, fwd, right, &fNum, &fList);
02742 if (fNum > 0)
02743 {
02744 #if 0
02745 if (atk->s.number == 0)
02746 {
02747 int x = 0;
02748 saberFace_t *l = fList;
02749 while (x < fNum)
02750 {
02751 G_TestLine(fList->v1, fList->v2, 0x0000ff, 100);
02752 G_TestLine(fList->v2, fList->v3, 0x0000ff, 100);
02753 G_TestLine(fList->v3, fList->v1, 0x0000ff, 100);
02754
02755 fList++;
02756 x++;
02757 }
02758 fList = l;
02759 }
02760 #endif
02761
02762 if (G_SaberFaceCollisionCheck(fNum, fList, atkStart, atkEnd, atkMins, atkMaxs, impactPoint))
02763 {
02764 return qtrue;
02765 }
02766 }
02767 }
02768 j++;
02769 }
02770 }
02771 i++;
02772 }
02773
02774 return qfalse;
02775 }
02776
02777 float WP_SaberBladeLength( saberInfo_t *saber )
02778 {
02779 int i;
02780 float len = 0.0f;
02781 for ( i = 0; i < saber->numBlades; i++ )
02782 {
02783 if ( saber->blade[i].lengthMax > len )
02784 {
02785 len = saber->blade[i].lengthMax;
02786 }
02787 }
02788 return len;
02789 }
02790
02791 float WP_SaberLength( gentity_t *ent )
02792 {
02793 if ( !ent || !ent->client )
02794 {
02795 return 0.0f;
02796 }
02797 else
02798 {
02799 int i;
02800 float len, bestLen = 0.0f;
02801 for ( i = 0; i < MAX_SABERS; i++ )
02802 {
02803 len = WP_SaberBladeLength( &ent->client->saber[i] );
02804 if ( len > bestLen )
02805 {
02806 bestLen = len;
02807 }
02808 }
02809 return bestLen;
02810 }
02811 }
02812 int WPDEBUG_SaberColor( saber_colors_t saberColor )
02813 {
02814 switch( (int)(saberColor) )
02815 {
02816 case SABER_RED:
02817 return 0x000000ff;
02818 break;
02819 case SABER_ORANGE:
02820 return 0x000088ff;
02821 break;
02822 case SABER_YELLOW:
02823 return 0x0000ffff;
02824 break;
02825 case SABER_GREEN:
02826 return 0x0000ff00;
02827 break;
02828 case SABER_BLUE:
02829 return 0x00ff0000;
02830 break;
02831 case SABER_PURPLE:
02832 return 0x00ff00ff;
02833 break;
02834 default:
02835 return 0x00ffffff;
02836 break;
02837 }
02838 }
02839
02840
02841
02842
02843
02844
02845
02846
02847 extern qboolean tri_tri_intersect(vec3_t V0,vec3_t V1,vec3_t V2,vec3_t U0,vec3_t U1,vec3_t U2);
02848 #define SABER_EXTRAPOLATE_DIST 16.0f
02849 qboolean WP_SabersIntersect( gentity_t *ent1, int ent1SaberNum, int ent1BladeNum, gentity_t *ent2, qboolean checkDir )
02850 {
02851 vec3_t saberBase1, saberTip1, saberBaseNext1, saberTipNext1;
02852 vec3_t saberBase2, saberTip2, saberBaseNext2, saberTipNext2;
02853 int ent2SaberNum = 0, ent2BladeNum = 0;
02854 vec3_t dir;
02855
02856 if ( !ent1 || !ent2 )
02857 {
02858 return qfalse;
02859 }
02860 if ( !ent1->client || !ent2->client )
02861 {
02862 return qfalse;
02863 }
02864 if ( BG_SabersOff( &ent1->client->ps )
02865 || BG_SabersOff( &ent2->client->ps ) )
02866 {
02867 return qfalse;
02868 }
02869
02870 for ( ent2SaberNum = 0; ent2SaberNum < MAX_SABERS; ent2SaberNum++ )
02871 {
02872 if ( ent2->client->saber[ent2SaberNum].type != SABER_NONE )
02873 {
02874 for ( ent2BladeNum = 0; ent2BladeNum < ent2->client->saber[ent2SaberNum].numBlades; ent2BladeNum++ )
02875 {
02876 if ( ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].lengthMax > 0 )
02877 {
02878
02879 {
02880 VectorCopy( ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzlePointOld, saberBase1 );
02881 VectorCopy( ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzlePoint, saberBaseNext1 );
02882
02883 VectorSubtract( ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzlePoint, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzlePointOld, dir );
02884 VectorNormalize( dir );
02885 VectorMA( saberBaseNext1, SABER_EXTRAPOLATE_DIST, dir, saberBaseNext1 );
02886
02887 VectorMA( saberBase1, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].lengthMax+SABER_EXTRAPOLATE_DIST, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzleDirOld, saberTip1 );
02888 VectorMA( saberBaseNext1, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].lengthMax+SABER_EXTRAPOLATE_DIST, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzleDir, saberTipNext1 );
02889
02890 VectorSubtract( saberTipNext1, saberTip1, dir );
02891 VectorNormalize( dir );
02892 VectorMA( saberTipNext1, SABER_EXTRAPOLATE_DIST, dir, saberTipNext1 );
02893 }
02894
02895
02896
02897
02898
02899
02900
02901
02902
02903
02904
02905 {
02906 VectorCopy( ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzlePointOld, saberBase2 );
02907 VectorCopy( ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzlePoint, saberBaseNext2 );
02908
02909 VectorSubtract( ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzlePoint, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzlePointOld, dir );
02910 VectorNormalize( dir );
02911 VectorMA( saberBaseNext2, SABER_EXTRAPOLATE_DIST, dir, saberBaseNext2 );
02912
02913 VectorMA( saberBase2, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].lengthMax+SABER_EXTRAPOLATE_DIST, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzleDirOld, saberTip2 );
02914 VectorMA( saberBaseNext2, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].lengthMax+SABER_EXTRAPOLATE_DIST, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzleDir, saberTipNext2 );
02915
02916 VectorSubtract( saberTipNext2, saberTip2, dir );
02917 VectorNormalize( dir );
02918 VectorMA( saberTipNext2, SABER_EXTRAPOLATE_DIST, dir, saberTipNext2 );
02919 }
02920
02921
02922
02923
02924
02925
02926
02927
02928
02929 if ( checkDir )
02930 {
02931 vec3_t saberDir1, saberDir2;
02932 float dot = 0.0f;
02933
02934 VectorSubtract( saberTipNext1, saberTip1, saberDir1 );
02935 VectorSubtract( saberTipNext2, saberTip2, saberDir2 );
02936 VectorNormalize( saberDir1 );
02937 VectorNormalize( saberDir2 );
02938 if ( DotProduct( saberDir1, saberDir2 ) > 0.6f )
02939 {
02940 continue;
02941 }
02942
02943 dot = DotProduct( ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].muzzleDir, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].muzzleDir );
02944 if ( dot > 0.9f || dot < -0.9f )
02945 {
02946 continue;
02947 }
02948 }
02949
02950 #ifdef DEBUG_SABER_BOX
02951 if ( g_saberDebugBox.integer == 2 || g_saberDebugBox.integer == 4 )
02952 {
02953 G_TestLine(saberBase1, saberTip1, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].color, 500);
02954 G_TestLine(saberTip1, saberTipNext1, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].color, 500);
02955 G_TestLine(saberTipNext1, saberBase1, ent1->client->saber[ent1SaberNum].blade[ent1BladeNum].color, 500);
02956
02957 G_TestLine(saberBase2, saberTip2, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].color, 500);
02958 G_TestLine(saberTip2, saberTipNext2, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].color, 500);
02959 G_TestLine(saberTipNext2, saberBase2, ent2->client->saber[ent2SaberNum].blade[ent2BladeNum].color, 500);
02960 }
02961 #endif
02962 if ( tri_tri_intersect( saberBase1, saberTip1, saberBaseNext1, saberBase2, saberTip2, saberBaseNext2 ) )
02963 {
02964 return qtrue;
02965 }
02966 if ( tri_tri_intersect( saberBase1, saberTip1, saberBaseNext1, saberBase2, saberTip2, saberTipNext2 ) )
02967 {
02968 return qtrue;
02969 }
02970 if ( tri_tri_intersect( saberBase1, saberTip1, saberTipNext1, saberBase2, saberTip2, saberBaseNext2 ) )
02971 {
02972 return qtrue;
02973 }
02974 if ( tri_tri_intersect( saberBase1, saberTip1, saberTipNext1, saberBase2, saberTip2, saberTipNext2 ) )
02975 {
02976 return qtrue;
02977 }
02978 }
02979 }
02980 }
02981 }
02982 return qfalse;
02983 }
02984
02985 static GAME_INLINE int G_PowerLevelForSaberAnim( gentity_t *ent, int saberNum, qboolean mySaberHit )
02986 {
02987 if ( !ent || !ent->client || saberNum >= MAX_SABERS )
02988 {
02989 return FORCE_LEVEL_0;
02990 }
02991 else
02992 {
02993 int anim = ent->client->ps.torsoAnim;
02994 int animTimer = ent->client->ps.torsoTimer;
02995 int animTimeElapsed = BG_AnimLength( ent->localAnimIndex, (animNumber_t)anim ) - animTimer;
02996 saberInfo_t *saber = &ent->client->saber[saberNum];
02997 if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ )
02998 {
02999
03000 if ( saber->type == SABER_LANCE )
03001 {
03002 return FORCE_LEVEL_4;
03003 }
03004 else if ( saber->type == SABER_TRIDENT )
03005 {
03006 return FORCE_LEVEL_3;
03007 }
03008 return FORCE_LEVEL_1;
03009 }
03010 if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ )
03011 {
03012 return FORCE_LEVEL_2;
03013 }
03014 if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ )
03015 {
03016 return FORCE_LEVEL_3;
03017 }
03018 if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ )
03019 {
03020 return FORCE_LEVEL_4;
03021 }
03022 if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ )
03023 {
03024 return FORCE_LEVEL_2;
03025 }
03026 if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ )
03027 {
03028 return FORCE_LEVEL_2;
03029 }
03030 if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ )
03031 {
03032 return FORCE_LEVEL_2;
03033 }
03034 if ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR )
03035 {
03036 return FORCE_LEVEL_1;
03037 }
03038 switch ( anim )
03039 {
03040 case BOTH_A2_STABBACK1:
03041 if ( mySaberHit )
03042 {
03043 return FORCE_LEVEL_1;
03044 }
03045 if ( animTimer < 450 )
03046 {
03047 return FORCE_LEVEL_0;
03048 }
03049 else if ( animTimeElapsed < 400 )
03050 {
03051 return FORCE_LEVEL_0;
03052 }
03053 return FORCE_LEVEL_3;
03054 break;
03055 case BOTH_ATTACK_BACK:
03056 if ( animTimer < 500 )
03057 {
03058 return FORCE_LEVEL_0;
03059 }
03060 return FORCE_LEVEL_3;
03061 break;
03062 case BOTH_CROUCHATTACKBACK1:
03063 if ( animTimer < 800 )
03064 {
03065 return FORCE_LEVEL_0;
03066 }
03067 return FORCE_LEVEL_3;
03068 break;
03069 case BOTH_BUTTERFLY_LEFT:
03070 case BOTH_BUTTERFLY_RIGHT:
03071 case BOTH_BUTTERFLY_FL1:
03072 case BOTH_BUTTERFLY_FR1:
03073
03074 return FORCE_LEVEL_3;
03075 break;
03076 case BOTH_FJSS_TR_BL:
03077 case BOTH_FJSS_TL_BR:
03078
03079 return FORCE_LEVEL_3;
03080 break;
03081 case BOTH_K1_S1_T_:
03082 case BOTH_K1_S1_TR:
03083 case BOTH_K1_S1_TL:
03084 case BOTH_K1_S1_BL:
03085 case BOTH_K1_S1_B_:
03086 case BOTH_K1_S1_BR:
03087
03088 return FORCE_LEVEL_3;
03089 break;
03090 case BOTH_LUNGE2_B__T_:
03091 if ( mySaberHit )
03092 {
03093 return FORCE_LEVEL_1;
03094 }
03095 if ( animTimer < 400 )
03096 {
03097 return FORCE_LEVEL_0;
03098 }
03099 else if ( animTimeElapsed < 150 )
03100 {
03101 return FORCE_LEVEL_0;
03102 }
03103 return FORCE_LEVEL_3;
03104 break;
03105 case BOTH_FORCELEAP2_T__B_:
03106 if ( animTimer < 400 )
03107 {
03108 return FORCE_LEVEL_0;
03109 }
03110 else if ( animTimeElapsed < 550 )
03111 {
03112 return FORCE_LEVEL_0;
03113 }
03114 return FORCE_LEVEL_3;
03115 break;
03116 case BOTH_VS_ATR_S:
03117 case BOTH_VS_ATL_S:
03118 case BOTH_VT_ATR_S:
03119 case BOTH_VT_ATL_S:
03120 return FORCE_LEVEL_3;
03121 break;
03122 case BOTH_JUMPFLIPSLASHDOWN1:
03123 if ( animTimer <= 1000 )
03124 {
03125 return FORCE_LEVEL_0;
03126 }
03127 else if ( animTimeElapsed < 600 )
03128 {
03129 return FORCE_LEVEL_0;
03130 }
03131 return FORCE_LEVEL_3;
03132 break;
03133 case BOTH_JUMPFLIPSTABDOWN:
03134 if ( animTimer <= 1300 )
03135 {
03136 return FORCE_LEVEL_0;
03137 }
03138 else if ( animTimeElapsed <= 300 )
03139 {
03140 return FORCE_LEVEL_0;
03141 }
03142 return FORCE_LEVEL_3;
03143 break;
03144 case BOTH_JUMPATTACK6:
03145
03146
03147
03148
03149
03150
03151
03152
03153
03154
03155
03156
03157 if ( ( animTimer >= 1450
03158 && animTimeElapsed >= 400 )
03159 ||(animTimer >= 400
03160 && animTimeElapsed >= 1100 ) )
03161 {
03162 return FORCE_LEVEL_3;
03163 }
03164 return FORCE_LEVEL_0;
03165 break;
03166 case BOTH_JUMPATTACK7:
03167 if ( animTimer <= 1200 )
03168 {
03169 return FORCE_LEVEL_0;
03170 }
03171 else if ( animTimeElapsed < 200 )
03172 {
03173 return FORCE_LEVEL_0;
03174 }
03175 return FORCE_LEVEL_3;
03176 break;
03177 case BOTH_SPINATTACK6:
03178 if ( animTimeElapsed <= 200 )
03179 {
03180 return FORCE_LEVEL_0;
03181 }
03182 return FORCE_LEVEL_2;
03183 break;
03184 case BOTH_SPINATTACK7:
03185 if ( animTimer <= 500 )
03186 {
03187 return FORCE_LEVEL_0;
03188 }
03189 else if ( animTimeElapsed < 500 )
03190 {
03191 return FORCE_LEVEL_0;
03192 }
03193 return FORCE_LEVEL_2;
03194 break;
03195 case BOTH_FORCELONGLEAP_ATTACK:
03196 if ( animTimeElapsed <= 200 )
03197 {
03198 return FORCE_LEVEL_3;
03199 }
03200 break;
03201
03202
03203
03204
03205
03206
03207
03208
03209
03210 case BOTH_STABDOWN:
03211 if ( animTimer <= 900 )
03212 {
03213 return FORCE_LEVEL_3;
03214 }
03215 break;
03216 case BOTH_STABDOWN_STAFF:
03217 if ( animTimer <= 850 )
03218 {
03219 return FORCE_LEVEL_3;
03220 }
03221 break;
03222 case BOTH_STABDOWN_DUAL:
03223 if ( animTimer <= 900 )
03224 {
03225 return FORCE_LEVEL_3;
03226 }
03227 break;
03228 case BOTH_A6_SABERPROTECT:
03229 if ( animTimer < 650 )
03230 {
03231 return FORCE_LEVEL_0;
03232 }
03233 else if ( animTimeElapsed < 250 )
03234 {
03235 return FORCE_LEVEL_0;
03236 }
03237 return FORCE_LEVEL_3;
03238 break;
03239 case BOTH_A7_SOULCAL:
03240 if ( animTimer < 650 )
03241 {
03242 return FORCE_LEVEL_0;
03243 }
03244 else if ( animTimeElapsed < 600 )
03245 {
03246 return FORCE_LEVEL_0;
03247 }
03248 return FORCE_LEVEL_3;
03249 break;
03250 case BOTH_A1_SPECIAL:
03251 if ( animTimer < 600 )
03252 {
03253 return FORCE_LEVEL_0;
03254 }
03255 else if ( animTimeElapsed < 200 )
03256 {
03257 return FORCE_LEVEL_0;
03258 }
03259 return FORCE_LEVEL_3;
03260 break;
03261 case BOTH_A2_SPECIAL:
03262 if ( animTimer < 300 )
03263 {
03264 return FORCE_LEVEL_0;
03265 }
03266 else if ( animTimeElapsed < 200 )
03267 {
03268 return FORCE_LEVEL_0;
03269 }
03270 return FORCE_LEVEL_3;
03271 break;
03272 case BOTH_A3_SPECIAL:
03273 if ( animTimer < 700 )
03274 {
03275 return FORCE_LEVEL_0;
03276 }
03277 else if ( animTimeElapsed < 200 )
03278 {
03279 return FORCE_LEVEL_0;
03280 }
03281 return FORCE_LEVEL_3;
03282 break;
03283 case BOTH_FLIP_ATTACK7:
03284 return FORCE_LEVEL_3;
03285 break;
03286 case BOTH_PULL_IMPALE_STAB:
03287 if ( mySaberHit )
03288 {
03289 return FORCE_LEVEL_1;
03290 }
03291 if ( animTimer < 1000 )
03292 {
03293 return FORCE_LEVEL_0;
03294 }
03295 return FORCE_LEVEL_3;
03296 break;
03297 case BOTH_PULL_IMPALE_SWING:
03298 if ( animTimer < 500 )
03299 {
03300 return FORCE_LEVEL_0;
03301 }
03302 else if ( animTimeElapsed < 650 )
03303 {
03304 return FORCE_LEVEL_0;
03305 }
03306 return FORCE_LEVEL_3;
03307 break;
03308 case BOTH_ALORA_SPIN_SLASH:
03309 if ( animTimer < 900 )
03310 {
03311 return FORCE_LEVEL_0;
03312 }
03313 else if ( animTimeElapsed < 250 )
03314 {
03315 return FORCE_LEVEL_0;
03316 }
03317 return FORCE_LEVEL_3;
03318 break;
03319 case BOTH_A6_FB:
03320 if ( mySaberHit )
03321 {
03322 return FORCE_LEVEL_1;
03323 }
03324 if ( animTimer < 250 )
03325 {
03326 return FORCE_LEVEL_0;
03327 }
03328 else if ( animTimeElapsed < 250 )
03329 {
03330 return FORCE_LEVEL_0;
03331 }
03332 return FORCE_LEVEL_3;
03333 break;
03334 case BOTH_A6_LR:
03335 if ( mySaberHit )
03336 {
03337 return FORCE_LEVEL_1;
03338 }
03339 if ( animTimer < 250 )
03340 {
03341 return FORCE_LEVEL_0;
03342 }
03343 else if ( animTimeElapsed < 250 )
03344 {
03345 return FORCE_LEVEL_0;
03346 }
03347 return FORCE_LEVEL_3;
03348 break;
03349 case BOTH_A7_HILT:
03350 return FORCE_LEVEL_0;
03351 break;
03352
03353 case BOTH_LK_S_DL_T_SB_1_W:
03354 if ( animTimer < 700 )
03355 {
03356 return FORCE_LEVEL_0;
03357 }
03358 return FORCE_LEVEL_5;
03359 break;
03360 case BOTH_LK_S_ST_S_SB_1_W:
03361 if ( animTimer < 300 )
03362 {
03363 return FORCE_LEVEL_0;
03364 }
03365 return FORCE_LEVEL_5;
03366 break;
03367 case BOTH_LK_S_DL_S_SB_1_W:
03368 case BOTH_LK_S_S_S_SB_1_W:
03369 if ( animTimer < 700 )
03370 {
03371 return FORCE_LEVEL_0;
03372 }
03373 else if ( animTimeElapsed < 400 )
03374 {
03375 return FORCE_LEVEL_0;
03376 }
03377 return FORCE_LEVEL_5;
03378 break;
03379 case BOTH_LK_S_ST_T_SB_1_W:
03380 case BOTH_LK_S_S_T_SB_1_W:
03381 if ( animTimer < 150 )
03382 {
03383 return FORCE_LEVEL_0;
03384 }
03385 else if ( animTimeElapsed < 400 )
03386 {
03387 return FORCE_LEVEL_0;
03388 }
03389 return FORCE_LEVEL_5;
03390 break;
03391 case BOTH_LK_DL_DL_T_SB_1_W:
03392 return FORCE_LEVEL_5;
03393 break;
03394 case BOTH_LK_DL_DL_S_SB_1_W:
03395 case BOTH_LK_DL_ST_S_SB_1_W:
03396 if ( animTimeElapsed < 1000 )
03397 {
03398 return FORCE_LEVEL_0;
03399 }
03400 return FORCE_LEVEL_5;
03401 break;
03402 case BOTH_LK_DL_ST_T_SB_1_W:
03403 if ( animTimer < 950 )
03404 {
03405 return FORCE_LEVEL_0;
03406 }
03407 else if ( animTimeElapsed < 650 )
03408 {
03409 return FORCE_LEVEL_0;
03410 }
03411 return FORCE_LEVEL_5;
03412 break;
03413 case BOTH_LK_DL_S_S_SB_1_W:
03414 if ( saberNum != 0 )
03415 {
03416 return FORCE_LEVEL_0;
03417 }
03418 if ( animTimer < 900 )
03419 {
03420 return FORCE_LEVEL_0;
03421 }
03422 else if ( animTimeElapsed < 450 )
03423 {
03424 return FORCE_LEVEL_0;
03425 }
03426 return FORCE_LEVEL_5;
03427 break;
03428 case BOTH_LK_DL_S_T_SB_1_W:
03429 if ( saberNum != 0 )
03430 {
03431 return FORCE_LEVEL_0;
03432 }
03433 if ( animTimer < 250 )
03434 {
03435 return FORCE_LEVEL_0;
03436 }
03437 else if ( animTimeElapsed < 150 )
03438 {
03439 return FORCE_LEVEL_0;
03440 }
03441 return FORCE_LEVEL_5;
03442 break;
03443 case BOTH_LK_ST_DL_S_SB_1_W:
03444 return FORCE_LEVEL_5;
03445 break;
03446 case BOTH_LK_ST_DL_T_SB_1_W:
03447
03448 return FORCE_LEVEL_0;
03449 break;
03450 case BOTH_LK_ST_ST_S_SB_1_W:
03451 case BOTH_LK_ST_S_S_SB_1_W:
03452 if ( animTimer < 800 )
03453 {
03454 return FORCE_LEVEL_0;
03455 }
03456 else if ( animTimeElapsed < 350 )
03457 {
03458 return FORCE_LEVEL_0;
03459 }
03460 return FORCE_LEVEL_5;
03461 break;
03462 case BOTH_LK_ST_ST_T_SB_1_W:
03463 case BOTH_LK_ST_S_T_SB_1_W:
03464 return FORCE_LEVEL_5;
03465 break;
03466
03467 case BOTH_HANG_ATTACK:
03468
03469 if ( animTimer < 1000 )
03470 {
03471 return FORCE_LEVEL_0;
03472 }
03473 else if ( animTimeElapsed < 250 )
03474 {
03475 return FORCE_LEVEL_0;
03476 }
03477 else
03478 {
03479 return FORCE_LEVEL_5;
03480 }
03481 break;
03482 case BOTH_ROLL_STAB:
03483 if ( mySaberHit )
03484 {
03485 return FORCE_LEVEL_1;
03486 }
03487 if ( animTimeElapsed > 400 )
03488 {
03489 return FORCE_LEVEL_0;
03490 }
03491 else
03492 {
03493 return FORCE_LEVEL_3;
03494 }
03495 break;
03496 }
03497 return FORCE_LEVEL_0;
03498 }
03499 }
03500
03501 #define MAX_SABER_VICTIMS 16
03502 static int victimEntityNum[MAX_SABER_VICTIMS];
03503 static qboolean victimHitEffectDone[MAX_SABER_VICTIMS];
03504 static float totalDmg[MAX_SABER_VICTIMS];
03505 static vec3_t dmgDir[MAX_SABER_VICTIMS];
03506 static vec3_t dmgSpot[MAX_SABER_VICTIMS];
03507 static qboolean dismemberDmg[MAX_SABER_VICTIMS];
03508 static int saberKnockbackFlags[MAX_SABER_VICTIMS];
03509 static int numVictims = 0;
03510 void WP_SaberClearDamage( void )
03511 {
03512 int ven;
03513 for ( ven = 0; ven < MAX_SABER_VICTIMS; ven++ )
03514 {
03515 victimEntityNum[ven] = ENTITYNUM_NONE;
03516 }
03517 memset( victimHitEffectDone, 0, sizeof( victimHitEffectDone ) );
03518 memset( totalDmg, 0, sizeof( totalDmg ) );
03519 memset( dmgDir, 0, sizeof( dmgDir ) );
03520 memset( dmgSpot, 0, sizeof( dmgSpot ) );
03521 memset( dismemberDmg, 0, sizeof( dismemberDmg ) );
03522 memset( saberKnockbackFlags, 0, sizeof( saberKnockbackFlags ) );
03523 numVictims = 0;
03524 }
03525
03526 void WP_SaberDamageAdd( int trVictimEntityNum, vec3_t trDmgDir, vec3_t trDmgSpot,
03527 int trDmg, qboolean doDismemberment, int knockBackFlags )
03528 {
03529 if ( trVictimEntityNum < 0 || trVictimEntityNum >= ENTITYNUM_WORLD )
03530 {
03531 return;
03532 }
03533
03534 if ( trDmg )
03535 {
03536 int curVictim = 0;
03537 int i;
03538
03539 for ( i = 0; i < numVictims; i++ )
03540 {
03541 if ( victimEntityNum[i] == trVictimEntityNum )
03542 {
03543 curVictim = i;
03544 break;
03545 }
03546 }
03547 if ( i == numVictims )
03548 {
03549 if ( numVictims + 1 >= MAX_SABER_VICTIMS )
03550 {
03551 return;
03552 }
03553
03554 curVictim = numVictims;
03555 victimEntityNum[numVictims++] = trVictimEntityNum;
03556 }
03557
03558 totalDmg[curVictim] += trDmg;
03559 if ( VectorCompare( dmgDir[curVictim], vec3_origin ) )
03560 {
03561 VectorCopy( trDmgDir, dmgDir[curVictim] );
03562 }
03563 if ( VectorCompare( dmgSpot[curVictim], vec3_origin ) )
03564 {
03565 VectorCopy( trDmgSpot, dmgSpot[curVictim] );
03566 }
03567 if ( doDismemberment )
03568 {
03569 dismemberDmg[curVictim] = qtrue;
03570 }
03571 saberKnockbackFlags[curVictim] |= knockBackFlags;
03572 }
03573 }
03574
03575 void WP_SaberApplyDamage( gentity_t *self )
03576 {
03577 int i;
03578 if ( !numVictims )
03579 {
03580 return;
03581 }
03582 for ( i = 0; i < numVictims; i++ )
03583 {
03584 gentity_t *victim = NULL;
03585 int dflags = 0;
03586
03587 victim = &g_entities[victimEntityNum[i]];
03588
03589
03590 if ( !victim->client )
03591 {
03592 totalDmg[i] *= g_saberWallDamageScale.value;
03593 }
03594
03595 if ( !dismemberDmg[i] )
03596 {
03597 dflags |= DAMAGE_NO_DISMEMBER;
03598 }
03599 dflags |= saberKnockbackFlags[i];
03600
03601 G_Damage( victim, self, self, dmgDir[i], dmgSpot[i], totalDmg[i], dflags, MOD_SABER );
03602 }
03603 }
03604
03605
03606 void WP_SaberDoHit( gentity_t *self, int saberNum, int bladeNum )
03607 {
03608 int i;
03609 if ( !numVictims )
03610 {
03611 return;
03612 }
03613 for ( i = 0; i < numVictims; i++ )
03614 {
03615 gentity_t *te = NULL, *victim = NULL;
03616 qboolean isDroid = qfalse;
03617
03618 if ( victimHitEffectDone[i] )
03619 {
03620 continue;
03621 }
03622
03623 victimHitEffectDone[i] = qtrue;
03624
03625 victim = &g_entities[victimEntityNum[i]];
03626
03627 if ( victim->client )
03628 {
03629 class_t npc_class = victim->client->NPC_class;
03630
03631 if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_REMOTE ||
03632 npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 ||
03633 npc_class == CLASS_PROTOCOL || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 ||
03634 npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY )
03635 {
03636 isDroid = qtrue;
03637 }
03638 }
03639
03640 te = G_TempEntity( dmgSpot[i], EV_SABER_HIT );
03641 if ( te )
03642 {
03643 te->s.otherEntityNum = victimEntityNum[i];
03644 te->s.otherEntityNum2 = self->s.number;
03645 te->s.weapon = saberNum;
03646 te->s.legsAnim = bladeNum;
03647
03648 VectorCopy(dmgSpot[i], te->s.origin);
03649
03650 VectorScale( dmgDir[i], -1, te->s.angles);
03651
03652 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
03653 {
03654 te->s.angles[1] = 1;
03655 }
03656
03657 if (!isDroid && (victim->client || victim->s.eType == ET_NPC ||
03658 victim->s.eType == ET_BODY))
03659 {
03660 if ( totalDmg[i] < 5 )
03661 {
03662 te->s.eventParm = 3;
03663 }
03664 else if (totalDmg[i] < 20 )
03665 {
03666 te->s.eventParm = 2;
03667 }
03668 else
03669 {
03670 te->s.eventParm = 1;
03671 }
03672 }
03673 else
03674 {
03675 if ( !WP_SaberBladeUseSecondBladeStyle( &self->client->saber[saberNum], bladeNum )
03676 && (self->client->saber[saberNum].saberFlags2&SFL2_NO_CLASH_FLARE) )
03677 {
03678 }
03679 else if ( WP_SaberBladeUseSecondBladeStyle( &self->client->saber[saberNum], bladeNum )
03680 && (self->client->saber[saberNum].saberFlags2&SFL2_NO_CLASH_FLARE2) )
03681 {
03682 }
03683 else
03684 {
03685 if (totalDmg[i] > SABER_NONATTACK_DAMAGE)
03686 {
03687 gentity_t *teS = G_TempEntity( te->s.origin, EV_SABER_CLASHFLARE );
03688 VectorCopy(te->s.origin, teS->s.origin);
03689 }
03690 te->s.eventParm = 0;
03691 }
03692 }
03693 }
03694 }
03695 }
03696
03697 extern qboolean G_EntIsBreakable( int entityNum );
03698 extern void G_Knockdown( gentity_t *victim );
03699 void WP_SaberRadiusDamage( gentity_t *ent, vec3_t point, float radius, int damage, float knockBack )
03700 {
03701 if ( !ent || !ent->client )
03702 {
03703 return;
03704 }
03705 else if ( radius <= 0.0f || (damage <= 0 && knockBack <= 0) )
03706 {
03707 return;
03708 }
03709 else
03710 {
03711 vec3_t mins, maxs, entDir;
03712 int radiusEnts[128];
03713 gentity_t *radiusEnt = NULL;
03714 int numEnts, i;
03715 float dist;
03716
03717
03718 for ( i = 0; i < 3; i++ )
03719 {
03720 mins[i] = point[i] - radius;
03721 maxs[i] = point[i] + radius;
03722 }
03723
03724
03725 numEnts = trap_EntitiesInBox( mins, maxs, radiusEnts, 128 );
03726
03727 for ( i = 0; i < numEnts; i++ )
03728 {
03729 radiusEnt = &g_entities[radiusEnts[i]];
03730 if ( !radiusEnt->inuse )
03731 {
03732 continue;
03733 }
03734
03735 if ( radiusEnt == ent )
03736 {
03737 continue;
03738 }
03739
03740 if ( radiusEnt->client == NULL )
03741 {
03742 if ( G_EntIsBreakable( radiusEnt->s.number ) )
03743 {
03744 G_Damage( radiusEnt, ent, ent, vec3_origin, radiusEnt->r.currentOrigin, 10, 0, MOD_MELEE );
03745 }
03746 continue;
03747 }
03748
03749 if ( (radiusEnt->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
03750 {
03751 continue;
03752 }
03753
03754 VectorSubtract( radiusEnt->r.currentOrigin, point, entDir );
03755 dist = VectorNormalize( entDir );
03756 if ( dist <= radius )
03757 {
03758 if ( damage > 0 )
03759 {
03760 int points = ceil((float)damage*dist/radius);
03761 G_Damage( radiusEnt, ent, ent, vec3_origin, radiusEnt->r.currentOrigin, points, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
03762 }
03763 if ( knockBack > 0 )
03764 {
03765 if ( radiusEnt->client
03766 && radiusEnt->client->NPC_class != CLASS_RANCOR
03767 && radiusEnt->client->NPC_class != CLASS_ATST
03768 && !(radiusEnt->flags&FL_NO_KNOCKBACK) )
03769 {
03770 float knockbackStr = knockBack*dist/radius;
03771 entDir[2] += 0.1f;
03772 VectorNormalize( entDir );
03773 G_Throw( radiusEnt, entDir, knockbackStr );
03774 if ( radiusEnt->health > 0 )
03775 {
03776 if ( knockbackStr > 50 )
03777 {
03778 if ( dist < (radius*0.5f)
03779 || radiusEnt->client->ps.groundEntityNum != ENTITYNUM_NONE )
03780 {
03781 G_Knockdown( radiusEnt );
03782 }
03783 }
03784 }
03785 }
03786 }
03787 }
03788 }
03789 }
03790 }
03791
03792 static qboolean saberDoClashEffect = qfalse;
03793 static vec3_t saberClashPos = {0};
03794 static vec3_t saberClashNorm = {0};
03795 static int saberClashEventParm = 1;
03796 void WP_SaberDoClash( gentity_t *self, int saberNum, int bladeNum )
03797 {
03798 if ( saberDoClashEffect )
03799 {
03800 gentity_t *te = G_TempEntity( saberClashPos, EV_SABER_BLOCK );
03801 VectorCopy(saberClashPos, te->s.origin);
03802 VectorCopy(saberClashNorm, te->s.angles);
03803 te->s.eventParm = saberClashEventParm;
03804 te->s.otherEntityNum2 = self->s.number;
03805 te->s.weapon = saberNum;
03806 te->s.legsAnim = bladeNum;
03807 }
03808 }
03809
03810 void WP_SaberBounceSound( gentity_t *ent, int saberNum, int bladeNum )
03811 {
03812 int index = 1;
03813 if ( !ent || !ent->client )
03814 {
03815 return;
03816 }
03817 index = Q_irand( 1, 9 );
03818 if ( !WP_SaberBladeUseSecondBladeStyle( &ent->client->saber[saberNum], bladeNum )
03819 && ent->client->saber[saberNum].bounceSound[0] )
03820 {
03821 G_Sound( ent, CHAN_AUTO, ent->client->saber[saberNum].bounceSound[Q_irand( 0, 2 )] );
03822 }
03823 else if ( WP_SaberBladeUseSecondBladeStyle( &ent->client->saber[saberNum], bladeNum )
03824 && ent->client->saber[saberNum].bounce2Sound[0] )
03825 {
03826 G_Sound( ent, CHAN_AUTO, ent->client->saber[saberNum].bounce2Sound[Q_irand( 0, 2 )] );
03827 }
03828 else if ( !WP_SaberBladeUseSecondBladeStyle( &ent->client->saber[saberNum], bladeNum )
03829 && ent->client->saber[saberNum].blockSound[0] )
03830 {
03831 G_Sound( ent, CHAN_AUTO, ent->client->saber[saberNum].blockSound[Q_irand( 0, 2 )] );
03832 }
03833 else if ( WP_SaberBladeUseSecondBladeStyle( &ent->client->saber[saberNum], bladeNum )
03834 && ent->client->saber[saberNum].block2Sound[0] )
03835 {
03836 G_Sound( ent, CHAN_AUTO, ent->client->saber[saberNum].block2Sound[Q_irand( 0, 2 )] );
03837 }
03838 else
03839 {
03840 G_Sound( ent, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/saber/saberblock%d.wav", index ) ) );
03841 }
03842 }
03843
03844 static qboolean saberHitWall = qfalse;
03845 static qboolean saberHitSaber = qfalse;
03846 static float saberHitFraction = 1.0f;
03847
03848
03849
03850
03851 #include "../namespace_begin.h"
03852 qboolean BG_SuperBreakWinAnim( int anim );
03853 #include "../namespace_end.h"
03854
03855 static GAME_INLINE qboolean CheckSaberDamage(gentity_t *self, int rSaberNum, int rBladeNum, vec3_t saberStart, vec3_t saberEnd, qboolean doInterpolate, int trMask, qboolean extrapolate )
03856 {
03857 static trace_t tr;
03858 static vec3_t dir;
03859 static vec3_t saberTrMins, saberTrMaxs;
03860 static vec3_t lastValidStart;
03861 static vec3_t lastValidEnd;
03862 static int selfSaberLevel;
03863 static int otherSaberLevel;
03864 int dmg = 0;
03865 int attackStr = 0;
03866 float saberBoxSize = d_saberBoxTraceSize.value;
03867 qboolean idleDamage = qfalse;
03868 qboolean didHit = qfalse;
03869 qboolean sabersClashed = qfalse;
03870 qboolean unblockable = qfalse;
03871 qboolean didDefense = qfalse;
03872 qboolean didOffense = qfalse;
03873 qboolean saberTraceDone = qfalse;
03874 qboolean otherUnblockable = qfalse;
03875 qboolean tryDeflectAgain = qfalse;
03876
03877 gentity_t *otherOwner;
03878
03879 if (BG_SabersOff( &self->client->ps ))
03880 {
03881 return qfalse;
03882 }
03883
03884 selfSaberLevel = G_SaberAttackPower(self, SaberAttacking(self));
03885
03886
03887 saberBoxSize += (self->client->saber[rSaberNum].blade[rBladeNum].radius*0.5f);
03888
03889 if (self->client->ps.weaponTime <= 0)
03890 {
03891 VectorClear(saberTrMins);
03892 VectorClear(saberTrMaxs);
03893 }
03894 else if (d_saberGhoul2Collision.integer)
03895 {
03896 if ( d_saberSPStyleDamage.integer )
03897 {
03898 VectorSet(saberTrMins, -2, -2, -2 );
03899 VectorSet(saberTrMaxs, 2, 2, 2 );
03900 }
03901 else
03902 {
03903 VectorSet(saberTrMins, -saberBoxSize*3, -saberBoxSize*3, -saberBoxSize*3);
03904 VectorSet(saberTrMaxs, saberBoxSize*3, saberBoxSize*3, saberBoxSize*3);
03905 }
03906 }
03907 else if (self->client->ps.fd.saberAnimLevel < FORCE_LEVEL_2)
03908 {
03909 VectorSet(saberTrMins, -saberBoxSize, -saberBoxSize, -saberBoxSize);
03910 VectorSet(saberTrMaxs, saberBoxSize, saberBoxSize, saberBoxSize);
03911 }
03912 else if (d_saberAlwaysBoxTrace.integer)
03913 {
03914 VectorSet(saberTrMins, -saberBoxSize, -saberBoxSize, -saberBoxSize);
03915 VectorSet(saberTrMaxs, saberBoxSize, saberBoxSize, saberBoxSize);
03916 }
03917 else
03918 {
03919 saberBoxSize = (self->client->saber[rSaberNum].blade[rBladeNum].radius*0.4f);
03920
03921 VectorSet(saberTrMins, -saberBoxSize, -saberBoxSize, -saberBoxSize);
03922 VectorSet(saberTrMaxs, saberBoxSize, saberBoxSize, saberBoxSize);
03923 }
03924
03925 while (!saberTraceDone)
03926 {
03927 if ( doInterpolate
03928 && !d_saberSPStyleDamage.integer )
03929 {
03930 vec3_t oldSaberStart, oldSaberEnd, saberDif, oldSaberDif;
03931 int traceTests = 0;
03932 float trDif = 8;
03933
03934 if ( (level.time-self->client->saber[rSaberNum].blade[rBladeNum].trail.lastTime) > 100 )
03935 {
03936 VectorCopy(saberStart, oldSaberStart);
03937 VectorCopy(saberEnd, oldSaberEnd);
03938 }
03939 else
03940 {
03941 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].trail.base, oldSaberStart);
03942 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].trail.tip, oldSaberEnd);
03943 }
03944
03945 VectorSubtract(saberStart, saberEnd, saberDif);
03946 VectorSubtract(oldSaberStart, oldSaberEnd, oldSaberDif);
03947
03948 VectorNormalize(saberDif);
03949 VectorNormalize(oldSaberDif);
03950
03951 saberEnd[0] = saberStart[0] - (saberDif[0]*trDif);
03952 saberEnd[1] = saberStart[1] - (saberDif[1]*trDif);
03953 saberEnd[2] = saberStart[2] - (saberDif[2]*trDif);
03954
03955 oldSaberEnd[0] = oldSaberStart[0] - (oldSaberDif[0]*trDif);
03956 oldSaberEnd[1] = oldSaberStart[1] - (oldSaberDif[1]*trDif);
03957 oldSaberEnd[2] = oldSaberStart[2] - (oldSaberDif[2]*trDif);
03958
03959 trap_Trace(&tr, saberEnd, saberTrMins, saberTrMaxs, saberStart, self->s.number, trMask);
03960
03961 VectorCopy(saberEnd, lastValidStart);
03962 VectorCopy(saberStart, lastValidEnd);
03963 if (tr.entityNum < MAX_CLIENTS)
03964 {
03965 G_G2TraceCollide(&tr, lastValidStart, lastValidEnd, saberTrMins, saberTrMaxs);
03966 }
03967 else if (tr.entityNum < ENTITYNUM_WORLD)
03968 {
03969 gentity_t *trHit = &g_entities[tr.entityNum];
03970
03971 if (trHit->inuse && trHit->ghoul2)
03972 {
03973 G_G2TraceCollide(&tr, lastValidStart, lastValidEnd, saberTrMins, saberTrMaxs);
03974 }
03975 }
03976
03977 trDif++;
03978
03979 while (tr.fraction == 1.0 && traceTests < 4 && tr.entityNum >= ENTITYNUM_NONE)
03980 {
03981 if ( (level.time-self->client->saber[rSaberNum].blade[rBladeNum].trail.lastTime) > 100 )
03982 {
03983 VectorCopy(saberStart, oldSaberStart);
03984 VectorCopy(saberEnd, oldSaberEnd);
03985 }
03986 else
03987 {
03988 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].trail.base, oldSaberStart);
03989 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].trail.tip, oldSaberEnd);
03990 }
03991
03992 VectorSubtract(saberStart, saberEnd, saberDif);
03993 VectorSubtract(oldSaberStart, oldSaberEnd, oldSaberDif);
03994
03995 VectorNormalize(saberDif);
03996 VectorNormalize(oldSaberDif);
03997
03998 saberEnd[0] = saberStart[0] - (saberDif[0]*trDif);
03999 saberEnd[1] = saberStart[1] - (saberDif[1]*trDif);
04000 saberEnd[2] = saberStart[2] - (saberDif[2]*trDif);
04001
04002 oldSaberEnd[0] = oldSaberStart[0] - (oldSaberDif[0]*trDif);
04003 oldSaberEnd[1] = oldSaberStart[1] - (oldSaberDif[1]*trDif);
04004 oldSaberEnd[2] = oldSaberStart[2] - (oldSaberDif[2]*trDif);
04005
04006 trap_Trace(&tr, saberEnd, saberTrMins, saberTrMaxs, saberStart, self->s.number, trMask);
04007
04008 VectorCopy(saberEnd, lastValidStart);
04009 VectorCopy(saberStart, lastValidEnd);
04010 if (tr.entityNum < MAX_CLIENTS)
04011 {
04012 G_G2TraceCollide(&tr, lastValidStart, lastValidEnd, saberTrMins, saberTrMaxs);
04013 }
04014 else if (tr.entityNum < ENTITYNUM_WORLD)
04015 {
04016 gentity_t *trHit = &g_entities[tr.entityNum];
04017
04018 if (trHit->inuse && trHit->ghoul2)
04019 {
04020 G_G2TraceCollide(&tr, lastValidStart, lastValidEnd, saberTrMins, saberTrMaxs);
04021 }
04022 }
04023
04024 traceTests++;
04025 trDif += 8;
04026 }
04027 }
04028 else
04029 {
04030 vec3_t saberEndExtrapolated;
04031 if ( extrapolate )
04032 {
04033 vec3_t diff;
04034 VectorSubtract( saberEnd, saberStart, diff );
04035 VectorNormalize( diff );
04036 VectorMA( saberStart, SABER_EXTRAPOLATE_DIST, diff, saberEndExtrapolated );
04037 }
04038 else
04039 {
04040 VectorCopy( saberEnd, saberEndExtrapolated );
04041 }
04042 trap_Trace(&tr, saberStart, saberTrMins, saberTrMaxs, saberEndExtrapolated, self->s.number, trMask);
04043
04044 VectorCopy(saberStart, lastValidStart);
04045 VectorCopy(saberEndExtrapolated, lastValidEnd);
04046
04047
04048
04049
04050
04051
04052
04053
04054
04055
04056 if (tr.entityNum < MAX_CLIENTS)
04057 {
04058 G_G2TraceCollide(&tr, lastValidStart, lastValidEnd, saberTrMins, saberTrMaxs);
04059 }
04060 else if (tr.entityNum < ENTITYNUM_WORLD)
04061 {
04062 gentity_t *trHit = &g_entities[tr.entityNum];
04063
04064 if (trHit->inuse && trHit->ghoul2)
04065 {
04066 G_G2TraceCollide(&tr, lastValidStart, lastValidEnd, saberTrMins, saberTrMaxs);
04067 }
04068 }
04069 }
04070
04071 saberTraceDone = qtrue;
04072 }
04073
04074 if ( self->client->ps.saberAttackWound < level.time
04075 && (SaberAttacking(self)
04076 || BG_SuperBreakWinAnim(self->client->ps.torsoAnim)
04077 || (d_saberSPStyleDamage.integer&&self->client->ps.saberInFlight&&rSaberNum==0)
04078 || (WP_SaberBladeDoTransitionDamage( &self->client->saber[rSaberNum], rBladeNum )&&BG_SaberInTransitionAny(self->client->ps.saberMove))
04079 || (self->client->ps.m_iVehicleNum && self->client->ps.saberMove > LS_READY) )
04080 )
04081 {
04082 qboolean saberInSpecial = BG_SaberInSpecial(self->client->ps.saberMove);
04083 qboolean inBackAttack = G_SaberInBackAttack(self->client->ps.saberMove);
04084
04085 if ( d_saberSPStyleDamage.integer )
04086 {
04087 float fDmg = 0.0f;
04088 if ( self->client->ps.saberInFlight )
04089 {
04090 gentity_t *saberEnt = &g_entities[self->client->ps.saberEntityNum];
04091 if ( !saberEnt
04092 || !saberEnt->s.saberInFlight )
04093 {
04094 fDmg = 1.0f;
04095 attackStr = FORCE_LEVEL_0;
04096 }
04097 else
04098 {
04099 fDmg = 2.5f*self->client->ps.fd.forcePowerLevel[FP_SABERTHROW];
04100 attackStr = FORCE_LEVEL_1;
04101 }
04102 }
04103 else
04104 {
04105 attackStr = G_PowerLevelForSaberAnim( self, rSaberNum, qfalse );
04106 if ( g_saberRealisticCombat.integer )
04107 {
04108 switch ( attackStr )
04109 {
04110 default:
04111 case FORCE_LEVEL_3:
04112 fDmg = 10.0f;
04113 break;
04114 case FORCE_LEVEL_2:
04115 fDmg = 5.0f;
04116 break;
04117 case FORCE_LEVEL_1:
04118 case FORCE_LEVEL_0:
04119 fDmg = 2.5f;
04120 break;
04121 }
04122 }
04123 else
04124 {
04125 if ( self->client->ps.torsoAnim == BOTH_SPINATTACK6
04126 || self->client->ps.torsoAnim == BOTH_SPINATTACK7 )
04127 {
04128 fDmg = 2.5f;
04129 }
04130 else
04131 {
04132 fDmg = 2.5f * (float)attackStr;
04133 }
04134 }
04135 }
04136 if ( g_saberRealisticCombat.integer > 1 )
04137 {
04138 if ( g_saberRealisticCombat.integer > 2 )
04139 {
04140 fDmg = 25.0f;
04141 }
04142 else if ( fDmg > 0.1f )
04143 {
04144 fDmg = 25.0f;
04145 }
04146 }
04147
04148
04149
04150
04151
04152
04153
04154
04155
04156
04157
04158
04159
04160 if ( g_gametype.integer != GT_DUEL
04161 && g_gametype.integer != GT_POWERDUEL
04162 && g_gametype.integer != GT_SIEGE )
04163 {
04164 fDmg *= 2.0f;
04165 }
04166 if ( fDmg )
04167 {
04168
04169 float traceLength = Distance( saberEnd, saberStart );
04170 if ( tr.fraction >= 1.0f )
04171 {
04172 dmg = ceil( fDmg*traceLength*0.1f*0.33f );
04173 }
04174 else
04175 {
04176 dmg = ceil( fDmg*traceLength*(1.0f-tr.fraction)*0.1f*0.33f );
04177 }
04178 #ifdef DEBUG_SABER_BOX
04179 if ( g_saberDebugBox.integer == 3 || g_saberDebugBox.integer == 4 )
04180 {
04181 G_TestLine( saberStart, saberEnd, 0x0000ff, 50 );
04182 }
04183 #endif
04184 }
04185
04186
04187
04188
04189
04190
04191 if ( self->client->ps.torsoAnim == BOTH_A1_SPECIAL
04192 || self->client->ps.torsoAnim == BOTH_A2_SPECIAL
04193 || self->client->ps.torsoAnim == BOTH_A3_SPECIAL )
04194 {
04195 attackStr++;
04196 }
04197 if ( BG_SuperBreakWinAnim( self->client->ps.torsoAnim ) )
04198 {
04199 trMask &= ~CONTENTS_LIGHTSABER;
04200 }
04201 }
04202 else
04203 {
04204 dmg = SABER_HITDAMAGE;
04205
04206 if (self->client->ps.fd.saberAnimLevel == SS_STAFF ||
04207 self->client->ps.fd.saberAnimLevel == SS_DUAL)
04208 {
04209 if (saberInSpecial)
04210 {
04211
04212 if (self->client->ps.saberMove == LS_SPINATTACK ||
04213 self->client->ps.saberMove == LS_SPINATTACK_DUAL)
04214 {
04215 dmg = 10;
04216 }
04217 else
04218 {
04219 if ( BG_KickingAnim( self->client->ps.legsAnim ) ||
04220 BG_KickingAnim( self->client->ps.torsoAnim ) )
04221 {
04222 dmg = 2;
04223 }
04224 else if (BG_SaberInKata(self->client->ps.saberMove))
04225 {
04226 if (self->client->ps.fd.saberAnimLevel == SS_DUAL)
04227 {
04228 dmg = 90;
04229 }
04230 else
04231 {
04232 dmg = G_GetAttackDamage(self, 60, 70, 0.5f);
04233 }
04234 }
04235 else
04236 {
04237
04238
04239 dmg = G_GetAttackDamage(self, 2, 90, 0.5f);
04240 }
04241 }
04242 }
04243 else
04244 {
04245 dmg = G_GetAttackDamage(self, 2, 70, 0.5f);
04246 }
04247 }
04248 else if (self->client->ps.fd.saberAnimLevel == 3)
04249 {
04250
04251 if (!saberInSpecial && !inBackAttack)
04252 {
04253 dmg = G_GetAttackDamage(self, 2, 120, 0.5f);
04254 }
04255 else if (saberInSpecial &&
04256 (self->client->ps.saberMove == LS_A_JUMP_T__B_))
04257 {
04258 dmg = G_GetAttackDamage(self, 2, 180, 0.65f);
04259 }
04260 else if (inBackAttack)
04261 {
04262 dmg = G_GetAttackDamage(self, 2, 30, 0.5f);
04263 }
04264 else
04265 {
04266 dmg = 100;
04267 }
04268 }
04269 else if (self->client->ps.fd.saberAnimLevel == 2)
04270 {
04271 if (saberInSpecial &&
04272 (self->client->ps.saberMove == LS_A_FLIP_STAB || self->client->ps.saberMove == LS_A_FLIP_SLASH))
04273 {
04274 dmg = G_GetAttackDamage(self, 2, 80, 0.5f);
04275 }
04276 else if (inBackAttack)
04277 {
04278 dmg = G_GetAttackDamage(self, 2, 25, 0.5f);
04279 }
04280 else
04281 {
04282 dmg = 60;
04283 }
04284 }
04285 else if (self->client->ps.fd.saberAnimLevel == 1)
04286 {
04287 if (saberInSpecial &&
04288 (self->client->ps.saberMove == LS_A_LUNGE))
04289 {
04290 dmg = G_GetAttackDamage(self, 2, SABER_HITDAMAGE-5, 0.3f);
04291 }
04292 else if (inBackAttack)
04293 {
04294 dmg = G_GetAttackDamage(self, 2, 30, 0.5f);
04295 }
04296 else
04297 {
04298 dmg = SABER_HITDAMAGE;
04299 }
04300 }
04301
04302 attackStr = self->client->ps.fd.saberAnimLevel;
04303 }
04304 }
04305 else if (self->client->ps.saberAttackWound < level.time &&
04306 self->client->ps.saberIdleWound < level.time)
04307 {
04308 if ( (self->client->saber[0].saberFlags2&SFL2_NO_IDLE_EFFECT) )
04309 {
04310 return qtrue;
04311 }
04312 trMask &= ~CONTENTS_LIGHTSABER;
04313 if ( d_saberSPStyleDamage.integer )
04314 {
04315 if ( BG_SaberInReturn( self->client->ps.saberMove ) )
04316 {
04317 dmg = SABER_NONATTACK_DAMAGE;
04318 }
04319 else
04320 {
04321 if (d_saberSPStyleDamage.integer == 2)
04322 {
04323 dmg = SABER_NONATTACK_DAMAGE;
04324 }
04325 else
04326 {
04327 dmg = 0;
04328 }
04329 }
04330 }
04331 else
04332 {
04333 dmg = SABER_NONATTACK_DAMAGE;
04334 }
04335 idleDamage = qtrue;
04336 }
04337 else
04338 {
04339 return qtrue;
04340 }
04341
04342 if (BG_SaberInSpecial(self->client->ps.saberMove))
04343 {
04344 qboolean inBackAttack = G_SaberInBackAttack(self->client->ps.saberMove);
04345
04346 unblockable = qtrue;
04347 self->client->ps.saberBlocked = 0;
04348
04349 if ( d_saberSPStyleDamage.integer )
04350 {
04351 }
04352 else if (!inBackAttack)
04353 {
04354 if (self->client->ps.saberMove == LS_A_JUMP_T__B_)
04355 {
04356 dmg += 5;
04357 }
04358 else if (self->client->ps.saberMove == LS_A_FLIP_STAB || self->client->ps.saberMove == LS_A_FLIP_SLASH)
04359 {
04360 dmg += 5;
04361 if (dmg <= 40 || G_GetAnimPoint(self) <= 0.4f)
04362 {
04363 dmg = 2;
04364 }
04365 }
04366 else if (self->client->ps.saberMove == LS_A_LUNGE)
04367 {
04368 dmg += 2;
04369 if (G_GetAnimPoint(self) <= 0.4f)
04370 {
04371 dmg = 2;
04372 }
04373 }
04374 else if (self->client->ps.saberMove == LS_SPINATTACK ||
04375 self->client->ps.saberMove == LS_SPINATTACK_DUAL)
04376 {
04377 dmg = G_GetAttackDamage(self, 2, dmg+3, 0.5f);
04378 dmg += 10;
04379 }
04380 else
04381 {
04382
04383 if ( BG_KickingAnim( self->client->ps.legsAnim ) ||
04384 BG_KickingAnim( self->client->ps.torsoAnim ) )
04385 {
04386 dmg = 2;
04387 }
04388 else
04389 {
04390 dmg = G_GetAttackDamage(self, 5, dmg+5, 0.5f);
04391 }
04392 }
04393 }
04394 }
04395
04396 if (!dmg)
04397 {
04398 if (tr.entityNum < MAX_CLIENTS ||
04399 (g_entities[tr.entityNum].inuse && (g_entities[tr.entityNum].r.contents & CONTENTS_LIGHTSABER)))
04400 {
04401 return qtrue;
04402 }
04403 return qfalse;
04404 }
04405
04406 if (dmg > SABER_NONATTACK_DAMAGE)
04407 {
04408 dmg *= g_saberDamageScale.value;
04409
04410
04411 if ( !WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum )
04412 && self->client->saber[rSaberNum].damageScale != 1.0f )
04413 {
04414 dmg = ceil( (float)dmg*self->client->saber[rSaberNum].damageScale );
04415 }
04416 else if ( WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum )
04417 && self->client->saber[rSaberNum].damageScale2 != 1.0f )
04418 {
04419 dmg = ceil( (float)dmg*self->client->saber[rSaberNum].damageScale2 );
04420 }
04421
04422 if ((self->client->ps.brokenLimbs & (1 << BROKENLIMB_RARM)) ||
04423 (self->client->ps.brokenLimbs & (1 << BROKENLIMB_LARM)))
04424 {
04425 dmg *= 0.3;
04426 if (dmg <= SABER_NONATTACK_DAMAGE)
04427 {
04428 dmg = SABER_NONATTACK_DAMAGE+1;
04429 }
04430 }
04431 }
04432
04433 if (dmg > SABER_NONATTACK_DAMAGE && self->client->ps.isJediMaster)
04434 {
04435 dmg *= 2;
04436 }
04437
04438 if (dmg > SABER_NONATTACK_DAMAGE && g_gametype.integer == GT_SIEGE &&
04439 self->client->siegeClass != -1 && (bgSiegeClasses[self->client->siegeClass].classflags & (1<<CFL_MORESABERDMG)))
04440 {
04441 dmg *= 2;
04442 }
04443
04444 if (g_gametype.integer == GT_POWERDUEL &&
04445 self->client->sess.duelTeam == DUELTEAM_LONE)
04446 {
04447 if ( g_duel_fraglimit.integer )
04448 {
04449
04450
04451 }
04452
04453 }
04454
04455 #ifndef FINAL_BUILD
04456 if (g_saberDebugPrint.integer > 2 && dmg > 1)
04457 {
04458 Com_Printf("CL %i SABER DMG: %i\n", self->s.number, dmg);
04459 }
04460 #endif
04461
04462 VectorSubtract(saberEnd, saberStart, dir);
04463 VectorNormalize(dir);
04464
04465 if (tr.entityNum == ENTITYNUM_WORLD ||
04466 g_entities[tr.entityNum].s.eType == ET_TERRAIN)
04467 {
04468 self->client->ps.saberEventFlags |= SEF_HITWALL;
04469 saberHitWall = qtrue;
04470 }
04471
04472 if (saberHitWall
04473 && (self->client->saber[rSaberNum].saberFlags & SFL_BOUNCE_ON_WALLS)
04474 && (BG_SaberInAttackPure( self->client->ps.saberMove )
04475 || self->client->ps.saberMove == LS_A_JUMP_T__B_ )
04476 )
04477 {
04478
04479
04480
04481
04482
04483
04484
04485
04486
04487
04488
04489
04490
04491
04492
04493
04494
04495
04496
04497
04498 {
04499 gentity_t *te = NULL;
04500
04501
04502
04503
04504
04505
04506
04507
04508
04509
04510
04511 self->client->ps.saberMove = BG_BrokenParryForAttack(self->client->ps.saberMove);
04512 self->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
04513 if (self->client->ps.torsoAnim == self->client->ps.legsAnim)
04514 {
04515 int anim = saberMoveData[self->client->ps.saberMove].animToUse;
04516 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
04517 }
04518
04519
04520 WP_SaberBounceSound( self, rSaberNum, rBladeNum );
04521
04522 te = G_TempEntity( tr.endpos, EV_SABER_HIT );
04523 te->s.otherEntityNum = ENTITYNUM_NONE;
04524 te->s.otherEntityNum2 = self->s.number;
04525 te->s.weapon = rSaberNum;
04526 te->s.legsAnim = rBladeNum;
04527 VectorCopy(tr.endpos, te->s.origin);
04528 VectorCopy(tr.plane.normal, te->s.angles);
04529 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
04530 {
04531 te->s.angles[1] = 1;
04532 }
04533
04534 if ( !WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum ) )
04535 {
04536 WP_SaberRadiusDamage( self, tr.endpos, self->client->saber[rSaberNum].splashRadius, self->client->saber[rSaberNum].splashDamage, self->client->saber[rSaberNum].splashKnockback );
04537 }
04538 else
04539 {
04540 WP_SaberRadiusDamage( self, tr.endpos, self->client->saber[rSaberNum].splashRadius2, self->client->saber[rSaberNum].splashDamage2, self->client->saber[rSaberNum].splashKnockback2 );
04541 }
04542
04543 return qtrue;
04544 }
04545 }
04546
04547
04548
04549
04550
04551 if ((tr.fraction != 1 || tr.startsolid) &&
04552 g_entities[tr.entityNum].takedamage &&
04553 (g_entities[tr.entityNum].health > 0 || !(g_entities[tr.entityNum].s.eFlags & EF_DISINTEGRATION)) &&
04554 tr.entityNum != self->s.number &&
04555 g_entities[tr.entityNum].inuse)
04556 {
04557 if (idleDamage &&
04558 g_entities[tr.entityNum].client &&
04559 OnSameTeam(self, &g_entities[tr.entityNum]) &&
04560 !g_friendlySaber.integer)
04561 {
04562 return qfalse;
04563 }
04564
04565 if (g_entities[tr.entityNum].client &&
04566 g_entities[tr.entityNum].client->ps.duelInProgress &&
04567 g_entities[tr.entityNum].client->ps.duelIndex != self->s.number)
04568 {
04569 return qfalse;
04570 }
04571
04572 if (g_entities[tr.entityNum].client &&
04573 self->client->ps.duelInProgress &&
04574 self->client->ps.duelIndex != g_entities[tr.entityNum].s.number)
04575 {
04576 return qfalse;
04577 }
04578
04579 if ( BG_StabDownAnim( self->client->ps.torsoAnim )
04580 && g_entities[tr.entityNum].client
04581 && !BG_InKnockDownOnGround( &g_entities[tr.entityNum].client->ps ) )
04582 {
04583 return qfalse;
04584 }
04585 self->client->ps.saberIdleWound = level.time + g_saberDmgDelay_Idle.integer;
04586
04587 didHit = qtrue;
04588
04589 if ( !d_saberSPStyleDamage.integer
04590 && g_entities[tr.entityNum].client
04591 && !unblockable
04592 && WP_SaberCanBlock(&g_entities[tr.entityNum], tr.endpos, 0, MOD_SABER, qfalse, attackStr))
04593 {
04594 if (dmg <= SABER_NONATTACK_DAMAGE)
04595 {
04596 self->client->ps.saberIdleWound = level.time + g_saberDmgDelay_Idle.integer;
04597 }
04598 saberDoClashEffect = qtrue;
04599 VectorCopy( tr.endpos, saberClashPos );
04600 VectorCopy( tr.plane.normal, saberClashNorm );
04601 saberClashEventParm = 1;
04602
04603 if (dmg > SABER_NONATTACK_DAMAGE)
04604 {
04605 int lockFactor = g_saberLockFactor.integer;
04606
04607 if ((g_entities[tr.entityNum].client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] - self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE]) > 1 &&
04608 Q_irand(1, 10) < lockFactor*2)
04609 {
04610 if (!G_ClientIdleInWorld(&g_entities[tr.entityNum]))
04611 {
04612 if ( (trMask&CONTENTS_LIGHTSABER)
04613 && WP_SabersCheckLock(self, &g_entities[tr.entityNum]))
04614 {
04615 self->client->ps.saberBlocked = BLOCKED_NONE;
04616 g_entities[tr.entityNum].client->ps.saberBlocked = BLOCKED_NONE;
04617 return didHit;
04618 }
04619 }
04620 }
04621 else if (Q_irand(1, 20) < lockFactor)
04622 {
04623 if (!G_ClientIdleInWorld(&g_entities[tr.entityNum]))
04624 {
04625 if ((trMask&CONTENTS_LIGHTSABER)
04626 && WP_SabersCheckLock(self, &g_entities[tr.entityNum]))
04627 {
04628 self->client->ps.saberBlocked = BLOCKED_NONE;
04629 g_entities[tr.entityNum].client->ps.saberBlocked = BLOCKED_NONE;
04630 return didHit;
04631 }
04632 }
04633 }
04634 }
04635 otherOwner = &g_entities[tr.entityNum];
04636 goto blockStuff;
04637 }
04638 else
04639 {
04640 qboolean doDismemberment = qfalse;
04641 int knockbackFlags = 0;
04642
04643 if (g_entities[tr.entityNum].client)
04644 {
04645 if ( dmg > SABER_NONATTACK_DAMAGE )
04646 {
04647 dmg *= 1.5;
04648 }
04649 }
04650
04651
04652
04653
04654
04655
04656
04657
04658
04659
04660
04661 if ( !d_saberSPStyleDamage.integer )
04662 {
04663 if (g_entities[tr.entityNum].client && g_entities[tr.entityNum].client->ps.weapon == WP_SABER)
04664 {
04665 if (g_gametype.integer != GT_SIEGE)
04666 {
04667 if (dmg > SABER_NONATTACK_DAMAGE && !unblockable)
04668 {
04669 if (dmg == SABER_HITDAMAGE)
04670 {
04671 dmg *= 0.7;
04672 }
04673 else
04674 {
04675 dmg *= 0.5;
04676 }
04677 }
04678 }
04679 }
04680 }
04681
04682 if (self->s.eType == ET_NPC &&
04683 g_entities[tr.entityNum].client &&
04684 self->client->playerTeam == g_entities[tr.entityNum].client->playerTeam)
04685 {
04686 dmg *= 0.2f;
04687 }
04688
04689
04690 if ( !WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum )
04691 && !(self->client->saber[rSaberNum].saberFlags2&SFL2_NO_DISMEMBERMENT) )
04692 {
04693 doDismemberment = qtrue;
04694 }
04695 if ( WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum )
04696 && !(self->client->saber[rSaberNum].saberFlags2&SFL2_NO_DISMEMBERMENT) )
04697 {
04698 doDismemberment = qtrue;
04699 }
04700
04701 if ( !WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum )
04702 && self->client->saber[rSaberNum].knockbackScale > 0.0f )
04703 {
04704 if ( rSaberNum < 1 )
04705 {
04706 knockbackFlags = DAMAGE_SABER_KNOCKBACK1;
04707 }
04708 else
04709 {
04710 knockbackFlags = DAMAGE_SABER_KNOCKBACK2;
04711 }
04712 }
04713
04714 if ( WP_SaberBladeUseSecondBladeStyle( &self->client->saber[rSaberNum], rBladeNum )
04715 && self->client->saber[rSaberNum].knockbackScale > 0.0f )
04716 {
04717 if ( rSaberNum < 1 )
04718 {
04719 knockbackFlags = DAMAGE_SABER_KNOCKBACK1_B2;
04720 }
04721 else
04722 {
04723 knockbackFlags = DAMAGE_SABER_KNOCKBACK2_B2;
04724 }
04725 }
04726
04727 WP_SaberDamageAdd( tr.entityNum, dir, tr.endpos, dmg, doDismemberment, knockbackFlags );
04728
04729 if (g_entities[tr.entityNum].client)
04730 {
04731
04732 if ( self->enemy && self->enemy == &g_entities[tr.entityNum] )
04733 {
04734 self->client->ps.saberEventFlags |= SEF_HITENEMY;
04735 }
04736 else
04737 {
04738 self->client->ps.saberEventFlags |= SEF_HITOBJECT;
04739 }
04740 }
04741
04742 if ( d_saberSPStyleDamage.integer )
04743 {
04744 }
04745 else
04746 {
04747 self->client->ps.saberAttackWound = level.time + 100;
04748 }
04749 }
04750 }
04751 else if ((tr.fraction != 1 || tr.startsolid) &&
04752 (g_entities[tr.entityNum].r.contents & CONTENTS_LIGHTSABER) &&
04753 g_entities[tr.entityNum].r.contents != -1 &&
04754 g_entities[tr.entityNum].inuse)
04755 {
04756 otherOwner = &g_entities[g_entities[tr.entityNum].r.ownerNum];
04757
04758 if (!otherOwner->inuse || !otherOwner->client)
04759 {
04760 return qfalse;
04761 }
04762
04763 if ( otherOwner
04764 && otherOwner->client
04765 && otherOwner->client->ps.saberInFlight )
04766 {
04767 }
04768 else
04769 {
04770 if ( d_saberSPStyleDamage.integer )
04771 {
04772 if ( !WP_SabersIntersect( self, rSaberNum, rBladeNum, otherOwner, qfalse ) )
04773 {
04774 return qfalse;
04775 }
04776 }
04777 else
04778 {
04779 if (!G_SaberCollide(self, otherOwner, lastValidStart,
04780 lastValidEnd, saberTrMins, saberTrMaxs, tr.endpos))
04781 {
04782 return qfalse;
04783 }
04784 }
04785 }
04786
04787 if (OnSameTeam(self, otherOwner) &&
04788 !g_friendlySaber.integer)
04789 {
04790 return qfalse;
04791 }
04792
04793 if ((self->s.eType == ET_NPC || otherOwner->s.eType == ET_NPC) &&
04794 self->client->playerTeam == otherOwner->client->playerTeam &&
04795 g_gametype.integer != GT_SIEGE)
04796 {
04797 return qfalse;
04798 }
04799
04800 if (otherOwner->client->ps.duelInProgress &&
04801 otherOwner->client->ps.duelIndex != self->s.number)
04802 {
04803 return qfalse;
04804 }
04805
04806 if (self->client->ps.duelInProgress &&
04807 self->client->ps.duelIndex != otherOwner->s.number)
04808 {
04809 return qfalse;
04810 }
04811
04812 if ( g_debugSaberLocks.integer )
04813 {
04814 WP_SabersCheckLock2( self, otherOwner, LOCK_RANDOM );
04815 return qtrue;
04816 }
04817 didHit = qtrue;
04818 self->client->ps.saberIdleWound = level.time + g_saberDmgDelay_Idle.integer;
04819
04820 if (dmg <= SABER_NONATTACK_DAMAGE)
04821 {
04822 self->client->ps.saberIdleWound = level.time + g_saberDmgDelay_Idle.integer;
04823 }
04824
04825 saberDoClashEffect = qtrue;
04826 VectorCopy( tr.endpos, saberClashPos );
04827 VectorCopy( tr.plane.normal, saberClashNorm );
04828 saberClashEventParm = 1;
04829
04830 sabersClashed = qtrue;
04831 saberHitSaber = qtrue;
04832 saberHitFraction = tr.fraction;
04833
04834 if (saberCheckKnockdown_Smashed(&g_entities[tr.entityNum], otherOwner, self, dmg))
04835 {
04836 return qfalse;
04837 }
04838
04839
04840 if ( self->client->ps.saberEntityNum
04841 && self->client->ps.saberInFlight
04842 && rSaberNum == 0
04843 && saberCheckKnockdown_Smashed( &g_entities[self->client->ps.saberEntityNum], self, otherOwner, dmg))
04844 {
04845 return qfalse;
04846 }
04847
04848 blockStuff:
04849 otherUnblockable = qfalse;
04850
04851 if (otherOwner && otherOwner->client && otherOwner->client->ps.saberInFlight)
04852 {
04853 return qfalse;
04854 }
04855
04856
04857 if ( self->client->ps.saberEntityNum
04858 && self->client->ps.saberInFlight
04859 && rSaberNum == 0 )
04860 {
04861 return qfalse;
04862 }
04863
04864 otherSaberLevel = G_SaberAttackPower(otherOwner, SaberAttacking(otherOwner));
04865
04866 if (dmg > SABER_NONATTACK_DAMAGE && !unblockable && !otherUnblockable)
04867 {
04868 int lockFactor = g_saberLockFactor.integer;
04869
04870 if (sabersClashed && Q_irand(1, 20) <= lockFactor)
04871 {
04872 if (!G_ClientIdleInWorld(otherOwner))
04873 {
04874 if (WP_SabersCheckLock(self, otherOwner))
04875 {
04876 self->client->ps.saberBlocked = BLOCKED_NONE;
04877 otherOwner->client->ps.saberBlocked = BLOCKED_NONE;
04878 return didHit;
04879 }
04880 }
04881 }
04882 }
04883
04884 if (!otherOwner || !otherOwner->client)
04885 {
04886 return didHit;
04887 }
04888
04889 if (BG_SaberInSpecial(otherOwner->client->ps.saberMove))
04890 {
04891 otherUnblockable = qtrue;
04892 otherOwner->client->ps.saberBlocked = 0;
04893 }
04894
04895 if ( sabersClashed &&
04896 dmg > SABER_NONATTACK_DAMAGE &&
04897 selfSaberLevel < FORCE_LEVEL_3 &&
04898 !PM_SaberInBounce(otherOwner->client->ps.saberMove) &&
04899 !PM_SaberInParry(self->client->ps.saberMove) &&
04900 !PM_SaberInBrokenParry(self->client->ps.saberMove) &&
04901 !BG_SaberInSpecial(self->client->ps.saberMove) &&
04902 !PM_SaberInBounce(self->client->ps.saberMove) &&
04903 !PM_SaberInDeflect(self->client->ps.saberMove) &&
04904 !PM_SaberInReflect(self->client->ps.saberMove) &&
04905 !unblockable )
04906 {
04907
04908 if (1)
04909 {
04910 if (!WP_GetSaberDeflectionAngle(self, otherOwner, tr.fraction))
04911 {
04912 tryDeflectAgain = qtrue;
04913 }
04914 else
04915 {
04916 self->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
04917 didOffense = qtrue;
04918 }
04919 }
04920 else
04921 {
04922 self->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
04923 didOffense = qtrue;
04924
04925 #ifndef FINAL_BUILD
04926 if (g_saberDebugPrint.integer)
04927 {
04928 Com_Printf("Client %i clashed into client %i's saber, did BLOCKED_ATK_BOUNCE\n", self->s.number, otherOwner->s.number);
04929 }
04930 #endif
04931 }
04932 }
04933
04934 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))
04935 && !PM_SaberInBounce(self->client->ps.saberMove)
04936
04937 && !PM_SaberInBrokenParry(otherOwner->client->ps.saberMove)
04938 && !BG_SaberInSpecial(otherOwner->client->ps.saberMove)
04939 && !PM_SaberInBounce(otherOwner->client->ps.saberMove)
04940 && !PM_SaberInDeflect(otherOwner->client->ps.saberMove)
04941 && !PM_SaberInReflect(otherOwner->client->ps.saberMove)
04942
04943 && (otherSaberLevel > FORCE_LEVEL_2 || ( otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] >= 3 && Q_irand(0, otherSaberLevel) ))
04944 && !unblockable
04945 && !otherUnblockable
04946 && dmg > SABER_NONATTACK_DAMAGE
04947 && !didOffense)
04948 {
04949
04950
04951 if (self->client->ps.saberEntityNum)
04952 {
04953 saberCheckKnockdown_BrokenParry(&g_entities[self->client->ps.saberEntityNum], self, otherOwner);
04954 }
04955
04956 if (!PM_SaberInParry(otherOwner->client->ps.saberMove))
04957 {
04958 WP_SaberBlockNonRandom(otherOwner, tr.endpos, qfalse);
04959 otherOwner->client->ps.saberMove = BG_KnockawayForParry( otherOwner->client->ps.saberBlocked );
04960 otherOwner->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
04961 }
04962 else
04963 {
04964 otherOwner->client->ps.saberMove = G_KnockawayForParry(otherOwner->client->ps.saberMove);
04965 otherOwner->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
04966 }
04967
04968
04969 self->client->ps.saberMove = BG_BrokenParryForAttack( self->client->ps.saberMove );
04970 self->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
04971
04972 #ifndef FINAL_BUILD
04973 if (g_saberDebugPrint.integer)
04974 {
04975 Com_Printf("Client %i sent client %i into a reflected attack with a knockaway\n", otherOwner->s.number, self->s.number);
04976 }
04977 #endif
04978
04979 didDefense = qtrue;
04980 }
04981 else if ((selfSaberLevel > FORCE_LEVEL_2 || unblockable) &&
04982 ( 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)) ) &&
04983 PM_SaberInParry(otherOwner->client->ps.saberMove) &&
04984 !PM_SaberInBrokenParry(otherOwner->client->ps.saberMove) &&
04985 !PM_SaberInParry(self->client->ps.saberMove) &&
04986 !PM_SaberInBrokenParry(self->client->ps.saberMove) &&
04987 !PM_SaberInBounce(self->client->ps.saberMove) &&
04988 dmg > SABER_NONATTACK_DAMAGE &&
04989 !didOffense &&
04990 !otherUnblockable)
04991 {
04992 if (otherOwner->client->ps.saberEntityNum)
04993 {
04994 saberCheckKnockdown_BrokenParry(&g_entities[otherOwner->client->ps.saberEntityNum], otherOwner, self);
04995 }
04996
04997 #ifndef FINAL_BUILD
04998 if (g_saberDebugPrint.integer)
04999 {
05000 Com_Printf("Client %i sent client %i into a broken parry\n", self->s.number, otherOwner->s.number);
05001 }
05002 #endif
05003
05004 otherOwner->client->ps.saberMove = BG_BrokenParryForParry( otherOwner->client->ps.saberMove );
05005 otherOwner->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
05006
05007 didDefense = qtrue;
05008 }
05009 else if ((selfSaberLevel > FORCE_LEVEL_2) &&
05010
05011 otherSaberLevel >= FORCE_LEVEL_3 &&
05012 PM_SaberInParry(otherOwner->client->ps.saberMove) &&
05013 !PM_SaberInBrokenParry(otherOwner->client->ps.saberMove) &&
05014 !PM_SaberInParry(self->client->ps.saberMove) &&
05015 !PM_SaberInBrokenParry(self->client->ps.saberMove) &&
05016 !PM_SaberInBounce(self->client->ps.saberMove) &&
05017 !PM_SaberInDeflect(self->client->ps.saberMove) &&
05018 !PM_SaberInReflect(self->client->ps.saberMove) &&
05019 dmg > SABER_NONATTACK_DAMAGE &&
05020 !didOffense &&
05021 !unblockable)
05022 {
05023 #ifndef FINAL_BUILD
05024 if (g_saberDebugPrint.integer)
05025 {
05026 Com_Printf("Client %i bounced off of client %i's saber\n", self->s.number, otherOwner->s.number);
05027 }
05028 #endif
05029
05030 if (!tryDeflectAgain)
05031 {
05032 if (!WP_GetSaberDeflectionAngle(self, otherOwner, tr.fraction))
05033 {
05034 tryDeflectAgain = qtrue;
05035 }
05036 }
05037
05038 didOffense = qtrue;
05039 }
05040 else if (SaberAttacking(otherOwner) && dmg > SABER_NONATTACK_DAMAGE && !BG_SaberInSpecial(otherOwner->client->ps.saberMove) && !didOffense && !otherUnblockable)
05041 {
05042 if (!PM_SaberInBounce(self->client->ps.saberMove) &&
05043 !PM_SaberInBounce(otherOwner->client->ps.saberMove) &&
05044 !PM_SaberInDeflect(self->client->ps.saberMove) &&
05045 !PM_SaberInDeflect(otherOwner->client->ps.saberMove) &&
05046
05047 !PM_SaberInReflect(self->client->ps.saberMove) &&
05048 !PM_SaberInReflect(otherOwner->client->ps.saberMove))
05049 {
05050 int attackAdv, defendStr = G_PowerLevelForSaberAnim( otherOwner, 0, qtrue ), attackBonus = 0;
05051 if ( otherOwner->client->ps.torsoAnim == BOTH_A1_SPECIAL
05052 || otherOwner->client->ps.torsoAnim == BOTH_A2_SPECIAL
05053 || otherOwner->client->ps.torsoAnim == BOTH_A3_SPECIAL )
05054 {
05055 defendStr++;
05056 }
05057 defendStr += Q_irand(0, otherOwner->client->saber[0].parryBonus );
05058 if ( otherOwner->client->saber[1].model
05059 && otherOwner->client->saber[1].model[0]
05060 && !otherOwner->client->ps.saberHolstered )
05061 {
05062 defendStr += Q_irand(0, otherOwner->client->saber[1].parryBonus );
05063 }
05064
05065 #ifndef FINAL_BUILD
05066 if (g_saberDebugPrint.integer)
05067 {
05068 Com_Printf("Client %i and client %i bounced off of each other's sabers\n", self->s.number, otherOwner->s.number);
05069 }
05070 #endif
05071
05072 attackBonus = Q_irand(0, self->client->saber[0].breakParryBonus );
05073 if ( self->client->saber[1].model
05074 && self->client->saber[1].model[0]
05075 && !self->client->ps.saberHolstered )
05076 {
05077 attackBonus += Q_irand(0, self->client->saber[1].breakParryBonus );
05078 }
05079 attackAdv = (attackStr+attackBonus+self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])-(defendStr+otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE]);
05080
05081 if ( attackAdv > 1 )
05082 {
05083 otherOwner->client->ps.saberMove = BG_BrokenParryForAttack( otherOwner->client->ps.saberMove );
05084 otherOwner->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
05085 }
05086 else if ( attackAdv > 0 )
05087 {
05088 otherOwner->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
05089 }
05090 else if ( attackAdv < 1 )
05091 {
05092 self->client->ps.saberMove = BG_BrokenParryForAttack( self->client->ps.saberMove );
05093 self->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
05094 }
05095 else if ( attackAdv < 0 )
05096 {
05097 self->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
05098 }
05099 else
05100 {
05101 self->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
05102 otherOwner->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
05103 }
05104
05105 didOffense = qtrue;
05106 }
05107 }
05108
05109 if (d_saberGhoul2Collision.integer && !didDefense && dmg <= SABER_NONATTACK_DAMAGE && !otherUnblockable)
05110 {
05111 if (!PM_SaberInParry(otherOwner->client->ps.saberMove) &&
05112 !PM_SaberInBrokenParry(otherOwner->client->ps.saberMove) &&
05113 !BG_SaberInSpecial(otherOwner->client->ps.saberMove) &&
05114 !PM_SaberInBounce(otherOwner->client->ps.saberMove) &&
05115 !PM_SaberInDeflect(otherOwner->client->ps.saberMove) &&
05116 !PM_SaberInReflect(otherOwner->client->ps.saberMove))
05117 {
05118 WP_SaberBlockNonRandom(otherOwner, tr.endpos, qfalse);
05119 otherOwner->client->ps.saberEventFlags |= SEF_PARRIED;
05120 }
05121 }
05122 else if (!didDefense && dmg > SABER_NONATTACK_DAMAGE && !otherUnblockable)
05123 {
05124 if (!PM_SaberInParry(otherOwner->client->ps.saberMove) &&
05125 !PM_SaberInBrokenParry(otherOwner->client->ps.saberMove) &&
05126 !BG_SaberInSpecial(otherOwner->client->ps.saberMove) &&
05127 !PM_SaberInBounce(otherOwner->client->ps.saberMove) &&
05128 !PM_SaberInDeflect(otherOwner->client->ps.saberMove) &&
05129 !PM_SaberInReflect(otherOwner->client->ps.saberMove))
05130 {
05131 qboolean crushTheParry = qfalse;
05132
05133 if (unblockable)
05134 {
05135 crushTheParry = qtrue;
05136 }
05137
05138 if (!SaberAttacking(otherOwner))
05139 {
05140 int otherIdleStr = otherOwner->client->ps.fd.saberAnimLevel;
05141 if ( otherIdleStr == SS_DUAL
05142 || otherIdleStr == SS_STAFF )
05143 {
05144 otherIdleStr = SS_MEDIUM;
05145 }
05146
05147 WP_SaberBlockNonRandom(otherOwner, tr.endpos, qfalse);
05148 otherOwner->client->ps.saberEventFlags |= SEF_PARRIED;
05149 self->client->ps.saberEventFlags |= SEF_BLOCKED;
05150
05151 if ( attackStr+self->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] > otherIdleStr+otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] )
05152 {
05153 crushTheParry = qtrue;
05154 }
05155 else
05156 {
05157 tryDeflectAgain = qtrue;
05158 }
05159 }
05160 else if (selfSaberLevel > otherSaberLevel ||
05161 (selfSaberLevel == otherSaberLevel && Q_irand(1, 10) <= 2))
05162 {
05163
05164 WP_SaberBlockNonRandom(otherOwner, tr.endpos, qfalse);
05165 crushTheParry = qtrue;
05166
05167 if (otherOwner->client->ps.saberEntityNum)
05168 {
05169 saberCheckKnockdown_BrokenParry(&g_entities[otherOwner->client->ps.saberEntityNum], otherOwner, self);
05170 }
05171
05172 #ifndef FINAL_BUILD
05173 if (g_saberDebugPrint.integer)
05174 {
05175 Com_Printf("Client %i forced client %i into a broken parry with a stronger attack\n", self->s.number, otherOwner->s.number);
05176 }
05177 #endif
05178 }
05179 else
05180 {
05181 if (selfSaberLevel == otherSaberLevel)
05182 {
05183 if (!didOffense &&
05184 !PM_SaberInParry(self->client->ps.saberMove) &&
05185 !PM_SaberInBrokenParry(self->client->ps.saberMove) &&
05186 !BG_SaberInSpecial(self->client->ps.saberMove) &&
05187 !PM_SaberInBounce(self->client->ps.saberMove) &&
05188 !PM_SaberInDeflect(self->client->ps.saberMove) &&
05189 !PM_SaberInReflect(self->client->ps.saberMove) &&
05190 !unblockable)
05191 {
05192 self->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
05193 didOffense = qtrue;
05194 }
05195 if (!didDefense &&
05196 !PM_SaberInParry(otherOwner->client->ps.saberMove) &&
05197 !PM_SaberInBrokenParry(otherOwner->client->ps.saberMove) &&
05198 !BG_SaberInSpecial(otherOwner->client->ps.saberMove) &&
05199 !PM_SaberInBounce(otherOwner->client->ps.saberMove) &&
05200 !PM_SaberInDeflect(otherOwner->client->ps.saberMove) &&
05201 !PM_SaberInReflect(otherOwner->client->ps.saberMove) &&
05202 !unblockable)
05203 {
05204 otherOwner->client->ps.saberBlocked = BLOCKED_ATK_BOUNCE;
05205 }
05206 #ifndef FINAL_BUILD
05207 if (g_saberDebugPrint.integer)
05208 {
05209 Com_Printf("Equal attack level bounce/deflection for clients %i and %i\n", self->s.number, otherOwner->s.number);
05210 }
05211 #endif
05212
05213 self->client->ps.saberEventFlags |= SEF_DEFLECTED;
05214 otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED;
05215 }
05216 else if ((level.time - otherOwner->client->lastSaberStorageTime) < 500 && !unblockable)
05217 {
05218
05219 self->client->ps.saberMove = BG_BrokenParryForAttack(self->client->ps.saberMove);
05220 self->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
05221
05222 if (self->client->ps.saberEntityNum)
05223 {
05224 saberCheckKnockdown_BrokenParry(&g_entities[self->client->ps.saberEntityNum], self, otherOwner);
05225 }
05226
05227 #ifndef FINAL_BUILD
05228 if (g_saberDebugPrint.integer)
05229 {
05230 Com_Printf("Client %i hit client %i's stronger attack, was forced into a broken parry\n", self->s.number, otherOwner->s.number);
05231 }
05232 #endif
05233
05234 otherOwner->client->ps.saberEventFlags &= ~SEF_BLOCKED;
05235
05236 didOffense = qtrue;
05237 }
05238 }
05239
05240 if (crushTheParry && PM_SaberInParry(G_GetParryForBlock(otherOwner->client->ps.saberBlocked)))
05241 {
05242
05243 otherOwner->client->ps.saberMove = BG_BrokenParryForParry( G_GetParryForBlock(otherOwner->client->ps.saberBlocked) );
05244 otherOwner->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
05245
05246 otherOwner->client->ps.saberEventFlags &= ~SEF_PARRIED;
05247 self->client->ps.saberEventFlags &= ~SEF_BLOCKED;
05248
05249 #ifndef FINAL_BUILD
05250 if (g_saberDebugPrint.integer)
05251 {
05252 Com_Printf("Client %i broke through %i's parry with a special or stronger attack\n", self->s.number, otherOwner->s.number);
05253 }
05254 #endif
05255 }
05256 else if (PM_SaberInParry(G_GetParryForBlock(otherOwner->client->ps.saberBlocked)) && !didOffense && tryDeflectAgain)
05257 {
05258 int preMove = otherOwner->client->ps.saberMove;
05259
05260 otherOwner->client->ps.saberMove = G_GetParryForBlock(otherOwner->client->ps.saberBlocked);
05261 WP_GetSaberDeflectionAngle(self, otherOwner, tr.fraction);
05262 otherOwner->client->ps.saberMove = preMove;
05263 }
05264 }
05265 }
05266
05267 self->client->ps.saberAttackWound = level.time + g_saberDmgDelay_Wound.integer;
05268 }
05269
05270 return didHit;
05271 }
05272
05273 GAME_INLINE int VectorCompare2( const vec3_t v1, const vec3_t v2 ) {
05274 if ( v1[0] > v2[0]+0.0001f || v1[0] < v2[0]-0.0001f
05275 || v1[1] > v2[1]+0.0001f || v1[1] < v2[1]-0.0001f
05276 || v1[2] > v2[2]+0.0001f || v1[2] < v2[2]-0.0001f ) {
05277 return 0;
05278 }
05279 return 1;
05280 }
05281
05282 #define MAX_SABER_SWING_INC 0.33f
05283 void G_SPSaberDamageTraceLerped( gentity_t *self, int saberNum, int bladeNum, vec3_t baseNew, vec3_t endNew, int clipmask )
05284 {
05285 vec3_t baseOld, endOld;
05286 vec3_t mp1, mp2;
05287 vec3_t md1, md2;
05288
05289 if ( (level.time-self->client->saber[saberNum].blade[bladeNum].trail.lastTime) > 100 )
05290 {
05291 VectorCopy(baseNew, baseOld);
05292 VectorCopy(endNew, endOld);
05293 }
05294 else
05295 {
05296 VectorCopy( self->client->saber[saberNum].blade[bladeNum].trail.base, baseOld );
05297 VectorCopy( self->client->saber[saberNum].blade[bladeNum].trail.tip, endOld );
05298 }
05299
05300 VectorCopy( baseOld, mp1 );
05301 VectorCopy( baseNew, mp2 );
05302 VectorSubtract( endOld, baseOld, md1 );
05303 VectorNormalize( md1 );
05304 VectorSubtract( endNew, baseNew, md2 );
05305 VectorNormalize( md2 );
05306
05307 saberHitWall = qfalse;
05308 saberHitSaber = qfalse;
05309 saberHitFraction = 1.0f;
05310 if ( VectorCompare2( baseOld, baseNew ) && VectorCompare2( endOld, endNew ) )
05311 {
05312 CheckSaberDamage( self, saberNum, bladeNum, baseNew, endNew, qfalse, clipmask, qfalse );
05313 }
05314 else
05315 {
05316 float step = 8, stepsize = 8;
05317 vec3_t ma1, ma2, md2ang, curBase1, curBase2;
05318 int xx;
05319 vec3_t curMD1, curMD2;
05320 float dirInc, curDirFrac;
05321 vec3_t baseDiff, bladePointOld, bladePointNew;
05322 qboolean extrapolate = qtrue;
05323
05324
05325 VectorCopy( baseOld, bladePointOld );
05326 VectorCopy( baseNew, bladePointNew );
05327 CheckSaberDamage( self, saberNum, bladeNum, bladePointOld, bladePointNew, qfalse, clipmask, qtrue );
05328
05329
05330 if ( saberHitFraction < 1.0f )
05331 {
05332
05333 vec3_t ma1, ma2;
05334 vectoangles( md1, ma1 );
05335 vectoangles( md2, ma2 );
05336 for ( xx = 0; xx < 3; xx++ )
05337 {
05338 md2ang[xx] = LerpAngle( ma1[xx], ma2[xx], saberHitFraction );
05339 }
05340 AngleVectors( md2ang, md2, NULL, NULL );
05341
05342 VectorSubtract( mp2, mp1, baseDiff );
05343 VectorMA( mp1, saberHitFraction, baseDiff, baseNew );
05344 VectorMA( baseNew, self->client->saber[saberNum].blade[bladeNum].lengthMax, md2, endNew );
05345 }
05346
05347
05348 if ( BG_SaberInAttack( self->client->ps.saberMove )
05349 || BG_SaberInSpecialAttack( self->client->ps.torsoAnim )
05350 || BG_SpinningSaberAnim( self->client->ps.torsoAnim )
05351 || BG_InSpecialJump( self->client->ps.torsoAnim ) )
05352
05353 {
05354 curDirFrac = DotProduct( md1, md2 );
05355 }
05356 else
05357 {
05358 curDirFrac = 1.0f;
05359 }
05360
05361 if ( fabs(curDirFrac) < 1.0f - MAX_SABER_SWING_INC )
05362 {
05363 curDirFrac = dirInc = 1.0f/((1.0f - curDirFrac)/MAX_SABER_SWING_INC);
05364 }
05365 else
05366 {
05367 curDirFrac = 1.0f;
05368 dirInc = 0.0f;
05369 }
05370
05371
05372 vectoangles( md1, ma1 );
05373 vectoangles( md2, ma2 );
05374
05375
05376 VectorCopy( md1, curMD2 );
05377 VectorCopy( baseOld, curBase2 );
05378
05379 while ( 1 )
05380 {
05381 VectorCopy( curMD2, curMD1 );
05382 VectorCopy( curBase2, curBase1 );
05383 if ( curDirFrac >= 1.0f )
05384 {
05385 VectorCopy( md2, curMD2 );
05386 VectorCopy( baseNew, curBase2 );
05387 }
05388 else
05389 {
05390 for ( xx = 0; xx < 3; xx++ )
05391 {
05392 md2ang[xx] = LerpAngle( ma1[xx], ma2[xx], curDirFrac );
05393 }
05394 AngleVectors( md2ang, curMD2, NULL, NULL );
05395
05396 VectorSubtract( baseNew, baseOld, baseDiff );
05397 VectorMA( baseOld, curDirFrac, baseDiff, curBase2 );
05398 }
05399
05400 for ( step = stepsize; step <= self->client->saber[saberNum].blade[bladeNum].lengthMax ; step += stepsize )
05401 {
05402 VectorMA( curBase1, step, curMD1, bladePointOld );
05403 VectorMA( curBase2, step, curMD2, bladePointNew );
05404
05405 if ( step+stepsize >= self->client->saber[saberNum].blade[bladeNum].lengthMax )
05406 {
05407 extrapolate = qfalse;
05408 }
05409
05410 CheckSaberDamage( self, saberNum, bladeNum, bladePointOld, bladePointNew, qfalse, clipmask, extrapolate );
05411
05412
05413
05414
05415
05416
05417
05418
05419
05420
05421 if ( saberHitFraction < 1.0f )
05422 {
05423 vec3_t curMA1, curMA2;
05424
05425 VectorSubtract( mp2, mp1, baseDiff );
05426 VectorMA( mp1, saberHitFraction, baseDiff, baseNew );
05427 VectorMA( baseNew, self->client->saber[saberNum].blade[bladeNum].lengthMax, curMD2, endNew );
05428
05429 vectoangles( curMD1, curMA1 );
05430 vectoangles( curMD2, curMA2 );
05431 for ( xx = 0; xx < 3; xx++ )
05432 {
05433 md2ang[xx] = LerpAngle( curMA1[xx], curMA2[xx], saberHitFraction );
05434 }
05435 AngleVectors( md2ang, curMD2, NULL, NULL );
05436 saberHitSaber = qtrue;
05437 }
05438 if (saberHitWall)
05439 {
05440 break;
05441 }
05442 }
05443 if ( saberHitWall || saberHitSaber )
05444 {
05445 break;
05446 }
05447 if ( curDirFrac >= 1.0f )
05448 {
05449 break;
05450 }
05451 else
05452 {
05453 curDirFrac += dirInc;
05454 if ( curDirFrac >= 1.0f )
05455 {
05456 curDirFrac = 1.0f;
05457 }
05458 }
05459 }
05460
05461
05462
05463
05464
05465
05466
05467
05468
05469
05470
05471
05472
05473
05474
05475
05476
05477 }
05478 }
05479
05480 #include "../namespace_begin.h"
05481 qboolean BG_SaberInTransitionAny( int move );
05482 #include "../namespace_end.h"
05483
05484 qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower );
05485 qboolean InFOV3( vec3_t spot, vec3_t from, vec3_t fromAngles, int hFOV, int vFOV );
05486 qboolean Jedi_WaitingAmbush( gentity_t *self );
05487 void Jedi_Ambush( gentity_t *self );
05488 evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc, vec3_t phitDir, gentity_t *incoming, float dist );
05489 void NPC_SetLookTarget( gentity_t *self, int entNum, int clearTime );
05490 void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd )
05491 {
05492 float dist;
05493 gentity_t *ent, *incoming = NULL;
05494 int entityList[MAX_GENTITIES];
05495 int numListedEntities;
05496 vec3_t mins, maxs;
05497 int i, e;
05498 float closestDist, radius = 256;
05499 vec3_t forward, dir, missile_dir, fwdangles = {0};
05500 trace_t trace;
05501 vec3_t traceTo, entDir;
05502 float dot1, dot2;
05503 float lookTDist = -1;
05504 gentity_t *lookT = NULL;
05505 qboolean doFullRoutine = qtrue;
05506
05507
05508 if ( !(self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
05509 {
05510 self->client->ps.hasLookTarget = qfalse;
05511 }
05512
05513 if ( self->client->ps.weapon != WP_SABER && self->client->NPC_class != CLASS_BOBAFETT )
05514 {
05515 doFullRoutine = qfalse;
05516 }
05517 else if ( self->client->ps.saberInFlight )
05518 {
05519 doFullRoutine = qfalse;
05520 }
05521 else if ( self->client->ps.fd.forcePowersActive&(1<<FP_LIGHTNING) )
05522 {
05523 doFullRoutine = qfalse;
05524 }
05525 else if ( self->client->ps.fd.forcePowersActive&(1<<FP_DRAIN) )
05526 {
05527 doFullRoutine = qfalse;
05528 }
05529 else if ( self->client->ps.fd.forcePowersActive&(1<<FP_PUSH) )
05530 {
05531 doFullRoutine = qfalse;
05532 }
05533 else if ( self->client->ps.fd.forcePowersActive&(1<<FP_GRIP) )
05534 {
05535 doFullRoutine = qfalse;
05536 }
05537
05538 if (self->client->ps.weaponTime > 0)
05539 {
05540 return;
05541 }
05542
05543 if ( (self->client->saber[0].saberFlags&SFL_NOT_ACTIVE_BLOCKING) )
05544 {
05545 return;
05546 }
05547
05548 if ( self->health <= 0 )
05549 {
05550 return;
05551 }
05552 if ( PM_InKnockDown( &self->client->ps ) )
05553 {
05554 return;
05555 }
05556
05557 if ( BG_SabersOff( &self->client->ps ) && self->client->NPC_class != CLASS_BOBAFETT )
05558 {
05559 if ( self->s.eType != ET_NPC )
05560 {
05561 doFullRoutine = qfalse;
05562 }
05563 }
05564
05565 if ( self->s.eType == ET_PLAYER )
05566 {
05567 if ( ucmd->buttons & BUTTON_ATTACK
05568 || BG_SaberInAttack( self->client->ps.saberMove )
05569 || BG_SaberInSpecialAttack( self->client->ps.torsoAnim )
05570 || BG_SaberInTransitionAny( self->client->ps.saberMove ))
05571 {
05572 doFullRoutine = qfalse;
05573 }
05574 }
05575
05576 if ( self->client->ps.fd.forcePowerDebounce[FP_SABER_DEFENSE] > level.time )
05577 {
05578 doFullRoutine = qfalse;
05579 }
05580
05581 fwdangles[1] = self->client->ps.viewangles[1];
05582 AngleVectors( fwdangles, forward, NULL, NULL );
05583
05584 for ( i = 0 ; i < 3 ; i++ )
05585 {
05586 mins[i] = self->r.currentOrigin[i] - radius;
05587 maxs[i] = self->r.currentOrigin[i] + radius;
05588 }
05589
05590 numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
05591
05592 closestDist = radius;
05593
05594 for ( e = 0 ; e < numListedEntities ; e++ )
05595 {
05596 ent = &g_entities[entityList[ e ]];
05597
05598 if (ent == self)
05599 continue;
05600
05601
05602 if (self->s.eType == ET_PLAYER &&
05603 ent->client &&
05604 (ent->s.eType == ET_NPC || ent->s.eType == ET_PLAYER) &&
05605 !OnSameTeam(ent, self) &&
05606 ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
05607 !(ent->client->ps.pm_flags & PMF_FOLLOW) &&
05608 (ent->s.eType != ET_NPC || ent->s.NPC_class != CLASS_VEHICLE) &&
05609 ent->health > 0)
05610 {
05611 vec3_t vecSub;
05612 float vecLen;
05613
05614 VectorSubtract(self->client->ps.origin, ent->client->ps.origin, vecSub);
05615 vecLen = VectorLength(vecSub);
05616
05617 if (lookTDist == -1 || vecLen < lookTDist)
05618 {
05619 trace_t tr;
05620 vec3_t myEyes;
05621
05622 VectorCopy(self->client->ps.origin, myEyes);
05623 myEyes[2] += self->client->ps.viewheight;
05624
05625 trap_Trace(&tr, myEyes, NULL, NULL, ent->client->ps.origin, self->s.number, MASK_PLAYERSOLID);
05626
05627 if (tr.fraction == 1.0f || tr.entityNum == ent->s.number)
05628 {
05629 lookT = ent;
05630 lookTDist = vecLen;
05631 }
05632 }
05633 }
05634
05635 if (!doFullRoutine)
05636 {
05637 continue;
05638 }
05639
05640 if (ent->r.ownerNum == self->s.number)
05641 continue;
05642 if ( !(ent->inuse) )
05643 continue;
05644 if ( ent->s.eType != ET_MISSILE && !(ent->s.eFlags&EF_MISSILE_STICK) )
05645 {
05646 gentity_t *pOwner;
05647
05648 if (ent->r.ownerNum < 0 || ent->r.ownerNum >= ENTITYNUM_WORLD)
05649 {
05650 continue;
05651 }
05652
05653 pOwner = &g_entities[ent->r.ownerNum];
05654
05655 if (!pOwner->inuse || !pOwner->client)
05656 {
05657 continue;
05658 }
05659
05660 if (!pOwner->client->ps.saberEntityNum ||
05661 !pOwner->client->ps.saberInFlight ||
05662 pOwner->client->ps.saberEntityNum != ent->s.number)
05663 {
05664 continue;
05665 }
05666
05667
05668 }
05669 else
05670 {
05671 if ( ent->s.pos.trType == TR_STATIONARY && self->s.eType == ET_PLAYER )
05672 {
05673 continue;
05674 }
05675 }
05676
05677
05678 VectorSubtract( ent->r.currentOrigin, self->r.currentOrigin, dir );
05679 dist = VectorNormalize( dir );
05680
05681 if ( ent->s.weapon == WP_THERMAL )
05682 {
05683 if ( self->NPC && dist < ent->splashRadius )
05684 {
05685 if ( dist < ent->splashRadius &&
05686 ent->nextthink < level.time + 600 &&
05687 ent->count &&
05688 self->client->ps.groundEntityNum != ENTITYNUM_NONE &&
05689 (ent->s.pos.trType == TR_STATIONARY||
05690 ent->s.pos.trType == TR_INTERPOLATE||
05691 (dot1 = DotProduct( dir, forward )) < SABER_REFLECT_MISSILE_CONE||
05692 !WP_ForcePowerUsable( self, FP_PUSH )) )
05693 {
05694
05695 self->client->ps.fd.forceJumpCharge = 480;
05696 }
05697 else if ( self->client->NPC_class != CLASS_BOBAFETT )
05698 {
05699 ForceThrow( self, qfalse );
05700 }
05701 }
05702 continue;
05703 }
05704 else if ( ent->splashDamage && ent->splashRadius )
05705 {
05706
05707
05708
05709 if ( self->s.eType == ET_PLAYER )
05710 {
05711 continue;
05712 }
05713 else
05714 {
05715
05716
05717 if (0)
05718 {
05719 if ( InFOV3( ent->r.currentOrigin, self->client->renderInfo.eyePoint, self->client->ps.viewangles, 90, 90 ) )
05720 {
05721 if ( G_ClearLOS4( self, ent ) )
05722 {
05723 vec3_t throwDir;
05724
05725 ForceThrow( self, qfalse );
05726
05727 ent->s.pos.trType = TR_GRAVITY;
05728 ent->s.eType = ET_MISSILE;
05729 ent->s.eFlags &= ~EF_MISSILE_STICK;
05730 ent->flags |= FL_BOUNCE_HALF;
05731 AngleVectors( ent->r.currentAngles, throwDir, NULL, NULL );
05732 VectorMA( ent->r.currentOrigin, ent->r.maxs[0]+4, throwDir, ent->r.currentOrigin );
05733 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
05734 VectorScale( throwDir, 300, ent->s.pos.trDelta );
05735 ent->s.pos.trDelta[2] += 150;
05736 VectorMA( ent->s.pos.trDelta, 800, dir, ent->s.pos.trDelta );
05737 ent->s.pos.trTime = level.time;
05738 VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
05739 ent->r.ownerNum = self->s.number;
05740
05741 ent->splashDamage /= 3;
05742 ent->splashRadius /= 3;
05743
05744 ent->nextthink = level.time + Q_irand( 500, 3000 );
05745 }
05746 }
05747 }
05748 else if ( dist < ent->splashRadius &&
05749 self->client->ps.groundEntityNum != ENTITYNUM_NONE &&
05750 (DotProduct( dir, forward ) < SABER_REFLECT_MISSILE_CONE||
05751 !WP_ForcePowerUsable( self, FP_PUSH )) )
05752 {
05753 self->client->ps.fd.forceJumpCharge = 480;
05754 }
05755 else if ( self->client->NPC_class != CLASS_BOBAFETT )
05756 {
05757
05758 ForceThrow( self, qfalse );
05759 }
05760 }
05761
05762 continue;
05763 }
05764
05765 if ( ent->s.weapon != WP_SABER )
05766 {
05767 if ( (dot1 = DotProduct( dir, forward )) < SABER_REFLECT_MISSILE_CONE )
05768 continue;
05769 }
05770 else if ( self->s.eType == ET_PLAYER )
05771 {
05772 continue;
05773 }
05774
05775
05776 VectorCopy( ent->s.pos.trDelta, missile_dir );
05777 VectorNormalize( missile_dir );
05778 if ( (dot2 = DotProduct( dir, missile_dir )) > 0 )
05779 continue;
05780
05781
05782 if ( dist < closestDist )
05783 {
05784 VectorCopy( self->r.currentOrigin, traceTo );
05785 traceTo[2] = self->r.absmax[2] - 4;
05786 trap_Trace( &trace, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, traceTo, ent->s.number, ent->clipmask );
05787 if ( trace.allsolid || trace.startsolid || (trace.fraction < 1.0f && trace.entityNum != self->s.number && trace.entityNum != self->client->ps.saberEntityNum) )
05788 {
05789 VectorNormalize2( ent->s.pos.trDelta, entDir );
05790 VectorMA( ent->r.currentOrigin, radius, entDir, traceTo );
05791 trap_Trace( &trace, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, traceTo, ent->s.number, ent->clipmask );
05792 if ( trace.allsolid || trace.startsolid || (trace.fraction < 1.0f && trace.entityNum != self->s.number && trace.entityNum != self->client->ps.saberEntityNum) )
05793 {
05794 continue;
05795 }
05796 }
05797 if ( self->s.eType == ET_NPC )
05798 {
05799 if ( self->NPC && !self->enemy && ent->r.ownerNum != ENTITYNUM_NONE )
05800 {
05801 gentity_t *owner = &g_entities[ent->r.ownerNum];
05802 if ( owner->health >= 0 && (!owner->client || owner->client->playerTeam != self->client->playerTeam) )
05803 {
05804 G_SetEnemy( self, owner );
05805 }
05806 }
05807 }
05808
05809 closestDist = dist;
05810 incoming = ent;
05811 }
05812 }
05813
05814 if (self->s.eType == ET_NPC && self->localAnimIndex <= 1)
05815 {
05816 if (self->client && self->client->renderInfo.lookTarget < ENTITYNUM_WORLD)
05817 {
05818 lookT = &g_entities[self->client->renderInfo.lookTarget];
05819 }
05820 }
05821
05822 if (lookT)
05823 {
05824 if ( !(self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
05825 {
05826 self->client->ps.hasLookTarget = qtrue;
05827 self->client->ps.lookTarget = lookT->s.number;
05828 }
05829 }
05830
05831 if (!doFullRoutine)
05832 {
05833 return;
05834 }
05835
05836 if ( incoming )
05837 {
05838 if ( self->NPC )
05839 {
05840 if ( Jedi_WaitingAmbush( self ) )
05841 {
05842 Jedi_Ambush( self );
05843 }
05844 if ( self->client->NPC_class == CLASS_BOBAFETT
05845 && (self->client->ps.eFlags2&EF2_FLYING)
05846 && incoming->methodOfDeath != MOD_ROCKET_HOMING )
05847 {
05848 if ( !Q_irand( 0, 1 ) )
05849 {
05850 self->NPC->standTime = 0;
05851 self->client->ps.fd.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + Q_irand( 1000, 2000 );
05852 }
05853 if ( !Q_irand( 0, 1 ) )
05854 {
05855 TIMER_Set( self, "heightChange", Q_irand( 1000, 3000 ) );
05856 self->client->ps.fd.forcePowerDebounce[FP_SABER_DEFENSE] = level.time + Q_irand( 1000, 2000 );
05857 }
05858 }
05859 else if ( Jedi_SaberBlockGo( self, &self->NPC->last_ucmd, NULL, NULL, incoming, 0.0f ) != EVASION_NONE )
05860 {
05861 if ( self->client->NPC_class != CLASS_BOBAFETT )
05862 {
05863
05864 WP_ActivateSaber(self);
05865 }
05866 }
05867 }
05868 else
05869 {
05870 gentity_t *owner = &g_entities[incoming->r.ownerNum];
05871
05872 WP_SaberBlockNonRandom( self, incoming->r.currentOrigin, qtrue );
05873 if ( owner && owner->client && (!self->enemy || self->enemy->s.weapon != WP_SABER) )
05874 {
05875 self->enemy = owner;
05876
05877
05878 }
05879 }
05880 }
05881 }
05882
05883 #define MIN_SABER_SLICE_DISTANCE 50
05884
05885 #define MIN_SABER_SLICE_RETURN_DISTANCE 30
05886
05887 #define SABER_THROWN_HIT_DAMAGE 30
05888 #define SABER_THROWN_RETURN_HIT_DAMAGE 5
05889
05890 void thrownSaberTouch (gentity_t *saberent, gentity_t *other, trace_t *trace);
05891
05892 static GAME_INLINE qboolean CheckThrownSaberDamaged(gentity_t *saberent, gentity_t *saberOwner, gentity_t *ent, int dist, int returning, qboolean noDCheck)
05893 {
05894 vec3_t vecsub;
05895 float veclen;
05896 gentity_t *te;
05897
05898 if (saberOwner && saberOwner->client && saberOwner->client->ps.saberAttackWound > level.time)
05899 {
05900 return qfalse;
05901 }
05902
05903 if (ent && ent->client && ent->inuse && ent->s.number != saberOwner->s.number &&
05904 ent->health > 0 && ent->takedamage &&
05905 trap_InPVS(ent->client->ps.origin, saberent->r.currentOrigin) &&
05906 ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
05907 (ent->client->pers.connected || ent->s.eType == ET_NPC))
05908 {
05909 if (ent->inuse && ent->client &&
05910 ent->client->ps.duelInProgress &&
05911 ent->client->ps.duelIndex != saberOwner->s.number)
05912 {
05913 return qfalse;
05914 }
05915
05916 if (ent->inuse && ent->client &&
05917 saberOwner->client->ps.duelInProgress &&
05918 saberOwner->client->ps.duelIndex != ent->s.number)
05919 {
05920 return qfalse;
05921 }
05922
05923 VectorSubtract(saberent->r.currentOrigin, ent->client->ps.origin, vecsub);
05924 veclen = VectorLength(vecsub);
05925
05926 if (veclen < dist)
05927 {
05928 trace_t tr;
05929
05930 trap_Trace(&tr, saberent->r.currentOrigin, NULL, NULL, ent->client->ps.origin, saberent->s.number, MASK_SHOT);
05931
05932 if (tr.fraction == 1 || tr.entityNum == ent->s.number)
05933 {
05934 if (!saberOwner->client->ps.isJediMaster && WP_SaberCanBlock(ent, tr.endpos, 0, MOD_SABER, qfalse, 999))
05935 {
05936 WP_SaberBlockNonRandom(ent, tr.endpos, qfalse);
05937
05938 te = G_TempEntity( tr.endpos, EV_SABER_BLOCK );
05939 VectorCopy(tr.endpos, te->s.origin);
05940 VectorCopy(tr.plane.normal, te->s.angles);
05941 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
05942 {
05943 te->s.angles[1] = 1;
05944 }
05945 te->s.eventParm = 1;
05946 te->s.weapon = 0;
05947 te->s.legsAnim = 0;
05948
05949 if (saberCheckKnockdown_Thrown(saberent, saberOwner, &g_entities[tr.entityNum]))
05950 {
05951 return qfalse;
05952 }
05953
05954 if (!returning)
05955 {
05956 thrownSaberTouch(saberent, saberent, NULL);
05957 }
05958
05959 saberOwner->client->ps.saberAttackWound = level.time + 500;
05960 return qfalse;
05961 }
05962 else
05963 {
05964 vec3_t dir;
05965 int dflags = 0;
05966
05967 VectorSubtract(tr.endpos, saberent->r.currentOrigin, dir);
05968 VectorNormalize(dir);
05969
05970 if (!dir[0] && !dir[1] && !dir[2])
05971 {
05972 dir[1] = 1;
05973 }
05974
05975 if ( (saberOwner->client->saber[0].saberFlags2&SFL2_NO_DISMEMBERMENT) )
05976 {
05977 dflags |= DAMAGE_NO_DISMEMBER;
05978 }
05979
05980 if ( saberOwner->client->saber[0].knockbackScale > 0.0f )
05981 {
05982 dflags |= DAMAGE_SABER_KNOCKBACK1;
05983 }
05984
05985 if (saberOwner->client->ps.isJediMaster)
05986 {
05987 G_Damage(ent, saberOwner, saberOwner, dir, tr.endpos, saberent->damage*2, dflags, MOD_SABER);
05988 }
05989 else
05990 {
05991 G_Damage(ent, saberOwner, saberOwner, dir, tr.endpos, saberent->damage, dflags, MOD_SABER);
05992 }
05993
05994 te = G_TempEntity( tr.endpos, EV_SABER_HIT );
05995 te->s.otherEntityNum = ent->s.number;
05996 te->s.otherEntityNum2 = saberOwner->s.number;
05997 te->s.weapon = 0;
05998 te->s.legsAnim = 0;
05999 VectorCopy(tr.endpos, te->s.origin);
06000 VectorCopy(tr.plane.normal, te->s.angles);
06001 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
06002 {
06003 te->s.angles[1] = 1;
06004 }
06005
06006 te->s.eventParm = 1;
06007
06008 if (!returning)
06009 {
06010 thrownSaberTouch(saberent, saberent, NULL);
06011 }
06012 }
06013
06014 saberOwner->client->ps.saberAttackWound = level.time + 500;
06015 }
06016 }
06017 }
06018 else if (ent && !ent->client && ent->inuse && ent->takedamage && ent->health > 0 && ent->s.number != saberOwner->s.number &&
06019 ent->s.number != saberent->s.number && (noDCheck ||trap_InPVS(ent->r.currentOrigin, saberent->r.currentOrigin)))
06020 {
06021
06022 if (noDCheck)
06023 {
06024 veclen = 0;
06025 }
06026 else
06027 {
06028 VectorSubtract(saberent->r.currentOrigin, ent->r.currentOrigin, vecsub);
06029 veclen = VectorLength(vecsub);
06030 }
06031
06032 if (veclen < dist)
06033 {
06034 trace_t tr;
06035 vec3_t entOrigin;
06036
06037 if (ent->s.eType == ET_MOVER)
06038 {
06039 VectorSubtract( ent->r.absmax, ent->r.absmin, entOrigin );
06040 VectorMA( ent->r.absmin, 0.5, entOrigin, entOrigin );
06041 VectorAdd( ent->r.absmin, ent->r.absmax, entOrigin );
06042 VectorScale( entOrigin, 0.5f, entOrigin );
06043 }
06044 else
06045 {
06046 VectorCopy(ent->r.currentOrigin, entOrigin);
06047 }
06048
06049 trap_Trace(&tr, saberent->r.currentOrigin, NULL, NULL, entOrigin, saberent->s.number, MASK_SHOT);
06050
06051 if (tr.fraction == 1 || tr.entityNum == ent->s.number)
06052 {
06053 vec3_t dir;
06054 int dflags = 0;
06055
06056 VectorSubtract(tr.endpos, entOrigin, dir);
06057 VectorNormalize(dir);
06058
06059 if ( (saberOwner->client->saber[0].saberFlags2&SFL2_NO_DISMEMBERMENT) )
06060 {
06061 dflags |= DAMAGE_NO_DISMEMBER;
06062 }
06063 if ( saberOwner->client->saber[0].knockbackScale > 0.0f )
06064 {
06065 dflags |= DAMAGE_SABER_KNOCKBACK1;
06066 }
06067
06068 if (ent->s.eType == ET_NPC)
06069 {
06070 G_Damage(ent, saberOwner, saberOwner, dir, tr.endpos, 40, dflags, MOD_SABER);
06071 }
06072 else
06073 {
06074 G_Damage(ent, saberOwner, saberOwner, dir, tr.endpos, 5, dflags, MOD_SABER);
06075 }
06076
06077 te = G_TempEntity( tr.endpos, EV_SABER_HIT );
06078 te->s.otherEntityNum = ENTITYNUM_NONE;
06079
06080 te->s.otherEntityNum2 = saberOwner->s.number;
06081 te->s.weapon = 0;
06082 te->s.legsAnim = 0;
06083 VectorCopy(tr.endpos, te->s.origin);
06084 VectorCopy(tr.plane.normal, te->s.angles);
06085 if (!te->s.angles[0] && !te->s.angles[1] && !te->s.angles[2])
06086 {
06087 te->s.angles[1] = 1;
06088 }
06089
06090 if ( ent->s.eType == ET_MOVER )
06091 {
06092 if ( saberOwner
06093 && saberOwner->client
06094 && (saberOwner->client->saber[0].saberFlags2&SFL2_NO_CLASH_FLARE) )
06095 {
06096 G_FreeEntity( te );
06097 }
06098 else
06099 {
06100
06101 gentity_t *teS = G_TempEntity( te->s.origin, EV_SABER_CLASHFLARE );
06102 VectorCopy(te->s.origin, teS->s.origin);
06103
06104 te->s.eventParm = 0;
06105 }
06106 }
06107 else
06108 {
06109 te->s.eventParm = 1;
06110 }
06111
06112 if (!returning)
06113 {
06114 thrownSaberTouch(saberent, saberent, NULL);
06115 }
06116
06117 saberOwner->client->ps.saberAttackWound = level.time + 500;
06118 }
06119 }
06120 }
06121
06122 return qtrue;
06123 }
06124
06125 static GAME_INLINE void saberCheckRadiusDamage(gentity_t *saberent, int returning)
06126 {
06127 int i = 0;
06128 int dist = 0;
06129 gentity_t *ent;
06130 gentity_t *saberOwner = &g_entities[saberent->r.ownerNum];
06131
06132 if (returning && returning != 2)
06133 {
06134 dist = MIN_SABER_SLICE_RETURN_DISTANCE;
06135 }
06136 else
06137 {
06138 dist = MIN_SABER_SLICE_DISTANCE;
06139 }
06140
06141 if (!saberOwner || !saberOwner->client)
06142 {
06143 return;
06144 }
06145
06146 if (saberOwner->client->ps.saberAttackWound > level.time)
06147 {
06148 return;
06149 }
06150
06151 while (i < level.num_entities)
06152 {
06153 ent = &g_entities[i];
06154
06155 CheckThrownSaberDamaged(saberent, saberOwner, ent, dist, returning, qfalse);
06156
06157 i++;
06158 }
06159 }
06160
06161 #define THROWN_SABER_COMP
06162
06163 static GAME_INLINE void saberMoveBack( gentity_t *ent, qboolean goingBack )
06164 {
06165 vec3_t origin, oldOrg;
06166
06167 ent->s.pos.trType = TR_LINEAR;
06168
06169 VectorCopy( ent->r.currentOrigin, oldOrg );
06170
06171 BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
06172
06173 BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles );
06174
06175
06176 #ifdef THROWN_SABER_COMP
06177 if (!goingBack && ent->s.pos.trType != TR_GRAVITY)
06178 {
06179 float originalLength = 0;
06180 int iCompensationLength = 32;
06181 trace_t tr;
06182 vec3_t mins, maxs;
06183 vec3_t calcComp, compensatedOrigin;
06184 VectorSet( mins, -24.0f, -24.0f, -8.0f );
06185 VectorSet( maxs, 24.0f, 24.0f, 8.0f );
06186
06187 VectorSubtract(origin, oldOrg, calcComp);
06188 originalLength = VectorLength(calcComp);
06189
06190 VectorNormalize(calcComp);
06191
06192 compensatedOrigin[0] = oldOrg[0] + calcComp[0]*(originalLength+iCompensationLength);
06193 compensatedOrigin[1] = oldOrg[1] + calcComp[1]*(originalLength+iCompensationLength);
06194 compensatedOrigin[2] = oldOrg[2] + calcComp[2]*(originalLength+iCompensationLength);
06195
06196 trap_Trace(&tr, oldOrg, mins, maxs, compensatedOrigin, ent->r.ownerNum, MASK_PLAYERSOLID);
06197
06198 if ((tr.fraction != 1 || tr.startsolid || tr.allsolid) && tr.entityNum != ent->r.ownerNum && !(g_entities[tr.entityNum].r.contents & CONTENTS_LIGHTSABER))
06199 {
06200 VectorClear(ent->s.pos.trDelta);
06201
06202
06203
06204
06205
06206 CheckThrownSaberDamaged(ent, &g_entities[ent->r.ownerNum], &g_entities[tr.entityNum], 256, 0, qtrue);
06207
06208 if (ent->s.pos.trType == TR_GRAVITY)
06209 {
06210 return;
06211 }
06212
06213 tr.startsolid = 0;
06214 if (tr.entityNum == ENTITYNUM_NONE)
06215 {
06216 tr.entityNum = ENTITYNUM_WORLD;
06217 }
06218 thrownSaberTouch(ent, &g_entities[tr.entityNum], &tr);
06219 return;
06220 }
06221 }
06222 #endif
06223
06224 VectorCopy( origin, ent->r.currentOrigin );
06225 }
06226
06227 void SaberBounceSound( gentity_t *self, gentity_t *other, trace_t *trace )
06228 {
06229 VectorCopy(self->r.currentAngles, self->s.apos.trBase);
06230 self->s.apos.trBase[PITCH] = 90;
06231 }
06232
06233 void DeadSaberThink(gentity_t *saberent)
06234 {
06235 if (saberent->speed < level.time)
06236 {
06237 saberent->think = G_FreeEntity;
06238 saberent->nextthink = level.time;
06239 return;
06240 }
06241
06242 G_RunObject(saberent);
06243 }
06244
06245 void MakeDeadSaber(gentity_t *ent)
06246 {
06247
06248 vec3_t startorg;
06249 vec3_t startang;
06250 gentity_t *saberent;
06251 gentity_t *owner = NULL;
06252
06253 if (g_gametype.integer == GT_JEDIMASTER)
06254 {
06255
06256 return;
06257 }
06258
06259 saberent = G_Spawn();
06260
06261 VectorCopy(ent->r.currentOrigin, startorg);
06262 VectorCopy(ent->r.currentAngles, startang);
06263
06264 saberent->classname = "deadsaber";
06265
06266 saberent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
06267 saberent->r.ownerNum = ent->s.number;
06268
06269 saberent->clipmask = MASK_PLAYERSOLID;
06270 saberent->r.contents = CONTENTS_TRIGGER;
06271
06272 VectorSet( saberent->r.mins, -3.0f, -3.0f, -1.5f );
06273 VectorSet( saberent->r.maxs, 3.0f, 3.0f, 1.5f );
06274
06275 saberent->touch = SaberBounceSound;
06276
06277 saberent->think = DeadSaberThink;
06278 saberent->nextthink = level.time;
06279
06280 VectorCopy(startorg, saberent->s.pos.trBase);
06281 VectorCopy(startang, saberent->s.apos.trBase);
06282
06283 VectorCopy(startorg, saberent->s.origin);
06284 VectorCopy(startang, saberent->s.angles);
06285
06286 VectorCopy(startorg, saberent->r.currentOrigin);
06287 VectorCopy(startang, saberent->r.currentAngles);
06288
06289 saberent->s.apos.trType = TR_GRAVITY;
06290 saberent->s.apos.trDelta[0] = Q_irand(200, 800);
06291 saberent->s.apos.trDelta[1] = Q_irand(200, 800);
06292 saberent->s.apos.trDelta[2] = Q_irand(200, 800);
06293 saberent->s.apos.trTime = level.time-50;
06294
06295 saberent->s.pos.trType = TR_GRAVITY;
06296 saberent->s.pos.trTime = level.time-50;
06297 saberent->flags = FL_BOUNCE_HALF;
06298 if (ent->r.ownerNum >= 0 && ent->r.ownerNum < ENTITYNUM_WORLD)
06299 {
06300 owner = &g_entities[ent->r.ownerNum];
06301
06302 if (owner->inuse && owner->client &&
06303 owner->client->saber[0].model[0])
06304 {
06305 WP_SaberAddG2Model( saberent, owner->client->saber[0].model, owner->client->saber[0].skin );
06306 }
06307 else
06308 {
06309
06310
06311 G_FreeEntity(saberent);
06312 return;
06313 }
06314 }
06315
06316 saberent->s.modelGhoul2 = 1;
06317 saberent->s.g2radius = 20;
06318
06319 saberent->s.eType = ET_MISSILE;
06320 saberent->s.weapon = WP_SABER;
06321
06322 saberent->speed = level.time + 4000;
06323
06324 saberent->bounceCount = 12;
06325
06326
06327 VectorCopy(ent->s.pos.trDelta, saberent->s.pos.trDelta);
06328
06329 saberMoveBack(saberent, qtrue);
06330 saberent->s.pos.trType = TR_GRAVITY;
06331
06332 trap_LinkEntity(saberent);
06333 }
06334
06335 #define MAX_LEAVE_TIME 20000
06336
06337 void saberReactivate(gentity_t *saberent, gentity_t *saberOwner);
06338 void saberBackToOwner(gentity_t *saberent);
06339
06340 void DownedSaberThink(gentity_t *saberent)
06341 {
06342 gentity_t *saberOwn = NULL;
06343 qboolean notDisowned = qfalse;
06344 qboolean pullBack = qfalse;
06345
06346 saberent->nextthink = level.time;
06347
06348 if (saberent->r.ownerNum == ENTITYNUM_NONE)
06349 {
06350 MakeDeadSaber(saberent);
06351
06352 saberent->think = G_FreeEntity;
06353 saberent->nextthink = level.time;
06354 return;
06355 }
06356
06357 saberOwn = &g_entities[saberent->r.ownerNum];
06358
06359 if (!saberOwn ||
06360 !saberOwn->inuse ||
06361 !saberOwn->client ||
06362 saberOwn->client->sess.sessionTeam == TEAM_SPECTATOR ||
06363 (saberOwn->client->ps.pm_flags & PMF_FOLLOW))
06364 {
06365 MakeDeadSaber(saberent);
06366
06367 saberent->think = G_FreeEntity;
06368 saberent->nextthink = level.time;
06369 return;
06370 }
06371
06372 if (saberOwn->client->ps.saberEntityNum)
06373 {
06374 if (saberOwn->client->ps.saberEntityNum == saberent->s.number)
06375 {
06376 notDisowned = qtrue;
06377 }
06378 else
06379 {
06380 assert(!"ULTRA BAD THING");
06381 MakeDeadSaber(saberent);
06382
06383 saberent->think = G_FreeEntity;
06384 saberent->nextthink = level.time;
06385 return;
06386 }
06387 }
06388
06389 if (notDisowned || saberOwn->health < 1 || !saberOwn->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
06390 {
06391 saberOwn->client->ps.saberEntityNum = saberOwn->client->saberStoredIndex;
06392
06393
06394
06395
06396 #ifdef _DEBUG
06397 if (saberOwn->client->saberStoredIndex != saberent->s.number)
06398 {
06399 assert(!"Bad saber index!!!");
06400 }
06401 #endif
06402
06403 saberReactivate(saberent, saberOwn);
06404
06405 if (saberOwn->health < 1)
06406 {
06407 saberOwn->client->ps.saberInFlight = qfalse;
06408 MakeDeadSaber(saberent);
06409 }
06410
06411 saberent->touch = SaberGotHit;
06412 saberent->think = SaberUpdateSelf;
06413 saberent->genericValue5 = 0;
06414 saberent->nextthink = level.time;
06415
06416 saberent->r.svFlags |= (SVF_NOCLIENT);
06417
06418 saberent->s.loopSound = 0;
06419 saberent->s.loopIsSoundset = qfalse;
06420
06421 if (saberOwn->health > 0)
06422 {
06423 saberOwn->client->ps.saberInFlight = qfalse;
06424 WP_SaberRemoveG2Model( saberent );
06425 }
06426 saberOwn->client->ps.saberEntityState = 0;
06427 saberOwn->client->ps.saberThrowDelay = level.time + 500;
06428 saberOwn->client->ps.saberCanThrow = qfalse;
06429
06430 return;
06431 }
06432
06433 if (saberOwn->client->saberKnockedTime < level.time && (saberOwn->client->pers.cmd.buttons & BUTTON_ATTACK))
06434 {
06435 pullBack = qtrue;
06436 }
06437 else if ((level.time - saberOwn->client->saberKnockedTime) > MAX_LEAVE_TIME)
06438 {
06439 pullBack = qtrue;
06440 }
06441
06442 if (pullBack)
06443 {
06444 saberOwn->client->ps.saberEntityNum = saberOwn->client->saberStoredIndex;
06445
06446 #ifdef _DEBUG
06447 if (saberOwn->client->saberStoredIndex != saberent->s.number)
06448 {
06449 assert(!"Bad saber index!!!");
06450 }
06451 #endif
06452 saberReactivate(saberent, saberOwn);
06453
06454 saberent->touch = SaberGotHit;
06455
06456 saberent->think = saberBackToOwner;
06457 saberent->speed = 0;
06458 saberent->genericValue5 = 0;
06459 saberent->nextthink = level.time;
06460
06461 saberent->r.contents = CONTENTS_LIGHTSABER;
06462
06463 G_Sound( saberOwn, CHAN_BODY, G_SoundIndex( "sound/weapons/force/pull.wav" ) );
06464 if (saberOwn->client->saber[0].soundOn)
06465 {
06466 G_Sound( saberent, CHAN_BODY, saberOwn->client->saber[0].soundOn );
06467 }
06468 if (saberOwn->client->saber[1].soundOn)
06469 {
06470 G_Sound( saberOwn, CHAN_BODY, saberOwn->client->saber[1].soundOn );
06471 }
06472
06473 return;
06474 }
06475
06476 G_RunObject(saberent);
06477 saberent->nextthink = level.time;
06478 }
06479
06480 void saberReactivate(gentity_t *saberent, gentity_t *saberOwner)
06481 {
06482 saberent->s.saberInFlight = qtrue;
06483
06484 saberent->s.apos.trType = TR_LINEAR;
06485 saberent->s.apos.trDelta[0] = 0;
06486 saberent->s.apos.trDelta[1] = 800;
06487 saberent->s.apos.trDelta[2] = 0;
06488
06489 saberent->s.pos.trType = TR_LINEAR;
06490 saberent->s.eType = ET_GENERAL;
06491 saberent->s.eFlags = 0;
06492
06493 saberent->parent = saberOwner;
06494
06495 saberent->genericValue5 = 0;
06496
06497 SetSaberBoxSize(saberent);
06498
06499 saberent->touch = thrownSaberTouch;
06500
06501 saberent->s.weapon = WP_SABER;
06502
06503 saberOwner->client->ps.saberEntityState = 1;
06504
06505 trap_LinkEntity(saberent);
06506 }
06507
06508 #define SABER_RETRIEVE_DELAY 3000 //3 seconds for now. This will leave you nice and open if you lose your saber.
06509
06510 void saberKnockDown(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other)
06511 {
06512 saberOwner->client->ps.saberEntityNum = 0;
06513 saberOwner->client->saberKnockedTime = level.time + SABER_RETRIEVE_DELAY;
06514
06515 saberent->clipmask = MASK_SOLID;
06516 saberent->r.contents = CONTENTS_TRIGGER;
06517
06518 VectorSet( saberent->r.mins, -3.0f, -3.0f, -1.5f );
06519 VectorSet( saberent->r.maxs, 3.0f, 3.0f, 1.5f );
06520
06521 saberent->s.apos.trType = TR_GRAVITY;
06522 saberent->s.apos.trDelta[0] = Q_irand(200, 800);
06523 saberent->s.apos.trDelta[1] = Q_irand(200, 800);
06524 saberent->s.apos.trDelta[2] = Q_irand(200, 800);
06525 saberent->s.apos.trTime = level.time-50;
06526
06527 saberent->s.pos.trType = TR_GRAVITY;
06528 saberent->s.pos.trTime = level.time-50;
06529 saberent->flags |= FL_BOUNCE_HALF;
06530
06531 WP_SaberAddG2Model( saberent, saberOwner->client->saber[0].model, saberOwner->client->saber[0].skin );
06532
06533 saberent->s.modelGhoul2 = 1;
06534 saberent->s.g2radius = 20;
06535
06536 saberent->s.eType = ET_MISSILE;
06537 saberent->s.weapon = WP_SABER;
06538
06539 saberent->speed = level.time + 4000;
06540
06541 saberent->bounceCount = -5;
06542
06543 saberMoveBack(saberent, qtrue);
06544 saberent->s.pos.trType = TR_GRAVITY;
06545
06546 saberent->s.loopSound = 0;
06547 saberent->s.loopIsSoundset = qfalse;
06548
06549 saberent->r.svFlags &= ~(SVF_NOCLIENT);
06550
06551 saberent->touch = SaberBounceSound;
06552 saberent->think = DownedSaberThink;
06553 saberent->nextthink = level.time;
06554
06555 if (saberOwner != other)
06556 {
06557 if (other->inuse && other->client)
06558 {
06559 vec3_t otherFwd;
06560 float deflectSpeed = 200;
06561
06562 AngleVectors(other->client->ps.viewangles, otherFwd, 0, 0);
06563
06564 saberent->s.pos.trDelta[0] = otherFwd[0]*deflectSpeed;
06565 saberent->s.pos.trDelta[1] = otherFwd[1]*deflectSpeed;
06566 saberent->s.pos.trDelta[2] = otherFwd[2]*deflectSpeed;
06567 }
06568 }
06569
06570 trap_LinkEntity(saberent);
06571
06572 if (saberOwner->client->saber[0].soundOff)
06573 {
06574 G_Sound( saberent, CHAN_BODY, saberOwner->client->saber[0].soundOff );
06575 }
06576
06577 if (saberOwner->client->saber[1].soundOff &&
06578 saberOwner->client->saber[1].model[0])
06579 {
06580 G_Sound( saberOwner, CHAN_BODY, saberOwner->client->saber[1].soundOff );
06581 }
06582 }
06583
06584
06585 #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))
06586
06587 void WP_SaberRemoveG2Model( gentity_t *saberent )
06588 {
06589 if ( saberent->ghoul2 )
06590 {
06591 trap_G2API_RemoveGhoul2Models( &saberent->ghoul2 );
06592 }
06593 }
06594
06595 void WP_SaberAddG2Model( gentity_t *saberent, const char *saberModel, qhandle_t saberSkin )
06596 {
06597 WP_SaberRemoveG2Model( saberent );
06598 if ( saberModel && saberModel[0] )
06599 {
06600 saberent->s.modelindex = G_ModelIndex(saberModel);
06601 }
06602 else
06603 {
06604 saberent->s.modelindex = G_ModelIndex( "models/weapons2/saber/saber_w.glm" );
06605 }
06606
06607 trap_G2API_InitGhoul2Model( &saberent->ghoul2, saberModel, saberent->s.modelindex, saberSkin, 0, 0, 0 );
06608 }
06609
06610
06611 qboolean saberKnockOutOfHand(gentity_t *saberent, gentity_t *saberOwner, vec3_t velocity)
06612 {
06613 if (!saberent || !saberOwner ||
06614 !saberent->inuse || !saberOwner->inuse ||
06615 !saberOwner->client)
06616 {
06617 return qfalse;
06618 }
06619
06620 if (!saberOwner->client->ps.saberEntityNum)
06621 {
06622 return qfalse;
06623 }
06624
06625 if ((level.time - saberOwner->client->lastSaberStorageTime) > 50)
06626 {
06627 return qfalse;
06628 }
06629
06630 if (saberOwner->client->ps.saberLockTime > (level.time-100))
06631 {
06632 return qfalse;
06633 }
06634 if ( (saberOwner->client->saber[0].saberFlags&SFL_NOT_DISARMABLE) )
06635 {
06636 return qfalse;
06637 }
06638
06639 saberOwner->client->ps.saberInFlight = qtrue;
06640 saberOwner->client->ps.saberEntityState = 1;
06641
06642 saberent->s.saberInFlight = qfalse;
06643
06644 saberent->s.pos.trType = TR_LINEAR;
06645 saberent->s.eType = ET_GENERAL;
06646 saberent->s.eFlags = 0;
06647
06648 WP_SaberAddG2Model( saberent, saberOwner->client->saber[0].model, saberOwner->client->saber[0].skin );
06649
06650 saberent->s.modelGhoul2 = 127;
06651
06652 saberent->parent = saberOwner;
06653
06654 saberent->damage = SABER_THROWN_HIT_DAMAGE;
06655 saberent->methodOfDeath = MOD_SABER;
06656 saberent->splashMethodOfDeath = MOD_SABER;
06657 saberent->s.solid = 2;
06658 saberent->r.contents = CONTENTS_LIGHTSABER;
06659
06660 saberent->genericValue5 = 0;
06661
06662 VectorSet( saberent->r.mins, -24.0f, -24.0f, -8.0f );
06663 VectorSet( saberent->r.maxs, 24.0f, 24.0f, 8.0f );
06664
06665 saberent->s.genericenemyindex = saberOwner->s.number+1024;
06666 saberent->s.weapon = WP_SABER;
06667
06668 saberent->genericValue5 = 0;
06669
06670 G_SetOrigin(saberent, saberOwner->client->lastSaberBase_Always);
06671
06672 saberKnockDown(saberent, saberOwner, saberOwner);
06673 VectorCopy(velocity, saberent->s.pos.trDelta);
06674
06675 return qtrue;
06676 }
06677
06678
06679 qboolean saberCheckKnockdown_DuelLoss(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other)
06680 {
06681 vec3_t dif;
06682 float totalDistance = 1;
06683 float distScale = 6.5f;
06684 qboolean validMomentum = qtrue;
06685 int disarmChance = 1;
06686
06687 if (SABERINVALID)
06688 {
06689 return qfalse;
06690 }
06691
06692 VectorClear(dif);
06693
06694 if (!other->client->olderIsValid || (level.time - other->client->lastSaberStorageTime) >= 200)
06695 {
06696 validMomentum = qfalse;
06697 }
06698
06699 if (validMomentum)
06700 {
06701
06702 VectorSubtract(other->client->lastSaberBase_Always, other->client->olderSaberBase, dif);
06703 totalDistance = VectorNormalize(dif);
06704
06705 if (!totalDistance)
06706 {
06707 if (!saberOwner->client->olderIsValid || (level.time - saberOwner->client->lastSaberStorageTime) >= 200)
06708 {
06709 validMomentum = qfalse;
06710 }
06711
06712 if (validMomentum)
06713 {
06714 VectorSubtract(saberOwner->client->lastSaberBase_Always, saberOwner->client->olderSaberBase, dif);
06715 totalDistance = VectorNormalize(dif);
06716 }
06717 }
06718
06719 if (validMomentum)
06720 {
06721 if (!totalDistance)
06722 {
06723 VectorSubtract(saberOwner->client->lastSaberBase_Always, other->client->lastSaberBase_Always, dif);
06724 totalDistance = VectorNormalize(dif);
06725 }
06726
06727 if (totalDistance)
06728 {
06729 if (totalDistance < 20)
06730 {
06731 totalDistance = 20;
06732 }
06733 VectorScale(dif, totalDistance*distScale, dif);
06734 }
06735 }
06736 }
06737
06738 saberOwner->client->ps.saberMove = LS_V1_BL;
06739 saberOwner->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
06740
06741 if ( other && other->client )
06742 {
06743 disarmChance += other->client->saber[0].disarmBonus;
06744 if ( other->client->saber[1].model
06745 && other->client->saber[1].model[0]
06746 && !other->client->ps.saberHolstered )
06747 {
06748 other->client->saber[1].disarmBonus;
06749 }
06750 }
06751 if ( Q_irand( 0, disarmChance ) )
06752 {
06753 return saberKnockOutOfHand(saberent, saberOwner, dif);
06754 }
06755 else
06756 {
06757 return qfalse;
06758 }
06759 }
06760
06761
06762
06763 qboolean saberCheckKnockdown_BrokenParry(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other)
06764 {
06765 int myAttack;
06766 int otherAttack;
06767 qboolean doKnock = qfalse;
06768 int disarmChance = 1;
06769
06770 if (SABERINVALID)
06771 {
06772 return qfalse;
06773 }
06774
06775
06776
06777 myAttack = G_SaberAttackPower(saberOwner, qfalse);
06778 otherAttack = G_SaberAttackPower(other, qfalse);
06779
06780 if (!other->client->olderIsValid || (level.time - other->client->lastSaberStorageTime) >= 200)
06781 {
06782 return qfalse;
06783 }
06784
06785
06786 if (otherAttack > myAttack+1 && Q_irand(1, 10) <= 7)
06787 {
06788 doKnock = qtrue;
06789 }
06790 else if (otherAttack > myAttack && Q_irand(1, 10) <= 3)
06791 {
06792 doKnock = qtrue;
06793 }
06794
06795 if (doKnock)
06796 {
06797 vec3_t dif;
06798 float totalDistance;
06799 float distScale = 6.5f;
06800
06801 VectorSubtract(other->client->lastSaberBase_Always, other->client->olderSaberBase, dif);
06802 totalDistance = VectorNormalize(dif);
06803
06804 if (!totalDistance)
06805 {
06806 if (!saberOwner->client->olderIsValid || (level.time - saberOwner->client->lastSaberStorageTime) >= 200)
06807 {
06808 return qfalse;
06809 }
06810
06811 VectorSubtract(saberOwner->client->lastSaberBase_Always, saberOwner->client->olderSaberBase, dif);
06812 totalDistance = VectorNormalize(dif);
06813 }
06814
06815 if (!totalDistance)
06816 {
06817 return qfalse;
06818 }
06819
06820 if (totalDistance < 20)
06821 {
06822 totalDistance = 20;
06823 }
06824 VectorScale(dif, totalDistance*distScale, dif);
06825
06826 if ( other && other->client )
06827 {
06828 disarmChance += other->client->saber[0].disarmBonus;
06829 if ( other->client->saber[1].model
06830 && other->client->saber[1].model[0]
06831 && !other->client->ps.saberHolstered )
06832 {
06833 other->client->saber[1].disarmBonus;
06834 }
06835 }
06836 if ( Q_irand( 0, disarmChance ) )
06837 {
06838 return saberKnockOutOfHand(saberent, saberOwner, dif);
06839 }
06840 }
06841
06842 return qfalse;
06843 }
06844
06845 #include "../namespace_begin.h"
06846 qboolean BG_InExtraDefenseSaberMove( int move );
06847 #include "../namespace_end.h"
06848
06849
06850 qboolean saberCheckKnockdown_Smashed(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other, int damage)
06851 {
06852 if (SABERINVALID)
06853 {
06854 return qfalse;
06855 }
06856
06857 if (!saberOwner->client->ps.saberInFlight)
06858 {
06859 return qfalse;
06860 }
06861
06862 if ( other
06863 && other->inuse
06864 && other->client
06865 && BG_InExtraDefenseSaberMove( other->client->ps.saberMove ) )
06866 {
06867 saberKnockDown(saberent, saberOwner, other);
06868 return qtrue;
06869 }
06870
06871 if (damage > 10)
06872 {
06873 saberKnockDown(saberent, saberOwner, other);
06874 return qtrue;
06875 }
06876
06877 return qfalse;
06878 }
06879
06880
06881
06882 qboolean saberCheckKnockdown_Thrown(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other)
06883 {
06884 int throwLevel = 0;
06885 int defenLevel = 0;
06886 qboolean tossIt = qfalse;
06887
06888 if (SABERINVALID)
06889 {
06890 return qfalse;
06891 }
06892
06893 defenLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE];
06894 throwLevel = saberOwner->client->ps.fd.forcePowerLevel[FP_SABERTHROW];
06895
06896 if (defenLevel > throwLevel)
06897 {
06898 tossIt = qtrue;
06899 }
06900 else if (defenLevel == throwLevel && Q_irand(1, 10) <= 4)
06901 {
06902 tossIt = qtrue;
06903 }
06904
06905
06906 if (tossIt)
06907 {
06908 saberKnockDown(saberent, saberOwner, other);
06909 return qtrue;
06910 }
06911
06912 return qfalse;
06913 }
06914
06915 void saberBackToOwner(gentity_t *saberent)
06916 {
06917 gentity_t *saberOwner = &g_entities[saberent->r.ownerNum];
06918 vec3_t dir;
06919 float ownerLen;
06920
06921 if (saberent->r.ownerNum == ENTITYNUM_NONE)
06922 {
06923 MakeDeadSaber(saberent);
06924
06925 saberent->think = G_FreeEntity;
06926 saberent->nextthink = level.time;
06927 return;
06928 }
06929
06930 if (!saberOwner->inuse ||
06931 !saberOwner->client ||
06932 saberOwner->client->sess.sessionTeam == TEAM_SPECTATOR)
06933 {
06934 MakeDeadSaber(saberent);
06935
06936 saberent->think = G_FreeEntity;
06937 saberent->nextthink = level.time;
06938 return;
06939 }
06940
06941 if (saberOwner->health < 1 || !saberOwner->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
06942 {
06943 saberent->touch = SaberGotHit;
06944 saberent->think = SaberUpdateSelf;
06945 saberent->genericValue5 = 0;
06946 saberent->nextthink = level.time;
06947
06948 if (saberOwner->client &&
06949 saberOwner->client->saber[0].soundOff)
06950 {
06951 G_Sound(saberent, CHAN_AUTO, saberOwner->client->saber[0].soundOff);
06952 }
06953 MakeDeadSaber(saberent);
06954
06955 saberent->r.svFlags |= (SVF_NOCLIENT);
06956 saberent->r.contents = CONTENTS_LIGHTSABER;
06957 SetSaberBoxSize(saberent);
06958 saberent->s.loopSound = 0;
06959 saberent->s.loopIsSoundset = qfalse;
06960 WP_SaberRemoveG2Model( saberent );
06961
06962 saberOwner->client->ps.saberInFlight = qfalse;
06963 saberOwner->client->ps.saberEntityState = 0;
06964 saberOwner->client->ps.saberThrowDelay = level.time + 500;
06965 saberOwner->client->ps.saberCanThrow = qfalse;
06966
06967 return;
06968 }
06969
06970
06971 assert(saberOwner->client->ps.saberEntityNum == saberent->s.number ||
06972 saberOwner->client->saberStoredIndex == saberent->s.number);
06973 saberOwner->client->ps.saberEntityNum = saberent->s.number;
06974
06975 saberent->r.contents = CONTENTS_LIGHTSABER;
06976
06977 VectorSubtract(saberent->pos1, saberent->r.currentOrigin, dir);
06978
06979 ownerLen = VectorLength(dir);
06980
06981 if (saberent->speed < level.time)
06982 {
06983 float baseSpeed = 900;
06984
06985 VectorNormalize(dir);
06986
06987 saberMoveBack(saberent, qtrue);
06988 VectorCopy(saberent->r.currentOrigin, saberent->s.pos.trBase);
06989
06990 if (saberOwner->client->ps.fd.forcePowerLevel[FP_SABERTHROW] >= FORCE_LEVEL_3)
06991 {
06992 baseSpeed = 900;
06993
06994 saberent->speed = level.time;
06995 }
06996 else
06997 {
06998 baseSpeed = 700;
06999 saberent->speed = level.time + 50;
07000 }
07001
07002
07003 if (ownerLen < 64)
07004 {
07005 VectorScale(dir, baseSpeed-200, saberent->s.pos.trDelta );
07006 }
07007 else if (ownerLen < 128)
07008 {
07009 VectorScale(dir, baseSpeed-150, saberent->s.pos.trDelta );
07010 }
07011 else if (ownerLen < 256)
07012 {
07013 VectorScale(dir, baseSpeed-100, saberent->s.pos.trDelta );
07014 }
07015 else
07016 {
07017 VectorScale(dir, baseSpeed, saberent->s.pos.trDelta );
07018 }
07019
07020 saberent->s.pos.trTime = level.time;
07021 }
07022
07023
07024
07025
07026
07027
07028
07029
07030
07031
07032 if (saberOwner->client->ps.saberEntityNum == saberent->s.number)
07033 {
07034 if ( !(saberOwner->client->saber[0].saberFlags&SFL_RETURN_DAMAGE)
07035 || saberOwner->client->ps.saberHolstered )
07036 {
07037 saberent->s.saberInFlight = qfalse;
07038 }
07039 saberent->s.loopSound = saberOwner->client->saber[0].soundLoop;
07040 saberent->s.loopIsSoundset = qfalse;
07041
07042 if (ownerLen <= 32)
07043 {
07044 G_Sound( saberent, CHAN_AUTO, G_SoundIndex( "sound/weapons/saber/saber_catch.wav" ) );
07045
07046 saberOwner->client->ps.saberInFlight = qfalse;
07047 saberOwner->client->ps.saberEntityState = 0;
07048 saberOwner->client->ps.saberCanThrow = qfalse;
07049 saberOwner->client->ps.saberThrowDelay = level.time + 300;
07050
07051 saberent->touch = SaberGotHit;
07052
07053 saberent->think = SaberUpdateSelf;
07054 saberent->genericValue5 = 0;
07055 saberent->nextthink = level.time + 50;
07056 WP_SaberRemoveG2Model( saberent );
07057
07058 return;
07059 }
07060
07061 if (!saberent->s.saberInFlight)
07062 {
07063 saberCheckRadiusDamage(saberent, 1);
07064 }
07065 else
07066 {
07067 saberCheckRadiusDamage(saberent, 2);
07068 }
07069
07070 saberMoveBack(saberent, qtrue);
07071 }
07072
07073 saberent->nextthink = level.time;
07074 }
07075
07076 void saberFirstThrown(gentity_t *saberent);
07077
07078 void thrownSaberTouch (gentity_t *saberent, gentity_t *other, trace_t *trace)
07079 {
07080 gentity_t *hitEnt = other;
07081
07082 if (other && other->s.number == saberent->r.ownerNum)
07083 {
07084 return;
07085 }
07086 VectorClear(saberent->s.pos.trDelta);
07087 saberent->s.pos.trTime = level.time;
07088
07089 saberent->s.apos.trType = TR_LINEAR;
07090 saberent->s.apos.trDelta[0] = 0;
07091 saberent->s.apos.trDelta[1] = 800;
07092 saberent->s.apos.trDelta[2] = 0;
07093
07094 VectorCopy(saberent->r.currentOrigin, saberent->s.pos.trBase);
07095
07096 saberent->think = saberBackToOwner;
07097 saberent->nextthink = level.time;
07098
07099 if (other && other->r.ownerNum < MAX_CLIENTS &&
07100 (other->r.contents & CONTENTS_LIGHTSABER) &&
07101 g_entities[other->r.ownerNum].client &&
07102 g_entities[other->r.ownerNum].inuse)
07103 {
07104 hitEnt = &g_entities[other->r.ownerNum];
07105 }
07106
07107
07108 CheckThrownSaberDamaged(saberent, &g_entities[saberent->r.ownerNum], hitEnt, 256, 0, qtrue);
07109
07110 saberent->speed = 0;
07111 }
07112
07113 #define SABER_MAX_THROW_DISTANCE 700
07114
07115 void saberFirstThrown(gentity_t *saberent)
07116 {
07117 vec3_t vSub;
07118 float vLen;
07119 gentity_t *saberOwn = &g_entities[saberent->r.ownerNum];
07120
07121 if (saberent->r.ownerNum == ENTITYNUM_NONE)
07122 {
07123 MakeDeadSaber(saberent);
07124
07125 saberent->think = G_FreeEntity;
07126 saberent->nextthink = level.time;
07127 return;
07128 }
07129
07130 if (!saberOwn ||
07131 !saberOwn->inuse ||
07132 !saberOwn->client ||
07133 saberOwn->client->sess.sessionTeam == TEAM_SPECTATOR)
07134 {
07135 MakeDeadSaber(saberent);
07136
07137 saberent->think = G_FreeEntity;
07138 saberent->nextthink = level.time;
07139 return;
07140 }
07141
07142 if (saberOwn->health < 1 || !saberOwn->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE])
07143 {
07144 saberent->touch = SaberGotHit;
07145 saberent->think = SaberUpdateSelf;
07146 saberent->genericValue5 = 0;
07147 saberent->nextthink = level.time;
07148
07149 if (saberOwn->client &&
07150 saberOwn->client->saber[0].soundOff)
07151 {
07152 G_Sound(saberent, CHAN_AUTO, saberOwn->client->saber[0].soundOff);
07153 }
07154 MakeDeadSaber(saberent);
07155
07156 saberent->r.svFlags |= (SVF_NOCLIENT);
07157 saberent->r.contents = CONTENTS_LIGHTSABER;
07158 SetSaberBoxSize(saberent);
07159 saberent->s.loopSound = 0;
07160 saberent->s.loopIsSoundset = qfalse;
07161 WP_SaberRemoveG2Model( saberent );
07162
07163 saberOwn->client->ps.saberInFlight = qfalse;
07164 saberOwn->client->ps.saberEntityState = 0;
07165 saberOwn->client->ps.saberThrowDelay = level.time + 500;
07166 saberOwn->client->ps.saberCanThrow = qfalse;
07167
07168 return;
07169 }
07170
07171 if ((level.time - saberOwn->client->ps.saberDidThrowTime) > 500)
07172 {
07173 if (!(saberOwn->client->buttons & BUTTON_ALT_ATTACK))
07174 {
07175 thrownSaberTouch(saberent, saberent, NULL);
07176 goto runMin;
07177 }
07178 else if ((level.time - saberOwn->client->ps.saberDidThrowTime) > 6000)
07179 {
07180 thrownSaberTouch(saberent, saberent, NULL);
07181 goto runMin;
07182 }
07183 }
07184
07185 if (BG_HasYsalamiri(g_gametype.integer, &saberOwn->client->ps))
07186 {
07187 thrownSaberTouch(saberent, saberent, NULL);
07188 goto runMin;
07189 }
07190
07191 if (!BG_CanUseFPNow(g_gametype.integer, &saberOwn->client->ps, level.time, FP_SABERTHROW))
07192 {
07193 thrownSaberTouch(saberent, saberent, NULL);
07194 goto runMin;
07195 }
07196
07197 VectorSubtract(saberOwn->client->ps.origin, saberent->r.currentOrigin, vSub);
07198 vLen = VectorLength(vSub);
07199
07200 if (vLen >= (SABER_MAX_THROW_DISTANCE*saberOwn->client->ps.fd.forcePowerLevel[FP_SABERTHROW]))
07201 {
07202 thrownSaberTouch(saberent, saberent, NULL);
07203 goto runMin;
07204 }
07205
07206 if (saberOwn->client->ps.fd.forcePowerLevel[FP_SABERTHROW] >= FORCE_LEVEL_2 &&
07207 saberent->speed < level.time)
07208 {
07209 vec3_t fwd, traceFrom, traceTo, dir;
07210 trace_t tr;
07211
07212 AngleVectors(saberOwn->client->ps.viewangles, fwd, 0, 0);
07213
07214 VectorCopy(saberOwn->client->ps.origin, traceFrom);
07215 traceFrom[2] += saberOwn->client->ps.viewheight;
07216
07217 VectorCopy(traceFrom, traceTo);
07218 traceTo[0] += fwd[0]*4096;
07219 traceTo[1] += fwd[1]*4096;
07220 traceTo[2] += fwd[2]*4096;
07221
07222 saberMoveBack(saberent, qfalse);
07223 VectorCopy(saberent->r.currentOrigin, saberent->s.pos.trBase);
07224
07225 if (saberOwn->client->ps.fd.forcePowerLevel[FP_SABERTHROW] >= FORCE_LEVEL_3)
07226 {
07227 trap_Trace(&tr, traceFrom, NULL, NULL, traceTo, saberOwn->s.number, MASK_PLAYERSOLID);
07228 }
07229 else
07230 {
07231 trap_Trace(&tr, traceFrom, NULL, NULL, traceTo, saberOwn->s.number, MASK_SOLID);
07232 }
07233
07234 VectorSubtract(tr.endpos, saberent->r.currentOrigin, dir);
07235
07236 VectorNormalize(dir);
07237
07238 VectorScale(dir, 500, saberent->s.pos.trDelta );
07239 saberent->s.pos.trTime = level.time;
07240
07241 if (saberOwn->client->ps.fd.forcePowerLevel[FP_SABERTHROW] >= FORCE_LEVEL_3)
07242 {
07243 saberent->speed = level.time + 100;
07244 }
07245 else
07246 {
07247 saberent->speed = level.time + 400;
07248 }
07249 }
07250
07251 runMin:
07252
07253 saberCheckRadiusDamage(saberent, 0);
07254 G_RunObject(saberent);
07255 }
07256
07257 void UpdateClientRenderBolts(gentity_t *self, vec3_t renderOrigin, vec3_t renderAngles)
07258 {
07259 mdxaBone_t boltMatrix;
07260 renderInfo_t *ri = &self->client->renderInfo;
07261
07262 if (!self->ghoul2)
07263 {
07264 VectorCopy(self->client->ps.origin, ri->headPoint);
07265 VectorCopy(self->client->ps.origin, ri->handRPoint);
07266 VectorCopy(self->client->ps.origin, ri->handLPoint);
07267 VectorCopy(self->client->ps.origin, ri->torsoPoint);
07268 VectorCopy(self->client->ps.origin, ri->crotchPoint);
07269 VectorCopy(self->client->ps.origin, ri->footRPoint);
07270 VectorCopy(self->client->ps.origin, ri->footLPoint);
07271 }
07272 else
07273 {
07274
07275 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->headBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07276 ri->headPoint[0] = boltMatrix.matrix[0][3];
07277 ri->headPoint[1] = boltMatrix.matrix[1][3];
07278 ri->headPoint[2] = boltMatrix.matrix[2][3];
07279
07280
07281 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->handRBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07282 ri->handRPoint[0] = boltMatrix.matrix[0][3];
07283 ri->handRPoint[1] = boltMatrix.matrix[1][3];
07284 ri->handRPoint[2] = boltMatrix.matrix[2][3];
07285
07286
07287 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->handLBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07288 ri->handLPoint[0] = boltMatrix.matrix[0][3];
07289 ri->handLPoint[1] = boltMatrix.matrix[1][3];
07290 ri->handLPoint[2] = boltMatrix.matrix[2][3];
07291
07292
07293 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->torsoBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07294 ri->torsoPoint[0] = boltMatrix.matrix[0][3];
07295 ri->torsoPoint[1] = boltMatrix.matrix[1][3];
07296 ri->torsoPoint[2] = boltMatrix.matrix[2][3];
07297
07298
07299 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->crotchBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07300 ri->crotchPoint[0] = boltMatrix.matrix[0][3];
07301 ri->crotchPoint[1] = boltMatrix.matrix[1][3];
07302 ri->crotchPoint[2] = boltMatrix.matrix[2][3];
07303
07304
07305 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->footRBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07306 ri->footRPoint[0] = boltMatrix.matrix[0][3];
07307 ri->footRPoint[1] = boltMatrix.matrix[1][3];
07308 ri->footRPoint[2] = boltMatrix.matrix[2][3];
07309
07310
07311 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->footLBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07312 ri->footLPoint[0] = boltMatrix.matrix[0][3];
07313 ri->footLPoint[1] = boltMatrix.matrix[1][3];
07314 ri->footLPoint[2] = boltMatrix.matrix[2][3];
07315 }
07316
07317 self->client->renderInfo.boltValidityTime = level.time;
07318 }
07319
07320 void UpdateClientRenderinfo(gentity_t *self, vec3_t renderOrigin, vec3_t renderAngles)
07321 {
07322 renderInfo_t *ri = &self->client->renderInfo;
07323 if ( ri->mPCalcTime < level.time )
07324 {
07325
07326
07327
07328 #if 0 //#if 0'd since it's a waste setting all this to 0 each frame.
07329
07330 ri->headYawRangeLeft = ri->headYawRangeRight = ri->headPitchRangeUp = ri->headPitchRangeDown = 0;
07331 ri->torsoYawRangeLeft = ri->torsoYawRangeRight = ri->torsoPitchRangeUp = ri->torsoPitchRangeDown = 0;
07332
07333 ri->torsoFpsMod = ri->legsFpsMod = 0;
07334
07335 VectorClear(ri->customRGB);
07336 ri->customAlpha = 0;
07337 ri->renderFlags = 0;
07338 ri->lockYaw = 0;
07339
07340 VectorClear(ri->headAngles);
07341 VectorClear(ri->torsoAngles);
07342
07343
07344
07345 ri->legsYaw = 0;
07346 #endif
07347
07348 if (self->ghoul2 &&
07349 self->ghoul2 != ri->lastG2)
07350 {
07351
07352 ri->lastG2 = self->ghoul2;
07353
07354 if (self->localAnimIndex <= 1)
07355 {
07356 ri->headBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*head_eyes");
07357 ri->handRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_hand");
07358 ri->handLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_hand");
07359 ri->torsoBolt = trap_G2API_AddBolt(self->ghoul2, 0, "thoracic");
07360 ri->crotchBolt = trap_G2API_AddBolt(self->ghoul2, 0, "pelvis");
07361 ri->footRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_leg_foot");
07362 ri->footLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_leg_foot");
07363 ri->motionBolt = trap_G2API_AddBolt(self->ghoul2, 0, "Motion");
07364 }
07365 else
07366 {
07367 ri->headBolt = -1;
07368 ri->handRBolt = -1;
07369 ri->handLBolt = -1;
07370 ri->torsoBolt = -1;
07371 ri->crotchBolt = -1;
07372 ri->footRBolt = -1;
07373 ri->footLBolt = -1;
07374 ri->motionBolt = -1;
07375 }
07376
07377 ri->lastG2 = self->ghoul2;
07378 }
07379
07380 VectorCopy( self->client->ps.viewangles, self->client->renderInfo.eyeAngles );
07381
07382
07383 ri->torsoFrame = bgAllAnims[self->localAnimIndex].anims[self->client->ps.torsoAnim].firstFrame;
07384 ri->legsFrame = bgAllAnims[self->localAnimIndex].anims[self->client->ps.legsAnim].firstFrame;
07385 if (g_debugServerSkel.integer)
07386 {
07387
07388
07389
07390 mdxaBone_t boltMatrix;
07391
07392 if (!self->ghoul2)
07393 {
07394 VectorCopy(self->client->ps.origin, ri->headPoint);
07395 VectorCopy(self->client->ps.origin, ri->handRPoint);
07396 VectorCopy(self->client->ps.origin, ri->handLPoint);
07397 VectorCopy(self->client->ps.origin, ri->torsoPoint);
07398 VectorCopy(self->client->ps.origin, ri->crotchPoint);
07399 VectorCopy(self->client->ps.origin, ri->footRPoint);
07400 VectorCopy(self->client->ps.origin, ri->footLPoint);
07401 }
07402 else
07403 {
07404
07405 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->headBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07406 ri->headPoint[0] = boltMatrix.matrix[0][3];
07407 ri->headPoint[1] = boltMatrix.matrix[1][3];
07408 ri->headPoint[2] = boltMatrix.matrix[2][3];
07409
07410
07411 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->handRBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07412 ri->handRPoint[0] = boltMatrix.matrix[0][3];
07413 ri->handRPoint[1] = boltMatrix.matrix[1][3];
07414 ri->handRPoint[2] = boltMatrix.matrix[2][3];
07415
07416
07417 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->handLBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07418 ri->handLPoint[0] = boltMatrix.matrix[0][3];
07419 ri->handLPoint[1] = boltMatrix.matrix[1][3];
07420 ri->handLPoint[2] = boltMatrix.matrix[2][3];
07421
07422
07423 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->torsoBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07424 ri->torsoPoint[0] = boltMatrix.matrix[0][3];
07425 ri->torsoPoint[1] = boltMatrix.matrix[1][3];
07426 ri->torsoPoint[2] = boltMatrix.matrix[2][3];
07427
07428
07429 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->crotchBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07430 ri->crotchPoint[0] = boltMatrix.matrix[0][3];
07431 ri->crotchPoint[1] = boltMatrix.matrix[1][3];
07432 ri->crotchPoint[2] = boltMatrix.matrix[2][3];
07433
07434
07435 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->footRBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07436 ri->footRPoint[0] = boltMatrix.matrix[0][3];
07437 ri->footRPoint[1] = boltMatrix.matrix[1][3];
07438 ri->footRPoint[2] = boltMatrix.matrix[2][3];
07439
07440
07441 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->footLBolt, &boltMatrix, renderAngles, renderOrigin, level.time, NULL, self->modelScale);
07442 ri->footLPoint[0] = boltMatrix.matrix[0][3];
07443 ri->footLPoint[1] = boltMatrix.matrix[1][3];
07444 ri->footLPoint[2] = boltMatrix.matrix[2][3];
07445 }
07446
07447
07448 G_TestLine(ri->headPoint, ri->torsoPoint, 0x000000ff, 50);
07449 G_TestLine(ri->torsoPoint, ri->handRPoint, 0x000000ff, 50);
07450 G_TestLine(ri->torsoPoint, ri->handLPoint, 0x000000ff, 50);
07451 G_TestLine(ri->torsoPoint, ri->crotchPoint, 0x000000ff, 50);
07452 G_TestLine(ri->crotchPoint, ri->footRPoint, 0x000000ff, 50);
07453 G_TestLine(ri->crotchPoint, ri->footLPoint, 0x000000ff, 50);
07454 }
07455
07456
07457 VectorCopy(ri->muzzlePoint, ri->muzzlePointOld);
07458 VectorCopy(self->client->ps.origin, ri->muzzlePoint);
07459 VectorCopy(ri->muzzleDir, ri->muzzleDirOld);
07460 AngleVectors(self->client->ps.viewangles, ri->muzzleDir, 0, 0);
07461 ri->mPCalcTime = level.time;
07462
07463 VectorCopy(self->client->ps.origin, ri->eyePoint);
07464 ri->eyePoint[2] += self->client->ps.viewheight;
07465 }
07466 }
07467
07468 #define STAFF_KICK_RANGE 16
07469 extern void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex );
07470
07471 extern qboolean BG_InKnockDown( int anim );
07472 static qboolean G_KickDownable(gentity_t *ent)
07473 {
07474 if (!d_saberKickTweak.integer)
07475 {
07476 return qtrue;
07477 }
07478
07479 if (!ent || !ent->inuse || !ent->client)
07480 {
07481 return qfalse;
07482 }
07483
07484 if (BG_InKnockDown(ent->client->ps.legsAnim) ||
07485 BG_InKnockDown(ent->client->ps.torsoAnim))
07486 {
07487 return qfalse;
07488 }
07489
07490 if (ent->client->ps.weaponTime <= 0 &&
07491 ent->client->ps.weapon == WP_SABER &&
07492 ent->client->ps.groundEntityNum != ENTITYNUM_NONE)
07493 {
07494 return qfalse;
07495 }
07496
07497 return qtrue;
07498 }
07499
07500 static void G_TossTheMofo(gentity_t *ent, vec3_t tossDir, float tossStr)
07501 {
07502 if (!ent->inuse || !ent->client)
07503 {
07504 return;
07505 }
07506
07507 if (ent->s.eType == ET_NPC && ent->s.NPC_class == CLASS_VEHICLE)
07508 {
07509 return;
07510 }
07511
07512 VectorMA(ent->client->ps.velocity, tossStr, tossDir, ent->client->ps.velocity);
07513 ent->client->ps.velocity[2] = 200;
07514 if (ent->health > 0 && ent->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN &&
07515 BG_KnockDownable(&ent->client->ps) &&
07516 G_KickDownable(ent))
07517 {
07518 ent->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
07519 ent->client->ps.forceHandExtendTime = level.time + 700;
07520 ent->client->ps.forceDodgeAnim = 0;
07521
07522 }
07523 }
07524
07525 static gentity_t *G_KickTrace( gentity_t *ent, vec3_t kickDir, float kickDist, vec3_t kickEnd, int kickDamage, float kickPush )
07526 {
07527 vec3_t traceOrg, traceEnd, kickMins, kickMaxs;
07528 trace_t trace;
07529 gentity_t *hitEnt = NULL;
07530 VectorSet(kickMins, -2.0f, -2.0f, -2.0f);
07531 VectorSet(kickMaxs, 2.0f, 2.0f, 2.0f);
07532
07533 if ( kickEnd && !VectorCompare( kickEnd, vec3_origin ) )
07534 {
07535
07536 VectorSet( traceOrg, ent->r.currentOrigin[0], ent->r.currentOrigin[1], kickEnd[2] );
07537 VectorCopy( kickEnd, traceEnd );
07538 }
07539 else
07540 {
07541 VectorSet( traceOrg, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2]+ent->r.maxs[2]*0.5f );
07542 VectorMA( traceOrg, kickDist, kickDir, traceEnd );
07543 }
07544
07545 if (d_saberKickTweak.integer)
07546 {
07547 trap_G2Trace( &trace, traceOrg, kickMins, kickMaxs, traceEnd, ent->s.number, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer );
07548 }
07549 else
07550 {
07551 trap_Trace( &trace, traceOrg, kickMins, kickMaxs, traceEnd, ent->s.number, MASK_SHOT );
07552 }
07553
07554
07555 if ( trace.fraction < 1.0f && !trace.startsolid && !trace.allsolid )
07556 {
07557 if (ent->client->jediKickTime > level.time)
07558 {
07559 if (trace.entityNum == ent->client->jediKickIndex)
07560 {
07561 return NULL;
07562 }
07563 }
07564 ent->client->jediKickIndex = trace.entityNum;
07565 ent->client->jediKickTime = level.time + ent->client->ps.legsTimer;
07566
07567 hitEnt = &g_entities[trace.entityNum];
07568
07569
07570 if ( ent->client->ps.torsoAnim == BOTH_A7_HILT )
07571 {
07572 G_Sound( ent, CHAN_AUTO, G_SoundIndex( "sound/movers/objects/saber_slam" ) );
07573 }
07574 else
07575 {
07576 G_Sound( ent, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
07577 }
07578 if ( hitEnt->inuse )
07579 {
07580
07581 if ( hitEnt->takedamage )
07582 {
07583 if (hitEnt->client)
07584 {
07585 hitEnt->client->ps.otherKiller = ent->s.number;
07586 hitEnt->client->ps.otherKillerDebounceTime = level.time + 10000;
07587 hitEnt->client->ps.otherKillerTime = level.time + 10000;
07588 }
07589
07590 if (d_saberKickTweak.integer)
07591 {
07592 G_Damage( hitEnt, ent, ent, kickDir, trace.endpos, kickDamage*0.2f, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
07593 }
07594 else
07595 {
07596 G_Damage( hitEnt, ent, ent, kickDir, trace.endpos, kickDamage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
07597 }
07598 }
07599 if ( hitEnt->client
07600 && !(hitEnt->client->ps.pm_flags&PMF_TIME_KNOCKBACK)
07601 && G_CanBeEnemy(ent, hitEnt) )
07602 {
07603 if ( hitEnt->health <= 0 )
07604 {
07605
07606
07607
07608
07609 G_TossTheMofo(hitEnt, kickDir, kickPush*4.0f);
07610 }
07611 else
07612 {
07613
07614
07615
07616
07617
07618
07619
07620
07621
07622
07623
07624 if ( kickPush >= 75.0f && !Q_irand( 0, 2 ) )
07625 {
07626 G_TossTheMofo(hitEnt, kickDir, 300.0f);
07627 }
07628 else
07629 {
07630 G_TossTheMofo(hitEnt, kickDir, kickPush);
07631 }
07632 }
07633 }
07634 }
07635 }
07636 return (hitEnt);
07637 }
07638
07639 static void G_KickSomeMofos(gentity_t *ent)
07640 {
07641 vec3_t kickDir, kickEnd, fwdAngs;
07642 float animLength = BG_AnimLength( ent->localAnimIndex, (animNumber_t)ent->client->ps.legsAnim );
07643 float elapsedTime = (float)(animLength-ent->client->ps.legsTimer);
07644 float remainingTime = (animLength-elapsedTime);
07645 float kickDist = (ent->r.maxs[0]*1.5f)+STAFF_KICK_RANGE+8.0f;
07646 int kickDamage = Q_irand(10, 15);
07647 int kickPush = flrand( 50.0f, 100.0f );
07648 qboolean doKick = qfalse;
07649 renderInfo_t *ri = &ent->client->renderInfo;
07650
07651 VectorSet(kickDir, 0.0f, 0.0f, 0.0f);
07652 VectorSet(kickEnd, 0.0f, 0.0f, 0.0f);
07653 VectorSet(fwdAngs, 0.0f, ent->client->ps.viewangles[YAW], 0.0f);
07654
07655
07656 if ( ent->client->ps.torsoAnim == BOTH_A7_HILT )
07657 {
07658 if ( elapsedTime >= 250 && remainingTime >= 250 )
07659 {
07660 doKick = qtrue;
07661 if ( ri->handRBolt != -1 )
07662 {
07663 G_GetBoltPosition( ent, ri->handRBolt, kickEnd, 0 );
07664 VectorSubtract( kickEnd, ent->client->ps.origin, kickDir );
07665 kickDir[2] = 0;
07666 VectorNormalize( kickDir );
07667 }
07668 else
07669 {
07670 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07671 }
07672 }
07673 }
07674 else
07675 {
07676 switch ( ent->client->ps.legsAnim )
07677 {
07678 case BOTH_GETUP_BROLL_B:
07679 case BOTH_GETUP_BROLL_F:
07680 case BOTH_GETUP_FROLL_B:
07681 case BOTH_GETUP_FROLL_F:
07682 if ( elapsedTime >= 250 && remainingTime >= 250 )
07683 {
07684 doKick = qtrue;
07685 if ( ri->footRBolt != -1 )
07686 {
07687 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07688 VectorSubtract( kickEnd, ent->client->ps.origin, kickDir );
07689 kickDir[2] = 0;
07690 VectorNormalize( kickDir );
07691 }
07692 else
07693 {
07694 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07695 }
07696 }
07697 break;
07698 case BOTH_A7_KICK_F_AIR:
07699 case BOTH_A7_KICK_B_AIR:
07700 case BOTH_A7_KICK_R_AIR:
07701 case BOTH_A7_KICK_L_AIR:
07702 if ( elapsedTime >= 100 && remainingTime >= 250 )
07703 {
07704 doKick = qtrue;
07705 if ( ri->footRBolt != -1 )
07706 {
07707 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07708 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07709 kickDir[2] = 0;
07710 VectorNormalize( kickDir );
07711 }
07712 else
07713 {
07714 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07715 }
07716 }
07717 break;
07718 case BOTH_A7_KICK_F:
07719
07720 if ( elapsedTime >= 250 && remainingTime >= 250 )
07721 {
07722 doKick = qtrue;
07723 if ( ri->footRBolt != -1 )
07724 {
07725 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07726 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07727 kickDir[2] = 0;
07728 VectorNormalize( kickDir );
07729 }
07730 else
07731 {
07732 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07733 }
07734 }
07735 break;
07736 case BOTH_A7_KICK_B:
07737
07738 if ( elapsedTime >= 250 && remainingTime >= 250 )
07739 {
07740 doKick = qtrue;
07741 if ( ri->footRBolt != -1 )
07742 {
07743 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07744 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07745 kickDir[2] = 0;
07746 VectorNormalize( kickDir );
07747 }
07748 else
07749 {
07750 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07751 VectorScale( kickDir, -1, kickDir );
07752 }
07753 }
07754 break;
07755 case BOTH_A7_KICK_R:
07756
07757 if ( elapsedTime >= 250 && remainingTime >= 250 )
07758 {
07759 doKick = qtrue;
07760 if ( ri->footRBolt != -1 )
07761 {
07762 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07763 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07764 kickDir[2] = 0;
07765 VectorNormalize( kickDir );
07766 }
07767 else
07768 {
07769 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07770 }
07771 }
07772 break;
07773 case BOTH_A7_KICK_L:
07774
07775 if ( elapsedTime >= 250 && remainingTime >= 250 )
07776 {
07777 doKick = qtrue;
07778 if ( ri->footLBolt != -1 )
07779 {
07780 G_GetBoltPosition( ent, ri->footLBolt, kickEnd, 0 );
07781 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07782 kickDir[2] = 0;
07783 VectorNormalize( kickDir );
07784 }
07785 else
07786 {
07787 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07788 VectorScale( kickDir, -1, kickDir );
07789 }
07790 }
07791 break;
07792 case BOTH_A7_KICK_S:
07793 kickPush = flrand( 75.0f, 125.0f );
07794 if ( ri->footRBolt != -1 )
07795 {
07796 if ( elapsedTime >= 550
07797 && elapsedTime <= 1050 )
07798 {
07799 doKick = qtrue;
07800 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07801 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07802 kickDir[2] = 0;
07803 VectorNormalize( kickDir );
07804
07805 VectorMA( kickEnd, 8.0f, kickDir, kickEnd );
07806 }
07807 }
07808 else
07809 {
07810 if ( elapsedTime >= 400 && elapsedTime < 500 )
07811 {
07812 doKick = qtrue;
07813 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07814 }
07815 else if ( elapsedTime >= 500 && elapsedTime < 600 )
07816 {
07817 doKick = qtrue;
07818 fwdAngs[YAW] += 45;
07819 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07820 }
07821 else if ( elapsedTime >= 600 && elapsedTime < 700 )
07822 {
07823 doKick = qtrue;
07824 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07825 }
07826 else if ( elapsedTime >= 700 && elapsedTime < 800 )
07827 {
07828 doKick = qtrue;
07829 fwdAngs[YAW] += 45;
07830 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07831 }
07832 else if ( elapsedTime >= 800 && elapsedTime < 900 )
07833 {
07834 doKick = qtrue;
07835 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07836 VectorScale( kickDir, -1, kickDir );
07837 }
07838 else if ( elapsedTime >= 900 && elapsedTime < 1000 )
07839 {
07840 doKick = qtrue;
07841 fwdAngs[YAW] += 45;
07842 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07843 }
07844 else if ( elapsedTime >= 1000 && elapsedTime < 1100 )
07845 {
07846 doKick = qtrue;
07847 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07848 VectorScale( kickDir, -1, kickDir );
07849 }
07850 else if ( elapsedTime >= 1100 && elapsedTime < 1200 )
07851 {
07852 doKick = qtrue;
07853 fwdAngs[YAW] += 45;
07854 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07855 VectorScale( kickDir, -1, kickDir );
07856 }
07857 }
07858 break;
07859 case BOTH_A7_KICK_BF:
07860 kickPush = flrand( 75.0f, 125.0f );
07861 kickDist += 20.0f;
07862 if ( elapsedTime < 1500 )
07863 {
07864
07865
07866
07867 }
07868 if ( ri->footRBolt != -1 )
07869 {
07870 if ( ( elapsedTime >= 750 && elapsedTime < 850 )
07871 || ( elapsedTime >= 1400 && elapsedTime < 1500 ) )
07872 {
07873 doKick = qtrue;
07874 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07875 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07876 kickDir[2] = 0;
07877 VectorNormalize( kickDir );
07878
07879 VectorMA( kickEnd, 8, kickDir, kickEnd );
07880 }
07881 }
07882 else
07883 {
07884 if ( elapsedTime >= 250 && elapsedTime < 350 )
07885 {
07886 doKick = qtrue;
07887 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07888 }
07889 else if ( elapsedTime >= 350 && elapsedTime < 450 )
07890 {
07891 doKick = qtrue;
07892 AngleVectors( fwdAngs, kickDir, NULL, NULL );
07893 VectorScale( kickDir, -1, kickDir );
07894 }
07895 }
07896 break;
07897 case BOTH_A7_KICK_RL:
07898 kickPush = flrand( 75.0f, 125.0f );
07899 kickDist += 10.0f;
07900
07901
07902
07903
07904
07905
07906 if (level.framenum&1)
07907 {
07908 doKick = qtrue;
07909 if ( ri->footRBolt != -1 )
07910 {
07911 G_GetBoltPosition( ent, ri->footRBolt, kickEnd, 0 );
07912 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07913 kickDir[2] = 0;
07914 VectorNormalize( kickDir );
07915
07916 VectorMA( kickEnd, 8, kickDir, kickEnd );
07917 }
07918 else
07919 {
07920 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07921 }
07922 }
07923
07924 else
07925 {
07926 doKick = qtrue;
07927 if ( ri->footLBolt != -1 )
07928 {
07929 G_GetBoltPosition( ent, ri->footLBolt, kickEnd, 0 );
07930 VectorSubtract( kickEnd, ent->r.currentOrigin, kickDir );
07931 kickDir[2] = 0;
07932 VectorNormalize( kickDir );
07933
07934 VectorMA( kickEnd, 8, kickDir, kickEnd );
07935 }
07936 else
07937 {
07938 AngleVectors( fwdAngs, NULL, kickDir, NULL );
07939 VectorScale( kickDir, -1, kickDir );
07940 }
07941 }
07942 break;
07943 }
07944 }
07945
07946 if ( doKick )
07947 {
07948
07949 G_KickTrace( ent, kickDir, kickDist, NULL, kickDamage, kickPush );
07950 }
07951 }
07952
07953 static GAME_INLINE qboolean G_PrettyCloseIGuess(float a, float b, float tolerance)
07954 {
07955 if ((a-b) < tolerance &&
07956 (a-b) > -tolerance)
07957 {
07958 return qtrue;
07959 }
07960
07961 return qfalse;
07962 }
07963
07964 static void G_GrabSomeMofos(gentity_t *self)
07965 {
07966 renderInfo_t *ri = &self->client->renderInfo;
07967 mdxaBone_t boltMatrix;
07968 vec3_t flatAng;
07969 vec3_t pos;
07970 vec3_t grabMins, grabMaxs;
07971 trace_t trace;
07972
07973 if (!self->ghoul2 || ri->handRBolt == -1)
07974 {
07975 return;
07976 }
07977
07978 VectorSet(flatAng, 0.0f, self->client->ps.viewangles[1], 0.0f);
07979 trap_G2API_GetBoltMatrix(self->ghoul2, 0, ri->handRBolt, &boltMatrix, flatAng, self->client->ps.origin,
07980 level.time, NULL, self->modelScale);
07981 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, pos);
07982
07983 VectorSet(grabMins, -4.0f, -4.0f, -4.0f);
07984 VectorSet(grabMaxs, 4.0f, 4.0f, 4.0f);
07985
07986
07987 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 );
07988
07989 if (trace.fraction != 1.0f &&
07990 trace.entityNum < ENTITYNUM_WORLD)
07991 {
07992 gentity_t *grabbed = &g_entities[trace.entityNum];
07993
07994 if (grabbed->inuse && (grabbed->s.eType == ET_PLAYER || grabbed->s.eType == ET_NPC) &&
07995 grabbed->client && grabbed->health > 0 &&
07996 G_CanBeEnemy(self, grabbed) &&
07997 G_PrettyCloseIGuess(grabbed->client->ps.origin[2], self->client->ps.origin[2], 4.0f) &&
07998 (!BG_InGrappleMove(grabbed->client->ps.torsoAnim) || grabbed->client->ps.torsoAnim == BOTH_KYLE_GRAB) &&
07999 (!BG_InGrappleMove(grabbed->client->ps.legsAnim) || grabbed->client->ps.legsAnim == BOTH_KYLE_GRAB))
08000 {
08001 int tortureAnim = -1;
08002 int correspondingAnim = -1;
08003
08004 if (self->client->pers.cmd.forwardmove > 0)
08005 {
08006 tortureAnim = BOTH_KYLE_PA_1;
08007 correspondingAnim = BOTH_PLAYER_PA_1;
08008 }
08009 else if (self->client->pers.cmd.forwardmove < 0)
08010 {
08011 tortureAnim = BOTH_KYLE_PA_2;
08012 correspondingAnim = BOTH_PLAYER_PA_2;
08013 }
08014
08015 if (tortureAnim == -1 || correspondingAnim == -1)
08016 {
08017 if (self->client->ps.torsoTimer < 300 && !self->client->grappleState)
08018 {
08019 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
08020 if (self->client->ps.torsoAnim == BOTH_KYLE_MISS)
08021 {
08022 self->client->ps.weaponTime = self->client->ps.torsoTimer;
08023 }
08024 }
08025 return;
08026 }
08027
08028 self->client->grappleIndex = grabbed->s.number;
08029 self->client->grappleState = 1;
08030
08031 grabbed->client->grappleIndex = self->s.number;
08032 grabbed->client->grappleState = 20;
08033
08034
08035 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, tortureAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
08036 if (self->client->ps.torsoAnim == tortureAnim)
08037 {
08038 self->client->ps.weaponTime = self->client->ps.torsoTimer;
08039 }
08040
08041 G_SetAnim(grabbed, &grabbed->client->pers.cmd, SETANIM_BOTH, correspondingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
08042 if (grabbed->client->ps.torsoAnim == correspondingAnim)
08043 {
08044 if (grabbed->client->ps.weapon == WP_SABER)
08045 {
08046 if (!grabbed->client->ps.saberHolstered)
08047 {
08048 grabbed->client->ps.saberHolstered = 2;
08049 if (grabbed->client->saber[0].soundOff)
08050 {
08051 G_Sound(grabbed, CHAN_AUTO, grabbed->client->saber[0].soundOff);
08052 }
08053 if (grabbed->client->saber[1].soundOff &&
08054 grabbed->client->saber[1].model[0])
08055 {
08056 G_Sound(grabbed, CHAN_AUTO, grabbed->client->saber[1].soundOff);
08057 }
08058 }
08059 }
08060 if (grabbed->client->ps.torsoTimer < self->client->ps.torsoTimer)
08061 {
08062 grabbed->client->ps.torsoTimer = self->client->ps.torsoTimer;
08063 }
08064 grabbed->client->ps.weaponTime = grabbed->client->ps.torsoTimer;
08065 }
08066 }
08067 }
08068
08069 if (self->client->ps.torsoTimer < 300 && !self->client->grappleState)
08070 {
08071 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
08072 if (self->client->ps.torsoAnim == BOTH_KYLE_MISS)
08073 {
08074 self->client->ps.weaponTime = self->client->ps.torsoTimer;
08075 }
08076 }
08077 }
08078
08079 void WP_SaberPositionUpdate( gentity_t *self, usercmd_t *ucmd )
08080 {
08081
08082 gentity_t *mySaber = NULL;
08083 mdxaBone_t boltMatrix;
08084 vec3_t properAngles, properOrigin;
08085 vec3_t boltAngles, boltOrigin;
08086 vec3_t end;
08087 vec3_t legAxis[3];
08088 vec3_t addVel;
08089 vec3_t rawAngles;
08090 float fVSpeed = 0;
08091 int returnAfterUpdate = 0;
08092 float animSpeedScale = 1.0f;
08093 int saberNum;
08094 qboolean clientOverride;
08095 gentity_t *vehEnt = NULL;
08096 int rSaberNum = 0;
08097 int rBladeNum = 0;
08098
08099 #ifdef _DEBUG
08100 if (g_disableServerG2.integer)
08101 {
08102 return;
08103 }
08104 #endif
08105
08106 if (self && self->inuse && self->client)
08107 {
08108 if (self->client->saberCycleQueue)
08109 {
08110 self->client->ps.fd.saberDrawAnimLevel = self->client->saberCycleQueue;
08111 }
08112 else
08113 {
08114 self->client->ps.fd.saberDrawAnimLevel = self->client->ps.fd.saberAnimLevel;
08115 }
08116 }
08117
08118 if (self &&
08119 self->inuse &&
08120 self->client &&
08121 self->client->saberCycleQueue &&
08122 (self->client->ps.weaponTime <= 0 || self->health < 1))
08123 {
08124 self->client->ps.fd.saberAnimLevel = self->client->saberCycleQueue;
08125 self->client->saberCycleQueue = 0;
08126 }
08127
08128 if (!self ||
08129 !self->inuse ||
08130 !self->client ||
08131 !self->ghoul2 ||
08132 !g2SaberInstance)
08133 {
08134 return;
08135 }
08136
08137 if (BG_KickingAnim(self->client->ps.legsAnim))
08138 {
08139 G_KickSomeMofos(self);
08140 }
08141 else if (self->client->ps.torsoAnim == BOTH_KYLE_GRAB)
08142 {
08143 G_GrabSomeMofos(self);
08144 }
08145 else if (self->client->grappleState)
08146 {
08147 gentity_t *grappler = &g_entities[self->client->grappleIndex];
08148
08149 if (!grappler->inuse || !grappler->client || grappler->client->grappleIndex != self->s.number ||
08150 !BG_InGrappleMove(grappler->client->ps.torsoAnim) || !BG_InGrappleMove(grappler->client->ps.legsAnim) ||
08151 !BG_InGrappleMove(self->client->ps.torsoAnim) || !BG_InGrappleMove(self->client->ps.legsAnim) ||
08152 !self->client->grappleState || !grappler->client->grappleState ||
08153 grappler->health < 1 || self->health < 1 ||
08154 !G_PrettyCloseIGuess(self->client->ps.origin[2], grappler->client->ps.origin[2], 4.0f))
08155 {
08156 self->client->grappleState = 0;
08157 if ((BG_InGrappleMove(self->client->ps.torsoAnim) && self->client->ps.torsoTimer > 100) ||
08158 (BG_InGrappleMove(self->client->ps.legsAnim) && self->client->ps.legsTimer > 100))
08159 {
08160 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
08161 if (self->client->ps.torsoAnim == BOTH_KYLE_MISS)
08162 {
08163 self->client->ps.weaponTime = self->client->ps.torsoTimer;
08164 }
08165 }
08166 }
08167 else
08168 {
08169 vec3_t grapAng;
08170
08171 VectorSubtract(grappler->client->ps.origin, self->client->ps.origin, grapAng);
08172
08173 if (VectorLength(grapAng) > 64.0f)
08174 {
08175 if ((BG_InGrappleMove(self->client->ps.torsoAnim) && self->client->ps.torsoTimer > 100) ||
08176 (BG_InGrappleMove(self->client->ps.legsAnim) && self->client->ps.legsTimer > 100))
08177 {
08178 self->client->grappleState = 0;
08179
08180 G_SetAnim(self, &self->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
08181 if (self->client->ps.torsoAnim == BOTH_KYLE_MISS)
08182 {
08183 self->client->ps.weaponTime = self->client->ps.torsoTimer;
08184 }
08185 }
08186 }
08187 else
08188 {
08189 vectoangles(grapAng, grapAng);
08190 SetClientViewAngle(self, grapAng);
08191
08192 if (self->client->grappleState >= 20)
08193 {
08194
08195 float idealDist;
08196 vec3_t gFwd, idealSpot;
08197 trace_t trace;
08198
08199 if (grappler->client->ps.torsoAnim == BOTH_KYLE_PA_1)
08200 {
08201 idealDist = 46.0f;
08202 }
08203 else
08204 {
08205 idealDist = 34.0f;
08206 }
08207
08208 AngleVectors(grappler->client->ps.viewangles, gFwd, 0, 0);
08209 VectorMA(grappler->client->ps.origin, idealDist, gFwd, idealSpot);
08210
08211 trap_Trace(&trace, self->client->ps.origin, self->r.mins, self->r.maxs, idealSpot, self->s.number, self->clipmask);
08212 if (!trace.startsolid && !trace.allsolid && trace.fraction == 1.0f)
08213 {
08214 G_SetOrigin(self, idealSpot);
08215 VectorCopy(idealSpot, self->client->ps.origin);
08216 }
08217 }
08218 else if (self->client->grappleState >= 1)
08219 {
08220 if (grappler->client->ps.weapon == WP_SABER)
08221 {
08222 if (!grappler->client->ps.saberHolstered)
08223 {
08224 grappler->client->ps.saberHolstered = 2;
08225 if (grappler->client->saber[0].soundOff)
08226 {
08227 G_Sound(grappler, CHAN_AUTO, grappler->client->saber[0].soundOff);
08228 }
08229 if (grappler->client->saber[1].soundOff &&
08230 grappler->client->saber[1].model[0])
08231 {
08232 G_Sound(grappler, CHAN_AUTO, grappler->client->saber[1].soundOff);
08233 }
08234 }
08235 }
08236
08237
08238 if (self->client->ps.torsoAnim == BOTH_KYLE_PA_1)
08239 {
08240 if (self->client->grappleState == 1)
08241 {
08242 if (self->client->ps.torsoTimer < 3400)
08243 {
08244 int grapplerAnim = grappler->client->ps.torsoAnim;
08245 int grapplerTime = grappler->client->ps.torsoTimer;
08246
08247 G_Damage(grappler, self, self, NULL, self->client->ps.origin, 10, 0, MOD_MELEE);
08248
08249
08250
08251 if (grappler->health > 0)
08252 {
08253 grappler->client->ps.torsoAnim = grapplerAnim;
08254 grappler->client->ps.torsoTimer = grapplerTime;
08255 grappler->client->ps.legsAnim = grapplerAnim;
08256 grappler->client->ps.legsTimer = grapplerTime;
08257 grappler->client->ps.weaponTime = grapplerTime;
08258 }
08259 self->client->grappleState++;
08260 }
08261 }
08262 else if (self->client->grappleState == 2)
08263 {
08264 if (self->client->ps.torsoTimer < 2550)
08265 {
08266 int grapplerAnim = grappler->client->ps.torsoAnim;
08267 int grapplerTime = grappler->client->ps.torsoTimer;
08268
08269 G_Damage(grappler, self, self, NULL, self->client->ps.origin, 10, 0, MOD_MELEE);
08270
08271
08272
08273 if (grappler->health > 0)
08274 {
08275 grappler->client->ps.torsoAnim = grapplerAnim;
08276 grappler->client->ps.torsoTimer = grapplerTime;
08277 grappler->client->ps.legsAnim = grapplerAnim;
08278 grappler->client->ps.legsTimer = grapplerTime;
08279 grappler->client->ps.weaponTime = grapplerTime;
08280 }
08281 self->client->grappleState++;
08282 }
08283 }
08284 else
08285 {
08286 if (self->client->ps.torsoTimer < 1300)
08287 {
08288 vec3_t tossDir;
08289
08290 G_Damage(grappler, self, self, NULL, self->client->ps.origin, 30, 0, MOD_MELEE);
08291
08292
08293 self->client->grappleState = 0;
08294
08295 VectorSubtract(grappler->client->ps.origin, self->client->ps.origin, tossDir);
08296 VectorNormalize(tossDir);
08297 VectorScale(tossDir, 500.0f, tossDir);
08298 tossDir[2] = 200.0f;
08299
08300 VectorAdd(grappler->client->ps.velocity, tossDir, grappler->client->ps.velocity);
08301
08302 if (grappler->health > 0)
08303 {
08304 grappler->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
08305 grappler->client->ps.forceHandExtendTime = level.time + 1300;
08306 }
08307 }
08308 }
08309 }
08310 else if (self->client->ps.torsoAnim == BOTH_KYLE_PA_2)
08311 {
08312 if (self->client->grappleState == 1)
08313 {
08314 if (self->client->ps.torsoTimer < 3200)
08315 {
08316 int grapplerAnim = grappler->client->ps.torsoAnim;
08317 int grapplerTime = grappler->client->ps.torsoTimer;
08318
08319 G_Damage(grappler, self, self, NULL, self->client->ps.origin, 20, 0, MOD_MELEE);
08320
08321
08322
08323 if (grappler->health > 0)
08324 {
08325 grappler->client->ps.torsoAnim = grapplerAnim;
08326 grappler->client->ps.torsoTimer = grapplerTime;
08327 grappler->client->ps.legsAnim = grapplerAnim;
08328 grappler->client->ps.legsTimer = grapplerTime;
08329 grappler->client->ps.weaponTime = grapplerTime;
08330 }
08331 self->client->grappleState++;
08332 }
08333 }
08334 else if (self->client->grappleState == 2)
08335 {
08336 if (self->client->ps.torsoTimer < 2000)
08337 {
08338
08339
08340 G_EntitySound( grappler, CHAN_VOICE, G_SoundIndex("*pain100.wav") );
08341
08342 self->client->grappleState++;
08343 }
08344 }
08345 else
08346 {
08347 if (self->client->ps.torsoTimer < 1000)
08348 {
08349 G_Damage(grappler, self, self, NULL, self->client->ps.origin, 30, 0, MOD_MELEE);
08350
08351
08352
08353 if (grappler->health > 0)
08354 {
08355 grappler->client->ps.torsoTimer = 1000;
08356
08357 grappler->client->grappleState = 0;
08358 }
08359 else
08360 {
08361 grappler->client->ps.torsoAnim = BOTH_DEADFLOP1;
08362 grappler->client->ps.legsAnim = BOTH_DEADFLOP1;
08363 }
08364
08365 self->client->grappleState = 0;
08366 }
08367 }
08368 }
08369 else
08370 {
08371 }
08372 }
08373 }
08374 }
08375 }
08376
08377
08378
08379
08380 clientOverride = trap_G2API_OverrideServer(self->ghoul2);
08381
08382 saberNum = self->client->ps.saberEntityNum;
08383
08384 if (!saberNum)
08385 {
08386 saberNum = self->client->saberStoredIndex;
08387 }
08388
08389 if (!saberNum)
08390 {
08391 returnAfterUpdate = 1;
08392 goto nextStep;
08393 }
08394
08395 mySaber = &g_entities[saberNum];
08396
08397 if (self->health < 1)
08398 {
08399
08400 if (mySaber && ((mySaber->r.contents & CONTENTS_LIGHTSABER) || mySaber->r.contents == 0) && !self->client->ps.saberInFlight)
08401 {
08402 VectorCopy(self->client->ps.origin, mySaber->r.currentOrigin);
08403 }
08404
08405
08406
08407
08408
08409 }
08410
08411 if ( BG_SuperBreakWinAnim( self->client->ps.torsoAnim ) )
08412 {
08413 self->client->ps.weaponstate = WEAPON_FIRING;
08414 }
08415 if (self->client->ps.weapon != WP_SABER ||
08416 self->client->ps.weaponstate == WEAPON_RAISING ||
08417 self->client->ps.weaponstate == WEAPON_DROPPING ||
08418 self->health < 1)
08419 {
08420 if (!self->client->ps.saberInFlight)
08421 {
08422 returnAfterUpdate = 1;
08423 }
08424 }
08425
08426 if (self->client->ps.saberThrowDelay < level.time)
08427 {
08428 if ( (self->client->saber[0].saberFlags&SFL_NOT_THROWABLE) )
08429 {
08430 if ( (self->client->saber[0].saberFlags&SFL_SINGLE_BLADE_THROWABLE) )
08431 {
08432 if ( self->client->saber[0].numBlades > 1
08433 && self->client->ps.saberHolstered == 1 )
08434 {
08435 self->client->ps.saberCanThrow = qtrue;
08436
08437 }
08438 else
08439 {
08440 self->client->ps.saberCanThrow = qfalse;
08441 }
08442 }
08443 else
08444 {
08445 self->client->ps.saberCanThrow = qfalse;
08446 }
08447 }
08448 else
08449 {
08450 self->client->ps.saberCanThrow = qtrue;
08451 }
08452 }
08453 nextStep:
08454 if (self->client->ps.fd.forcePowersActive & (1 << FP_RAGE))
08455 {
08456 animSpeedScale = 2;
08457 }
08458
08459 VectorCopy(self->client->ps.origin, properOrigin);
08460
08461
08462 VectorCopy(self->client->ps.velocity, addVel);
08463 VectorNormalize(addVel);
08464
08465 if (self->client->ps.velocity[0] < 0)
08466 {
08467 fVSpeed += (-self->client->ps.velocity[0]);
08468 }
08469 else
08470 {
08471 fVSpeed += self->client->ps.velocity[0];
08472 }
08473 if (self->client->ps.velocity[1] < 0)
08474 {
08475 fVSpeed += (-self->client->ps.velocity[1]);
08476 }
08477 else
08478 {
08479 fVSpeed += self->client->ps.velocity[1];
08480 }
08481 if (self->client->ps.velocity[2] < 0)
08482 {
08483 fVSpeed += (-self->client->ps.velocity[2]);
08484 }
08485 else
08486 {
08487 fVSpeed += self->client->ps.velocity[2];
08488 }
08489
08490
08491 fVSpeed *= 1.6f/g_svfps.value;
08492
08493
08494
08495 if (fVSpeed > 70)
08496 {
08497 fVSpeed = 70;
08498 }
08499 if (fVSpeed < -70)
08500 {
08501 fVSpeed = -70;
08502 }
08503
08504 properOrigin[0] += addVel[0]*fVSpeed;
08505 properOrigin[1] += addVel[1]*fVSpeed;
08506 properOrigin[2] += addVel[2]*fVSpeed;
08507
08508 properAngles[0] = 0;
08509 if (self->s.number < MAX_CLIENTS && self->client->ps.m_iVehicleNum)
08510 {
08511 vehEnt = &g_entities[self->client->ps.m_iVehicleNum];
08512 if (vehEnt->inuse && vehEnt->client && vehEnt->m_pVehicle)
08513 {
08514 properAngles[1] = vehEnt->m_pVehicle->m_vOrientation[YAW];
08515 }
08516 else
08517 {
08518 properAngles[1] = self->client->ps.viewangles[YAW];
08519 vehEnt = NULL;
08520 }
08521 }
08522 else
08523 {
08524 properAngles[1] = self->client->ps.viewangles[YAW];
08525 }
08526 properAngles[2] = 0;
08527
08528 AnglesToAxis( properAngles, legAxis );
08529
08530 UpdateClientRenderinfo(self, properOrigin, properAngles);
08531
08532 if (!clientOverride)
08533 {
08534 G_G2PlayerAngles( self, legAxis, properAngles );
08535 }
08536
08537 if (vehEnt)
08538 {
08539 properAngles[1] = vehEnt->m_pVehicle->m_vOrientation[YAW];
08540 }
08541
08542 if (returnAfterUpdate && saberNum)
08543 {
08544
08545
08546
08547 if (mySaber && ((mySaber->r.contents & CONTENTS_LIGHTSABER) || mySaber->r.contents == 0) && !self->client->ps.saberInFlight)
08548 {
08549 VectorCopy(self->client->ps.origin, mySaber->r.currentOrigin);
08550 }
08551
08552 goto finalUpdate;
08553 }
08554
08555 if (returnAfterUpdate)
08556 {
08557 goto finalUpdate;
08558 }
08559
08560
08561
08562 trap_G2API_GetBoltMatrix(self->ghoul2, 1, 0, &boltMatrix, properAngles, properOrigin, level.time, NULL, self->modelScale);
08563 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrigin);
08564 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, boltAngles);
08565
08566
08567 if (self->client->lastSaberStorageTime && (level.time - self->client->lastSaberStorageTime) < 200)
08568 {
08569 VectorCopy(self->client->lastSaberBase_Always, self->client->olderSaberBase);
08570 self->client->olderIsValid = qtrue;
08571 }
08572 else
08573 {
08574 self->client->olderIsValid = qfalse;
08575 }
08576
08577 VectorCopy(boltOrigin, self->client->lastSaberBase_Always);
08578 VectorCopy(boltAngles, self->client->lastSaberDir_Always);
08579 self->client->lastSaberStorageTime = level.time;
08580
08581 VectorCopy(boltAngles, rawAngles);
08582
08583 VectorMA( boltOrigin, self->client->saber[0].blade[0].lengthMax, boltAngles, end );
08584
08585 if (self->client->ps.saberEntityNum)
08586 {
08587
08588 if (mySaber && ((mySaber->r.contents & CONTENTS_LIGHTSABER) || mySaber->r.contents == 0) && !self->client->ps.saberInFlight)
08589 {
08590 VectorMA( boltOrigin, self->client->saber[0].blade[0].lengthMax, boltAngles, mySaber->r.currentOrigin );
08591 }
08592 }
08593
08594 boltAngles[YAW] = self->client->ps.viewangles[YAW];
08595
08596
08597
08598
08599
08600
08601
08602
08603
08604
08605 if (self->client->ps.saberInFlight)
08606 {
08607 gentity_t *saberent = &g_entities[saberNum];
08608
08609 if (saberent)
08610 {
08611 if (!self->client->ps.saberEntityState && self->client->ps.saberEntityNum)
08612 {
08613 vec3_t startorg, startang, dir;
08614
08615 VectorCopy(boltOrigin, saberent->r.currentOrigin);
08616
08617 VectorCopy(boltOrigin, startorg);
08618 VectorCopy(boltAngles, startang);
08619
08620
08621
08622
08623
08624 saberent->r.svFlags &= ~(SVF_NOCLIENT);
08625 VectorCopy(startorg, saberent->s.pos.trBase);
08626 VectorCopy(startang, saberent->s.apos.trBase);
08627
08628 VectorCopy(startorg, saberent->s.origin);
08629 VectorCopy(startang, saberent->s.angles);
08630
08631 saberent->s.saberInFlight = qtrue;
08632
08633 saberent->s.apos.trType = TR_LINEAR;
08634 saberent->s.apos.trDelta[0] = 0;
08635 saberent->s.apos.trDelta[1] = 800;
08636 saberent->s.apos.trDelta[2] = 0;
08637
08638 saberent->s.pos.trType = TR_LINEAR;
08639 saberent->s.eType = ET_GENERAL;
08640 saberent->s.eFlags = 0;
08641
08642 WP_SaberAddG2Model( saberent, self->client->saber[0].model, self->client->saber[0].skin );
08643
08644 saberent->s.modelGhoul2 = 127;
08645
08646 saberent->parent = self;
08647
08648 self->client->ps.saberEntityState = 1;
08649
08650
08651 AngleVectors(self->client->ps.viewangles, dir, NULL, NULL);
08652
08653 saberent->nextthink = level.time + FRAMETIME;
08654 saberent->think = saberFirstThrown;
08655
08656 saberent->damage = SABER_THROWN_HIT_DAMAGE;
08657 saberent->methodOfDeath = MOD_SABER;
08658 saberent->splashMethodOfDeath = MOD_SABER;
08659 saberent->s.solid = 2;
08660 saberent->r.contents = CONTENTS_LIGHTSABER;
08661
08662 saberent->genericValue5 = 0;
08663
08664 VectorSet( saberent->r.mins, SABERMINS_X, SABERMINS_Y, SABERMINS_Z );
08665 VectorSet( saberent->r.maxs, SABERMAXS_X, SABERMAXS_Y, SABERMAXS_Z );
08666
08667 saberent->s.genericenemyindex = self->s.number+1024;
08668
08669 saberent->touch = thrownSaberTouch;
08670
08671 saberent->s.weapon = WP_SABER;
08672
08673 VectorScale(dir, 400, saberent->s.pos.trDelta );
08674 saberent->s.pos.trTime = level.time;
08675
08676 if ( self->client->saber[0].spinSound )
08677 {
08678 saberent->s.loopSound = self->client->saber[0].spinSound;
08679 }
08680 else
08681 {
08682 saberent->s.loopSound = saberSpinSound;
08683 }
08684 saberent->s.loopIsSoundset = qfalse;
08685
08686 self->client->ps.saberDidThrowTime = level.time;
08687
08688 self->client->dangerTime = level.time;
08689 self->client->ps.eFlags &= ~EF_INVULNERABLE;
08690 self->client->invulnerableTimer = 0;
08691
08692 trap_LinkEntity(saberent);
08693 }
08694 else if (self->client->ps.saberEntityNum)
08695 {
08696 VectorCopy(boltOrigin, saberent->pos1);
08697 trap_LinkEntity(saberent);
08698
08699 if (saberent->genericValue5 == PROPER_THROWN_VALUE)
08700 {
08701 saberent->genericValue5 = 0;
08702 saberent->think = SaberUpdateSelf;
08703 saberent->nextthink = level.time;
08704 WP_SaberRemoveG2Model( saberent );
08705
08706 self->client->ps.saberInFlight = qfalse;
08707 self->client->ps.saberEntityState = 0;
08708 self->client->ps.saberThrowDelay = level.time + 500;
08709 self->client->ps.saberCanThrow = qfalse;
08710 }
08711 }
08712 }
08713 }
08714
08715
08716
08717
08718
08719
08720
08721
08722 if (!BG_SabersOff(&self->client->ps))
08723 {
08724 gentity_t *saberent = &g_entities[saberNum];
08725
08726 if (!self->client->ps.saberInFlight && saberent)
08727 {
08728 saberent->r.svFlags |= (SVF_NOCLIENT);
08729 saberent->r.contents = CONTENTS_LIGHTSABER;
08730 SetSaberBoxSize(saberent);
08731 saberent->s.loopSound = 0;
08732 saberent->s.loopIsSoundset = qfalse;
08733 }
08734
08735 if (self->client->ps.saberLockTime > level.time && self->client->ps.saberEntityNum)
08736 {
08737 if (self->client->ps.saberIdleWound < level.time)
08738 {
08739 gentity_t *te;
08740 vec3_t dir;
08741 te = G_TempEntity( g_entities[saberNum].r.currentOrigin, EV_SABER_BLOCK );
08742 VectorSet( dir, 0, 1, 0 );
08743 VectorCopy(g_entities[saberNum].r.currentOrigin, te->s.origin);
08744 VectorCopy(dir, te->s.angles);
08745 te->s.eventParm = 1;
08746 te->s.weapon = 0;
08747 te->s.legsAnim = 0;
08748
08749 self->client->ps.saberIdleWound = level.time + Q_irand(400, 600);
08750 }
08751
08752 while (rSaberNum < MAX_SABERS)
08753 {
08754 rBladeNum = 0;
08755 while (rBladeNum < self->client->saber[rSaberNum].numBlades)
08756 {
08757 VectorCopy(boltOrigin, self->client->saber[saberNum].blade[rBladeNum].trail.base);
08758 VectorCopy(end, self->client->saber[saberNum].blade[rBladeNum].trail.tip);
08759 self->client->saber[saberNum].blade[rBladeNum].trail.lastTime = level.time;
08760
08761 rBladeNum++;
08762 }
08763
08764 rSaberNum++;
08765 }
08766 self->client->hasCurrentPosition = qtrue;
08767
08768 self->client->ps.saberBlocked = BLOCKED_NONE;
08769
08770 goto finalUpdate;
08771 }
08772
08773
08774 rSaberNum = rBladeNum = 0;
08775
08776 if (self->client->ps.saberInFlight)
08777 {
08778 if (!self->client->ps.saberEntityNum)
08779 {
08780 rSaberNum = 1;
08781 }
08782 else
08783 {
08784 rSaberNum = 0;
08785 }
08786 }
08787
08788 WP_SaberClearDamage();
08789 saberDoClashEffect = qfalse;
08790
08791
08792 while (rSaberNum < MAX_SABERS)
08793 {
08794 if (!self->client->saber[rSaberNum].model[0])
08795 {
08796 rSaberNum++;
08797 continue;
08798 }
08799
08800
08801
08802
08803
08804
08805
08806
08807
08808
08809
08810 if (rSaberNum == 1 && (self->client->ps.brokenLimbs & (1 << BROKENLIMB_LARM)))
08811 {
08812 break;
08813 }
08814 if (rSaberNum > 0
08815 && self->client->saber[1].model
08816 && self->client->saber[1].model[0]
08817 && self->client->ps.saberHolstered == 1 )
08818 {
08819 break;
08820 }
08821 rBladeNum = 0;
08822 while (rBladeNum < self->client->saber[rSaberNum].numBlades)
08823 {
08824
08825 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].muzzlePoint, self->client->saber[rSaberNum].blade[rBladeNum].muzzlePointOld);
08826 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].muzzleDir, self->client->saber[rSaberNum].blade[rBladeNum].muzzleDirOld);
08827
08828 if ( rBladeNum > 0
08829 && (!self->client->saber[1].model||!self->client->saber[1].model[0])
08830 && self->client->saber[rSaberNum].numBlades > 1
08831 && self->client->ps.saberHolstered == 1 )
08832 {
08833 break;
08834 }
08835
08836
08837 if ( rSaberNum == 0 && self->client->ps.saberInFlight )
08838 {
08839 if ( !self->client->ps.saberEntityNum )
08840 {
08841
08842
08843 rSaberNum++;
08844 rBladeNum = 0;
08845 continue;
08846 }
08847 else
08848 {
08849 gentity_t *saberEnt = &g_entities[self->client->ps.saberEntityNum];
08850 vec3_t saberOrg, saberAngles;
08851 if ( !saberEnt
08852 || !saberEnt->inuse
08853 || !saberEnt->ghoul2 )
08854 {
08855 rSaberNum++;
08856 rBladeNum = 0;
08857 continue;
08858 }
08859 if ( saberent->s.saberInFlight )
08860 {
08861 BG_EvaluateTrajectory( &saberEnt->s.pos, level.time+50, saberOrg );
08862 BG_EvaluateTrajectory( &saberEnt->s.apos, level.time+50, saberAngles );
08863 }
08864 else
08865 {
08866 vec3_t saberDir;
08867 BG_EvaluateTrajectory( &saberEnt->s.pos, level.time, saberOrg );
08868 VectorSubtract( self->r.currentOrigin, saberOrg, saberDir );
08869 vectoangles( saberDir, saberAngles );
08870 }
08871 trap_G2API_GetBoltMatrix(saberEnt->ghoul2, 0, rBladeNum, &boltMatrix, saberAngles, saberOrg, level.time, NULL, self->modelScale);
08872 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, self->client->saber[rSaberNum].blade[rBladeNum].muzzlePoint);
08873 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, self->client->saber[rSaberNum].blade[rBladeNum].muzzleDir);
08874 VectorCopy( self->client->saber[rSaberNum].blade[rBladeNum].muzzlePoint, boltOrigin );
08875 VectorMA( boltOrigin, self->client->saber[rSaberNum].blade[rBladeNum].lengthMax, self->client->saber[rSaberNum].blade[rBladeNum].muzzleDir, end );
08876 }
08877
08878 }
08879 else
08880 {
08881 trap_G2API_GetBoltMatrix(self->ghoul2, rSaberNum+1, rBladeNum, &boltMatrix, properAngles, properOrigin, level.time, NULL, self->modelScale);
08882 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, self->client->saber[rSaberNum].blade[rBladeNum].muzzlePoint);
08883 BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, self->client->saber[rSaberNum].blade[rBladeNum].muzzleDir);
08884 VectorCopy( self->client->saber[rSaberNum].blade[rBladeNum].muzzlePoint, boltOrigin );
08885 VectorMA( boltOrigin, self->client->saber[rSaberNum].blade[rBladeNum].lengthMax, self->client->saber[rSaberNum].blade[rBladeNum].muzzleDir, end );
08886 }
08887
08888 self->client->saber[rSaberNum].blade[rBladeNum].storageTime = level.time;
08889
08890 if (self->client->hasCurrentPosition && d_saberInterpolate.integer)
08891 {
08892 if (self->client->ps.weaponTime <= 0)
08893 {
08894 CheckSaberDamage(self, rSaberNum, rBladeNum, boltOrigin, end, qfalse, (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT), qfalse);
08895 }
08896 else if (d_saberInterpolate.integer == 1)
08897 {
08898 int trMask = CONTENTS_LIGHTSABER|CONTENTS_BODY;
08899 int sN = 0;
08900 qboolean gotHit = qfalse;
08901 qboolean clientUnlinked[MAX_CLIENTS];
08902 qboolean skipSaberTrace = qfalse;
08903
08904 if (!g_saberTraceSaberFirst.integer)
08905 {
08906 skipSaberTrace = qtrue;
08907 }
08908 else if (g_saberTraceSaberFirst.integer >= 2 &&
08909 g_gametype.integer != GT_DUEL &&
08910 g_gametype.integer != GT_POWERDUEL &&
08911 !self->client->ps.duelInProgress)
08912 {
08913 skipSaberTrace = qtrue;
08914 }
08915
08916 if (skipSaberTrace)
08917 {
08918 trMask = (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT);
08919 }
08920 else
08921 {
08922 while (sN < MAX_CLIENTS)
08923 {
08924 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))
08925 {
08926 g_entities[sN].r.contents &= ~CONTENTS_BODY;
08927 clientUnlinked[sN] = qtrue;
08928 }
08929 else
08930 {
08931 clientUnlinked[sN] = qfalse;
08932 }
08933 sN++;
08934 }
08935 }
08936
08937 while (!gotHit)
08938 {
08939 if (!CheckSaberDamage(self, rSaberNum, rBladeNum, boltOrigin, end, qfalse, trMask, qfalse))
08940 {
08941 if (!CheckSaberDamage(self, rSaberNum, rBladeNum, boltOrigin, end, qtrue, trMask, qfalse))
08942 {
08943 vec3_t oldSaberStart;
08944 vec3_t oldSaberEnd;
08945 vec3_t saberAngleNow;
08946 vec3_t saberAngleBefore;
08947 vec3_t saberMidDir;
08948 vec3_t saberMidAngle;
08949 vec3_t saberMidPoint;
08950 vec3_t saberMidEnd;
08951 vec3_t saberSubBase;
08952 float deltaX, deltaY, deltaZ;
08953
08954 if ( (level.time-self->client->saber[rSaberNum].blade[rBladeNum].trail.lastTime) > 100 )
08955 {
08956 VectorCopy(boltOrigin, oldSaberStart);
08957 VectorCopy(end, oldSaberEnd);
08958 }
08959 else
08960 {
08961 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].trail.base, oldSaberStart);
08962 VectorCopy(self->client->saber[rSaberNum].blade[rBladeNum].trail.tip, oldSaberEnd);
08963 }
08964
08965 VectorSubtract(oldSaberEnd, oldSaberStart, saberAngleBefore);
08966 vectoangles(saberAngleBefore, saberAngleBefore);
08967
08968 VectorSubtract(end, boltOrigin, saberAngleNow);
08969 vectoangles(saberAngleNow, saberAngleNow);
08970
08971 deltaX = AngleDelta(saberAngleBefore[0], saberAngleNow[0]);
08972 deltaY = AngleDelta(saberAngleBefore[1], saberAngleNow[1]);
08973 deltaZ = AngleDelta(saberAngleBefore[2], saberAngleNow[2]);
08974
08975 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)) )
08976 {
08977
08978 saberMidAngle[0] = saberAngleBefore[0] + (deltaX/2);
08979 saberMidAngle[1] = saberAngleBefore[1] + (deltaY/2);
08980 saberMidAngle[2] = saberAngleBefore[2] + (deltaZ/2);
08981
08982
08983
08984 VectorSubtract(boltOrigin, oldSaberStart, saberSubBase);
08985 saberMidPoint[0] = boltOrigin[0] + (saberSubBase[0]*0.5);
08986 saberMidPoint[1] = boltOrigin[1] + (saberSubBase[1]*0.5);
08987 saberMidPoint[2] = boltOrigin[2] + (saberSubBase[2]*0.5);
08988
08989 AngleVectors(saberMidAngle, saberMidDir, 0, 0);
08990 saberMidEnd[0] = saberMidPoint[0] + saberMidDir[0]*self->client->saber[rSaberNum].blade[rBladeNum].lengthMax;
08991 saberMidEnd[1] = saberMidPoint[1] + saberMidDir[1]*self->client->saber[rSaberNum].blade[rBladeNum].lengthMax;
08992 saberMidEnd[2] = saberMidPoint[2] + saberMidDir[2]*self->client->saber[rSaberNum].blade[rBladeNum].lengthMax;
08993
08994
08995 if (CheckSaberDamage(self, rSaberNum, rBladeNum, saberMidPoint, saberMidEnd, qfalse, trMask, qfalse))
08996 {
08997 gotHit = qtrue;
08998 }
08999 }
09000 }
09001 else
09002 {
09003 gotHit = qtrue;
09004 }
09005 }
09006 else
09007 {
09008 gotHit = qtrue;
09009 }
09010
09011 if (g_saberTraceSaberFirst.integer)
09012 {
09013 sN = 0;
09014 while (sN < MAX_CLIENTS)
09015 {
09016 if (clientUnlinked[sN])
09017 {
09018 if (g_entities[sN].inuse && g_entities[sN].health > 0)
09019 {
09020 g_entities[sN].r.contents |= CONTENTS_BODY;
09021 }
09022 }
09023 sN++;
09024 }
09025 }
09026
09027 if (!gotHit)
09028 {
09029 if (trMask != (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT))
09030 {
09031 trMask = (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT);
09032 }
09033 else
09034 {
09035 gotHit = qtrue;
09036 }
09037 }
09038 }
09039 }
09040 else if (d_saberInterpolate.integer)
09041 {
09042 if (!CheckSaberDamage(self, rSaberNum, rBladeNum, boltOrigin, end, qfalse, (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT), qfalse))
09043 {
09044 CheckSaberDamage(self, rSaberNum, rBladeNum, boltOrigin, end, qtrue, (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT), qfalse);
09045 }
09046 }
09047 }
09048 else if ( d_saberSPStyleDamage.integer )
09049 {
09050 G_SPSaberDamageTraceLerped( self, rSaberNum, rBladeNum, boltOrigin, end, (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT) );
09051 }
09052 else
09053 {
09054 CheckSaberDamage(self, rSaberNum, rBladeNum, boltOrigin, end, qfalse, (MASK_PLAYERSOLID|CONTENTS_LIGHTSABER|MASK_SHOT), qfalse);
09055 }
09056
09057 VectorCopy(boltOrigin, self->client->saber[rSaberNum].blade[rBladeNum].trail.base);
09058 VectorCopy(end, self->client->saber[rSaberNum].blade[rBladeNum].trail.tip);
09059 self->client->saber[rSaberNum].blade[rBladeNum].trail.lastTime = level.time;
09060
09061
09062 self->client->hasCurrentPosition = qtrue;
09063
09064
09065 WP_SaberDoHit( self, rSaberNum, rBladeNum );
09066 WP_SaberDoClash( self, rSaberNum, rBladeNum );
09067
09068 rBladeNum++;
09069 }
09070
09071 rSaberNum++;
09072 }
09073
09074 WP_SaberApplyDamage( self );
09075
09076
09077
09078
09079
09080 if (mySaber && mySaber->inuse)
09081 {
09082 trap_LinkEntity(mySaber);
09083 }
09084
09085 if (!self->client->ps.saberInFlight)
09086 {
09087 self->client->ps.saberEntityState = 0;
09088 }
09089 }
09090 finalUpdate:
09091 if (clientOverride)
09092 {
09093 return;
09094 }
09095
09096 G_UpdateClientAnims(self, animSpeedScale);
09097 }
09098
09099 int WP_MissileBlockForBlock( int saberBlock )
09100 {
09101 switch( saberBlock )
09102 {
09103 case BLOCKED_UPPER_RIGHT:
09104 return BLOCKED_UPPER_RIGHT_PROJ;
09105 break;
09106 case BLOCKED_UPPER_LEFT:
09107 return BLOCKED_UPPER_LEFT_PROJ;
09108 break;
09109 case BLOCKED_LOWER_RIGHT:
09110 return BLOCKED_LOWER_RIGHT_PROJ;
09111 break;
09112 case BLOCKED_LOWER_LEFT:
09113 return BLOCKED_LOWER_LEFT_PROJ;
09114 break;
09115 case BLOCKED_TOP:
09116 return BLOCKED_TOP_PROJ;
09117 break;
09118 }
09119 return saberBlock;
09120 }
09121
09122 void WP_SaberBlockNonRandom( gentity_t *self, vec3_t hitloc, qboolean missileBlock )
09123 {
09124 vec3_t diff, fwdangles={0,0,0}, right;
09125 vec3_t clEye;
09126 float rightdot;
09127 float zdiff;
09128
09129 VectorCopy(self->client->ps.origin, clEye);
09130 clEye[2] += self->client->ps.viewheight;
09131
09132 VectorSubtract( hitloc, clEye, diff );
09133 diff[2] = 0;
09134 VectorNormalize( diff );
09135
09136 fwdangles[1] = self->client->ps.viewangles[1];
09137
09138 AngleVectors( fwdangles, NULL, right, NULL );
09139
09140 rightdot = DotProduct(right, diff);
09141 zdiff = hitloc[2] - clEye[2];
09142
09143 if ( zdiff > 0 )
09144 {
09145 if ( rightdot > 0.3 )
09146 {
09147 self->client->ps.saberBlocked = BLOCKED_UPPER_RIGHT;
09148 }
09149 else if ( rightdot < -0.3 )
09150 {
09151 self->client->ps.saberBlocked = BLOCKED_UPPER_LEFT;
09152 }
09153 else
09154 {
09155 self->client->ps.saberBlocked = BLOCKED_TOP;
09156 }
09157 }
09158 else if ( zdiff > -20 )
09159 {
09160 if ( zdiff < -10 )
09161 {
09162
09163 }
09164 if ( rightdot > 0.1 )
09165 {
09166 self->client->ps.saberBlocked = BLOCKED_UPPER_RIGHT;
09167 }
09168 else if ( rightdot < -0.1 )
09169 {
09170 self->client->ps.saberBlocked = BLOCKED_UPPER_LEFT;
09171 }
09172 else
09173 {
09174 self->client->ps.saberBlocked = BLOCKED_TOP;
09175 }
09176 }
09177 else
09178 {
09179 if ( rightdot >= 0 )
09180 {
09181 self->client->ps.saberBlocked = BLOCKED_LOWER_RIGHT;
09182 }
09183 else
09184 {
09185 self->client->ps.saberBlocked = BLOCKED_LOWER_LEFT;
09186 }
09187 }
09188
09189 if ( missileBlock )
09190 {
09191 self->client->ps.saberBlocked = WP_MissileBlockForBlock( self->client->ps.saberBlocked );
09192 }
09193 }
09194
09195 void WP_SaberBlock( gentity_t *playerent, vec3_t hitloc, qboolean missileBlock )
09196 {
09197 vec3_t diff, fwdangles={0,0,0}, right;
09198 float rightdot;
09199 float zdiff;
09200
09201 VectorSubtract(hitloc, playerent->client->ps.origin, diff);
09202 VectorNormalize(diff);
09203
09204 fwdangles[1] = playerent->client->ps.viewangles[1];
09205
09206 AngleVectors( fwdangles, NULL, right, NULL );
09207
09208 rightdot = DotProduct(right, diff) + RandFloat(-0.2f,0.2f);
09209 zdiff = hitloc[2] - playerent->client->ps.origin[2] + Q_irand(-8,8);
09210
09211
09212 if (zdiff > 24)
09213 {
09214 if (Q_irand(0,1))
09215 {
09216 playerent->client->ps.saberBlocked = BLOCKED_TOP;
09217 }
09218 else
09219 {
09220 playerent->client->ps.saberBlocked = BLOCKED_UPPER_LEFT;
09221 }
09222 }
09223 else if (zdiff > 13)
09224 {
09225 if (rightdot > 0.25)
09226 {
09227 if (Q_irand(0,1))
09228 {
09229 playerent->client->ps.saberBlocked = BLOCKED_UPPER_LEFT;
09230 }
09231 else
09232 {
09233 playerent->client->ps.saberBlocked = BLOCKED_LOWER_LEFT;
09234 }
09235 }
09236 else
09237 {
09238 switch(Q_irand(0,3))
09239 {
09240 case 0:
09241 playerent->client->ps.saberBlocked = BLOCKED_UPPER_RIGHT;
09242 break;
09243 case 1:
09244 case 2:
09245 playerent->client->ps.saberBlocked = BLOCKED_LOWER_RIGHT;
09246 break;
09247 case 3:
09248 playerent->client->ps.saberBlocked = BLOCKED_TOP;
09249 break;
09250 }
09251 }
09252 }
09253 else
09254 {
09255 if (Q_irand(0,1))
09256 {
09257 playerent->client->ps.saberBlocked = BLOCKED_LOWER_RIGHT;
09258 }
09259 else
09260 {
09261 playerent->client->ps.saberBlocked = BLOCKED_LOWER_LEFT;
09262 }
09263 }
09264
09265 if ( missileBlock )
09266 {
09267 playerent->client->ps.saberBlocked = WP_MissileBlockForBlock( playerent->client->ps.saberBlocked );
09268 }
09269 }
09270
09271 int WP_SaberCanBlock(gentity_t *self, vec3_t point, int dflags, int mod, qboolean projectile, int attackStr)
09272 {
09273 qboolean thrownSaber = qfalse;
09274 float blockFactor = 0;
09275
09276 if (!self || !self->client || !point)
09277 {
09278 return 0;
09279 }
09280
09281 if (attackStr == 999)
09282 {
09283 attackStr = 0;
09284 thrownSaber = qtrue;
09285 }
09286
09287 if (BG_SaberInAttack(self->client->ps.saberMove))
09288 {
09289 return 0;
09290 }
09291
09292 if (PM_InSaberAnim(self->client->ps.torsoAnim) && !self->client->ps.saberBlocked &&
09293 self->client->ps.saberMove != LS_READY && self->client->ps.saberMove != LS_NONE)
09294 {
09295 if ( self->client->ps.saberMove < LS_PARRY_UP || self->client->ps.saberMove > LS_REFLECT_LL )
09296 {
09297 return 0;
09298 }
09299 }
09300
09301 if (PM_SaberInBrokenParry(self->client->ps.saberMove))
09302 {
09303 return 0;
09304 }
09305
09306 if (!self->client->ps.saberEntityNum)
09307 {
09308 return 0;
09309 }
09310
09311 if (BG_SabersOff( &self->client->ps ))
09312 {
09313 return 0;
09314 }
09315
09316 if (self->client->ps.weapon != WP_SABER)
09317 {
09318 return 0;
09319 }
09320
09321 if (self->client->ps.weaponstate == WEAPON_RAISING)
09322 {
09323 return 0;
09324 }
09325
09326 if (self->client->ps.saberInFlight)
09327 {
09328 return 0;
09329 }
09330
09331 if ((self->client->pers.cmd.buttons & BUTTON_ATTACK)
09332 )
09333 {
09334 return 0;
09335 }
09336
09337
09338
09339
09340
09341
09342
09343
09344
09345
09346
09347
09348
09349
09350
09351
09352
09353
09354
09355
09356
09357
09358
09359
09360
09361
09362
09363
09364
09365
09366
09367
09368
09369
09370
09371
09372
09373
09374
09375
09376
09377
09378
09379
09380
09381 if (SaberAttacking(self))
09382 {
09383 return 0;
09384 }
09385
09386 if (self->client->ps.saberMove != LS_READY &&
09387 !self->client->ps.saberBlocking)
09388 {
09389 return 0;
09390 }
09391
09392 if (self->client->ps.saberBlockTime >= level.time)
09393 {
09394 return 0;
09395 }
09396
09397 if (self->client->ps.forceHandExtend != HANDEXTEND_NONE)
09398 {
09399 return 0;
09400 }
09401
09402 if (self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] == FORCE_LEVEL_3)
09403 {
09404 if (d_saberGhoul2Collision.integer)
09405 {
09406 blockFactor = 0.3f;
09407 }
09408 else
09409 {
09410 blockFactor = 0.05f;
09411 }
09412 }
09413 else if (self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] == FORCE_LEVEL_2)
09414 {
09415 blockFactor = 0.6f;
09416 }
09417 else if (self->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE] == FORCE_LEVEL_1)
09418 {
09419 blockFactor = 0.9f;
09420 }
09421 else
09422 {
09423 return 0;
09424 }
09425
09426 if (thrownSaber)
09427 {
09428 blockFactor -= 0.25f;
09429 }
09430
09431 if (attackStr)
09432 {
09433 blockFactor -= 0.25f;
09434 }
09435
09436 if (!InFront( point, self->client->ps.origin, self->client->ps.viewangles, blockFactor ))
09437 {
09438 return 0;
09439 }
09440
09441 if (projectile)
09442 {
09443 WP_SaberBlockNonRandom(self, point, projectile);
09444 }
09445 return 1;
09446 }
09447
09448 qboolean HasSetSaberOnly(void)
09449 {
09450 int i = 0;
09451 int wDisable = 0;
09452
09453 if (g_gametype.integer == GT_JEDIMASTER)
09454 {
09455 return qfalse;
09456 }
09457
09458 if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
09459 {
09460 wDisable = g_duelWeaponDisable.integer;
09461 }
09462 else
09463 {
09464 wDisable = g_weaponDisable.integer;
09465 }
09466
09467 while (i < WP_NUM_WEAPONS)
09468 {
09469 if (!(wDisable & (1 << i)) &&
09470 i != WP_SABER && i != WP_NONE)
09471 {
09472 return qfalse;
09473 }
09474
09475 i++;
09476 }
09477
09478 return qtrue;
09479 }
09480