123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845 |
- //
- // NPC_move.cpp
- //
- // leave this line at the top for all NPC_xxxx.cpp files...
- #include "g_headers.h"
- #include "b_local.h"
- #include "g_nav.h"
- #include "anims.h"
- extern qboolean NPC_ClearPathToGoal( vec3_t dir, gentity_t *goal );
- extern qboolean NAV_MoveDirSafe( gentity_t *self, usercmd_t *cmd, float distScale = 1.0f );
- void CG_Cylinder( vec3_t start, vec3_t end, float radius, vec3_t color );
- qboolean G_BoundsOverlap(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2);
- extern int GetTime ( int lastTime );
- navInfo_t frameNavInfo;
- extern qboolean FlyingCreature( gentity_t *ent );
- extern qboolean PM_InKnockDown( playerState_t *ps );
- extern cvar_t *g_navSafetyChecks;
- extern qboolean Boba_Flying( gentity_t *self );
- extern qboolean PM_InRoll( playerState_t *ps );
- #define APEX_HEIGHT 200.0f
- #define PARA_WIDTH (sqrt(APEX_HEIGHT)+sqrt(APEX_HEIGHT))
- #define JUMP_SPEED 200.0f
- static qboolean NPC_TryJump();
- static qboolean NPC_Jump( vec3_t dest, int goalEntNum )
- {//FIXME: if land on enemy, knock him down & jump off again
- float targetDist, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed,
- float originalShotSpeed, shotSpeed, speedStep = 50.0f, minShotSpeed = 30.0f, maxShotSpeed = 500.0f;
- qboolean belowBlocked = qfalse, aboveBlocked = qfalse;
- vec3_t targetDir, shotVel, failCase;
- trace_t trace;
- trajectory_t tr;
- qboolean blocked;
- int elapsedTime, timeStep = 250, hitCount = 0, aboveTries = 0, belowTries = 0, maxHits = 10;
- vec3_t lastPos, testPos, bottom;
- VectorSubtract( dest, NPC->currentOrigin, targetDir );
- targetDist = VectorNormalize( targetDir );
- //make our shotSpeed reliant on the distance
- originalShotSpeed = targetDist;//DistanceHorizontal( dest, NPC->currentOrigin )/2.0f;
- if ( originalShotSpeed > maxShotSpeed )
- {
- originalShotSpeed = maxShotSpeed;
- }
- else if ( originalShotSpeed < minShotSpeed )
- {
- originalShotSpeed = minShotSpeed;
- }
- shotSpeed = originalShotSpeed;
- while ( hitCount < maxHits )
- {
- VectorScale( targetDir, shotSpeed, shotVel );
- travelTime = targetDist/shotSpeed;
- shotVel[2] += travelTime * 0.5 * NPC->client->ps.gravity;
- if ( !hitCount )
- {//save the first one as the worst case scenario
- VectorCopy( shotVel, failCase );
- }
- if ( 1 )//tracePath )
- {//do a rough trace of the path
- blocked = qfalse;
- VectorCopy( NPC->currentOrigin, tr.trBase );
- VectorCopy( shotVel, tr.trDelta );
- tr.trType = TR_GRAVITY;
- tr.trTime = level.time;
- travelTime *= 1000.0f;
- VectorCopy( NPC->currentOrigin, lastPos );
-
- //This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down?
- for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep )
- {
- if ( (float)elapsedTime > travelTime )
- {//cap it
- elapsedTime = floor( travelTime );
- }
- EvaluateTrajectory( &tr, level.time + elapsedTime, testPos );
- //FUCK IT, always check for do not enter...
- gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP );
- /*
- if ( testPos[2] < lastPos[2]
- && elapsedTime < floor( travelTime ) )
- {//going down, haven't reached end, ignore botclip
- gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask );
- }
- else
- {//going up, check for botclip
- gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP );
- }
- */
- if ( trace.allsolid || trace.startsolid )
- {//started in solid
- if ( NAVDEBUG_showCollision )
- {
- CG_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND );
- }
- return qfalse;//you're hosed, dude
- }
- if ( trace.fraction < 1.0f )
- {//hit something
- if ( NAVDEBUG_showCollision )
- {
- CG_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND ); // TryJump
- }
- if ( trace.entityNum == goalEntNum )
- {//hit the enemy, that's bad!
- blocked = qtrue;
- /*
- if ( g_entities[goalEntNum].client && g_entities[goalEntNum].client->ps.groundEntityNum == ENTITYNUM_NONE )
- {//bah, would collide in mid-air, no good
- blocked = qtrue;
- }
- else
- {//he's on the ground, good enough, I guess
- //Hmm, don't want to land on him, though...?
- }
- */
- break;
- }
- else
- {
- if ( trace.contents & CONTENTS_BOTCLIP )
- {//hit a do-not-enter brush
- blocked = qtrue;
- break;
- }
- if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, dest ) < 4096 )//hit within 64 of desired location, should be okay
- {//close enough!
- break;
- }
- else
- {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow?
- impactDist = DistanceSquared( trace.endpos, dest );
- if ( impactDist < bestImpactDist )
- {
- bestImpactDist = impactDist;
- VectorCopy( shotVel, failCase );
- }
- blocked = qtrue;
- break;
- }
- }
- }
- else
- {
- if ( NAVDEBUG_showCollision )
- {
- CG_DrawEdge( lastPos, testPos, EDGE_WHITE_TWOSECOND ); // TryJump
- }
- }
- if ( elapsedTime == floor( travelTime ) )
- {//reached end, all clear
- if ( trace.fraction >= 1.0f )
- {//hmm, make sure we'll land on the ground...
- //FIXME: do we care how far below ourselves or our dest we'll land?
- VectorCopy( trace.endpos, bottom );
- bottom[2] -= 128;
- gi.trace( &trace, trace.endpos, NPC->mins, NPC->maxs, bottom, NPC->s.number, NPC->clipmask );
- if ( trace.fraction >= 1.0f )
- {//would fall too far
- blocked = qtrue;
- }
- }
- break;
- }
- else
- {
- //all clear, try next slice
- VectorCopy( testPos, lastPos );
- }
- }
- if ( blocked )
- {//hit something, adjust speed (which will change arc)
- hitCount++;
- //alternate back and forth between trying an arc slightly above or below the ideal
- if ( (hitCount%2) && !belowBlocked )
- {//odd
- belowTries++;
- shotSpeed = originalShotSpeed - (belowTries*speedStep);
- }
- else if ( !aboveBlocked )
- {//even
- aboveTries++;
- shotSpeed = originalShotSpeed + (aboveTries*speedStep);
- }
- else
- {//can't go any higher or lower
- hitCount = maxHits;
- break;
- }
- if ( shotSpeed > maxShotSpeed )
- {
- shotSpeed = maxShotSpeed;
- aboveBlocked = qtrue;
- }
- else if ( shotSpeed < minShotSpeed )
- {
- shotSpeed = minShotSpeed;
- belowBlocked = qtrue;
- }
- }
- else
- {//made it!
- break;
- }
- }
- else
- {//no need to check the path, go with first calc
- break;
- }
- }
- if ( hitCount >= maxHits )
- {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?)
- return qfalse;
- //NOTE: or try failcase?
- //VectorCopy( failCase, NPC->client->ps.velocity );
- //return qtrue;
- }
- VectorCopy( shotVel, NPC->client->ps.velocity );
- return qtrue;
- }
- #define NPC_JUMP_PREP_BACKUP_DIST 34.0f
- trace_t mJumpTrace;
- qboolean NPC_CanTryJump()
- {
- if (!(NPCInfo->scriptFlags&SCF_NAV_CAN_JUMP) || // Can't Jump
- (NPCInfo->scriptFlags&SCF_NO_ACROBATICS) || // If Can't Jump At All
- (level.time<NPCInfo->jumpBackupTime) || // If Backing Up, Don't Try The Jump Again
- (level.time<NPCInfo->jumpNextCheckTime) || // Don't Even Try To Jump Again For This Amount Of Time
- (NPCInfo->jumpTime) || // Don't Jump If Already Going
- (PM_InKnockDown(&NPC->client->ps)) || // Don't Jump If In Knockdown
- (PM_InRoll(&NPC->client->ps)) || // ... Or Roll
- (NPC->client->ps.groundEntityNum==ENTITYNUM_NONE) // ... Or In The Air
- )
- {
- return qfalse;
- }
- return qtrue;
- }
- qboolean NPC_TryJump(const vec3_t& pos, float max_xy_dist, float max_z_diff)
- {
- if (NPC_CanTryJump())
- {
- NPCInfo->jumpNextCheckTime = level.time + Q_irand(1000, 2000);
- VectorCopy(pos, NPCInfo->jumpDest);
- // Can't Try To Jump At A Point In The Air
- //-----------------------------------------
- {
- vec3_t groundTest;
- VectorCopy(pos, groundTest);
- groundTest[2] += (NPC->mins[2]*3);
- gi.trace(&mJumpTrace, NPCInfo->jumpDest, vec3_origin, vec3_origin, groundTest, NPC->s.number, NPC->clipmask );
- if (mJumpTrace.fraction >= 1.0f)
- {
- return qfalse; //no ground = no jump
- }
- }
- NPCInfo->jumpTarget = 0;
- NPCInfo->jumpMaxXYDist = (max_xy_dist)?(max_xy_dist):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?1200:750);
- NPCInfo->jumpMazZDist = (max_z_diff)?(max_z_diff):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?-1000:-450);
- NPCInfo->jumpTime = 0;
- NPCInfo->jumpBackupTime = 0;
- return NPC_TryJump();
- }
- return qfalse;
- }
- qboolean NPC_TryJump(gentity_t *goal, float max_xy_dist, float max_z_diff)
- {
- if (NPC_CanTryJump())
- {
- NPCInfo->jumpNextCheckTime = level.time + Q_irand(1000, 3000);
- // Can't Jump At Targets In The Air
- //---------------------------------
- if (goal->client && goal->client->ps.groundEntityNum==ENTITYNUM_NONE)
- {
- return qfalse;
- }
- VectorCopy(goal->currentOrigin, NPCInfo->jumpDest);
- NPCInfo->jumpTarget = goal;
- NPCInfo->jumpMaxXYDist = (max_xy_dist)?(max_xy_dist):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?1200:750);
- NPCInfo->jumpMazZDist = (max_z_diff)?(max_z_diff):((NPC->client->NPC_class==CLASS_ROCKETTROOPER)?-1000:-400);
- NPCInfo->jumpTime = 0;
- NPCInfo->jumpBackupTime = 0;
- return NPC_TryJump();
- }
- return qfalse;
- }
- void NPC_JumpAnimation()
- {
- int jumpAnim = BOTH_JUMP1;
- if ( NPC->client->NPC_class == CLASS_BOBAFETT
- || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER)
- || NPC->client->NPC_class == CLASS_ROCKETTROOPER
- ||( NPCInfo->rank != RANK_CREWMAN && NPCInfo->rank <= RANK_LT_JG ) )
- {//can't do acrobatics
- jumpAnim = BOTH_FORCEJUMP1;
- }
- else if (NPC->client->NPC_class != CLASS_HOWLER)
- {
- if ( NPC->client->NPC_class == CLASS_ALORA && Q_irand( 0, 3 ) )
- {
- jumpAnim = Q_irand( BOTH_ALORA_FLIP_1, BOTH_ALORA_FLIP_3 );
- }
- else
- {
- jumpAnim = BOTH_FLIP_F;
- }
- }
- NPC_SetAnim( NPC, SETANIM_BOTH, jumpAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- }
- extern void JET_FlyStart(gentity_t* actor);
- void NPC_JumpSound()
- {
- if ( NPC->client->NPC_class == CLASS_HOWLER )
- {
- //FIXME: can I delay the actual jump so that it matches the anim...?
- }
- else if ( NPC->client->NPC_class == CLASS_BOBAFETT
- || NPC->client->NPC_class == CLASS_ROCKETTROOPER )
- {
- // does this really need to be here?
- JET_FlyStart(NPC);
- }
- else
- {
- G_SoundOnEnt( NPC, CHAN_BODY, "sound/weapons/force/jump.wav" );
- }
- }
- qboolean NPC_TryJump()
- {
- vec3_t targetDirection;
- float targetDistanceXY;
- float targetDistanceZ;
- // Get The Direction And Distances To The Target
- //-----------------------------------------------
- VectorSubtract(NPCInfo->jumpDest, NPC->currentOrigin, targetDirection);
- targetDirection[2] = 0.0f;
- targetDistanceXY = VectorNormalize(targetDirection);
- targetDistanceZ = NPCInfo->jumpDest[2] - NPC->currentOrigin[2];
- if ((targetDistanceXY>NPCInfo->jumpMaxXYDist) ||
- (targetDistanceZ<NPCInfo->jumpMazZDist))
- {
- return qfalse;
- }
- // Test To See If There Is A Wall Directly In Front Of Actor, If So, Backup Some
- //-------------------------------------------------------------------------------
- if (TIMER_Done(NPC, "jumpBackupDebounce"))
- {
- vec3_t actorProjectedTowardTarget;
- VectorMA(NPC->currentOrigin, NPC_JUMP_PREP_BACKUP_DIST, targetDirection, actorProjectedTowardTarget);
- gi.trace(&mJumpTrace, NPC->currentOrigin, vec3_origin, vec3_origin, actorProjectedTowardTarget, NPC->s.number, NPC->clipmask);
- if ((mJumpTrace.fraction < 1.0f) ||
- (mJumpTrace.allsolid) ||
- (mJumpTrace.startsolid))
- {
- if (NAVDEBUG_showCollision)
- {
- CG_DrawEdge(NPC->currentOrigin, actorProjectedTowardTarget, EDGE_RED_TWOSECOND); // TryJump
- }
- // TODO: We may want to test to see if it is safe to back up here?
- NPCInfo->jumpBackupTime = level.time + 1000;
- TIMER_Set(NPC, "jumpBackupDebounce", 5000);
- return qtrue;
- }
- }
- // bool Wounded = (NPC->health < 150);
- // bool OnLowerLedge = ((targetDistanceZ<-80.0f) && (targetDistanceZ>-200.0f));
- // bool WithinNormalJumpRange = ((targetDistanceZ<32.0f) && (targetDistanceXY<200.0f));
- bool WithinForceJumpRange = ((fabsf(targetDistanceZ)>0) || (targetDistanceXY>128));
- /* if (Wounded && OnLowerLedge)
- {
- ucmd.forwardmove = 127;
- VectorClear(NPC->client->ps.moveDir);
- TIMER_Set(NPC, "duck", -level.time);
- return qtrue;
- }
-
- if (WithinNormalJumpRange)
- {
- ucmd.upmove = 127;
- ucmd.forwardmove = 127;
- VectorClear(NPC->client->ps.moveDir);
- TIMER_Set(NPC, "duck", -level.time);
- return qtrue;
- }
- */
- if (!WithinForceJumpRange)
- {
- return qfalse;
- }
- // If There Is Any Chance That This Jump Will Land On An Enemy, Try 8 Different Traces Around The Target
- //-------------------------------------------------------------------------------------------------------
- if (NPCInfo->jumpTarget)
- {
- float minSafeRadius = (NPC->maxs[0]*1.5f) + (NPCInfo->jumpTarget->maxs[0]*1.5f);
- float minSafeRadiusSq = (minSafeRadius * minSafeRadius);
- if (DistanceSquared(NPCInfo->jumpDest, NPCInfo->jumpTarget->currentOrigin)<minSafeRadiusSq)
- {
- vec3_t startPos;
- vec3_t floorPos;
- VectorCopy(NPCInfo->jumpDest, startPos);
- floorPos[2] = NPCInfo->jumpDest[2] + (NPC->mins[2]-32);
- for (int sideTryCount=0; sideTryCount<8; sideTryCount++)
- {
- NPCInfo->jumpSide++;
- if ( NPCInfo->jumpSide > 7 )
- {
- NPCInfo->jumpSide = 0;
- }
- switch ( NPCInfo->jumpSide )
- {
- case 0:
- NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius;
- NPCInfo->jumpDest[1] = startPos[1];
- break;
- case 1:
- NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius;
- NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius;
- break;
- case 2:
- NPCInfo->jumpDest[0] = startPos[0];
- NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius;
- break;
- case 3:
- NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius;
- NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius;
- break;
- case 4:
- NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius;
- NPCInfo->jumpDest[1] = startPos[1];
- break;
- case 5:
- NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius;
- NPCInfo->jumpDest[1] = startPos[1] - minSafeRadius;
- break;
- case 6:
- NPCInfo->jumpDest[0] = startPos[0];
- NPCInfo->jumpDest[1] = startPos[1] - minSafeRadius;
- break;
- case 7:
- NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius;
- NPCInfo->jumpDest[1] = startPos[1] -=minSafeRadius;
- break;
- }
- floorPos[0] = NPCInfo->jumpDest[0];
- floorPos[1] = NPCInfo->jumpDest[1];
- gi.trace(&mJumpTrace, NPCInfo->jumpDest, NPC->mins, NPC->maxs, floorPos, (NPCInfo->jumpTarget)?(NPCInfo->jumpTarget->s.number):(NPC->s.number), (NPC->clipmask|CONTENTS_BOTCLIP));
- if ((mJumpTrace.fraction<1.0f) &&
- (!mJumpTrace.allsolid) &&
- (!mJumpTrace.startsolid))
- {
- break;
- }
- if ( NAVDEBUG_showCollision )
- {
- CG_DrawEdge( NPCInfo->jumpDest, floorPos, EDGE_RED_TWOSECOND );
- }
- }
- // If All Traces Failed, Just Try Going Right Back At The Target Location
- //------------------------------------------------------------------------
- if ((mJumpTrace.fraction>=1.0f) ||
- (mJumpTrace.allsolid) ||
- (mJumpTrace.startsolid))
- {
- VectorCopy(startPos, NPCInfo->jumpDest);
- }
- }
- }
-
- // Now, Actually Try The Jump To The Dest Target
- //-----------------------------------------------
- if (NPC_Jump(NPCInfo->jumpDest, (NPCInfo->jumpTarget)?(NPCInfo->jumpTarget->s.number):(NPC->s.number)))
- {
- // We Made IT!
- //-------------
- NPC_JumpAnimation();
- NPC_JumpSound();
- NPC->client->ps.forceJumpZStart = NPC->currentOrigin[2];
- NPC->client->ps.pm_flags |= PMF_JUMPING;
- NPC->client->ps.weaponTime = NPC->client->ps.torsoAnimTimer;
- NPC->client->ps.forcePowersActive |= ( 1 << FP_LEVITATION );
- ucmd.forwardmove = 0;
- NPCInfo->jumpTime = 1;
- VectorClear(NPC->client->ps.moveDir);
- TIMER_Set(NPC, "duck", -level.time);
- return qtrue;
- }
- return qfalse;
- }
- qboolean NPC_Jumping()
- {
- if ( NPCInfo->jumpTime )
- {
- if ( !(NPC->client->ps.pm_flags & PMF_JUMPING )//forceJumpZStart )
- && !(NPC->client->ps.pm_flags&PMF_TRIGGER_PUSHED))
- {//landed
- NPCInfo->jumpTime = 0;
- }
- else
- {
- // if (NPCInfo->jumpTarget)
- // {
- // NPC_FaceEntity(NPCInfo->jumpTarget, qtrue);
- // }
- // else
- {
- NPC_FacePosition(NPCInfo->jumpDest, qtrue);
- }
- return qtrue;
- }
- }
- return qfalse;
- }
- qboolean NPC_JumpBackingUp()
- {
- if (NPCInfo->jumpBackupTime)
- {
- if (level.time<NPCInfo->jumpBackupTime)
- {
- STEER::Activate(NPC);
- STEER::Flee(NPC, NPCInfo->jumpDest);
- STEER::DeActivate(NPC, &ucmd);
- NPC_FacePosition(NPCInfo->jumpDest, qtrue);
- NPC_UpdateAngles( qfalse, qtrue );
- return qtrue;
- }
- NPCInfo->jumpBackupTime = 0;
- return NPC_TryJump();
- }
- return false;
- }
- /*
- -------------------------
- NPC_CheckCombatMove
- -------------------------
- */
- inline qboolean NPC_CheckCombatMove( void )
- {
- //return NPCInfo->combatMove;
- if ( ( NPCInfo->goalEntity && NPC->enemy && NPCInfo->goalEntity == NPC->enemy ) || ( NPCInfo->combatMove ) )
- {
- return qtrue;
- }
- if ( NPCInfo->goalEntity && NPCInfo->watchTarget )
- {
- if ( NPCInfo->goalEntity != NPCInfo->watchTarget )
- {
- return qtrue;
- }
- }
- return qfalse;
- }
- /*
- -------------------------
- NPC_LadderMove
- -------------------------
- */
- static void NPC_LadderMove( vec3_t dir )
- {
- //FIXME: this doesn't guarantee we're facing ladder
- //ALSO: Need to be able to get off at top
- //ALSO: Need to play an anim
- //ALSO: Need transitionary anims?
-
- if ( ( dir[2] > 0 ) || ( dir[2] < 0 && NPC->client->ps.groundEntityNum == ENTITYNUM_NONE ) )
- {
- //Set our movement direction
- ucmd.upmove = (dir[2] > 0) ? 127 : -127;
- //Don't move around on XY
- ucmd.forwardmove = ucmd.rightmove = 0;
- }
- }
- /*
- -------------------------
- NPC_GetMoveInformation
- -------------------------
- */
- inline qboolean NPC_GetMoveInformation( vec3_t dir, float *distance )
- {
- //NOTENOTE: Use path stacks!
- //Make sure we have somewhere to go
- if ( NPCInfo->goalEntity == NULL )
- return qfalse;
- //Get our move info
- VectorSubtract( NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, dir );
- *distance = VectorNormalize( dir );
-
- VectorCopy( NPCInfo->goalEntity->currentOrigin, NPCInfo->blockedTargetPosition );
- return qtrue;
- }
- /*
- -------------------------
- NAV_GetLastMove
- -------------------------
- */
- void NAV_GetLastMove( navInfo_t &info )
- {
- info = frameNavInfo;
- }
- void G_UcmdMoveForDir( gentity_t *self, usercmd_t *cmd, vec3_t dir )
- {
- vec3_t forward, right;
- AngleVectors( self->currentAngles, forward, right, NULL );
- dir[2] = 0;
- VectorNormalize( dir );
- //NPCs cheat and store this directly because converting movement into a ucmd loses precision
- VectorCopy( dir, self->client->ps.moveDir );
- float fDot = DotProduct( forward, dir ) * 127.0f;
- float rDot = DotProduct( right, dir ) * 127.0f;
- //Must clamp this because DotProduct is not guaranteed to return a number within -1 to 1, and that would be bad when we're shoving this into a signed byte
- if ( fDot > 127.0f )
- {
- fDot = 127.0f;
- }
- if ( fDot < -127.0f )
- {
- fDot = -127.0f;
- }
- if ( rDot > 127.0f )
- {
- rDot = 127.0f;
- }
- if ( rDot < -127.0f )
- {
- rDot = -127.0f;
- }
- cmd->forwardmove = floor(fDot);
- cmd->rightmove = floor(rDot);
- /*
- vec3_t wishvel;
- for ( int i = 0 ; i < 3 ; i++ )
- {
- wishvel[i] = forward[i]*cmd->forwardmove + right[i]*cmd->rightmove;
- }
- VectorNormalize( wishvel );
- if ( !VectorCompare( wishvel, dir ) )
- {
- Com_Printf( "PRECISION LOSS: %s != %s\n", vtos(wishvel), vtos(dir) );
- }
- */
- }
- /*
- -------------------------
- NPC_MoveToGoal
- Now assumes goal is goalEntity, was no reason for it to be otherwise
- -------------------------
- */
- #if AI_TIMERS
- extern int navTime;
- #endif// AI_TIMERS
- qboolean NPC_MoveToGoal( qboolean tryStraight ) //FIXME: tryStraight not even used! Stop passing it
- {
- #if AI_TIMERS
- int startTime = GetTime(0);
- #endif// AI_TIMERS
-
- if ( PM_InKnockDown( &NPC->client->ps ) || ( ( NPC->client->ps.legsAnim >= BOTH_PAIN1 ) && ( NPC->client->ps.legsAnim <= BOTH_PAIN18 ) && NPC->client->ps.legsAnimTimer > 0 ) )
- {//If taking full body pain, don't move
- return qtrue;
- }
- if( NPC->s.eFlags & EF_LOCKED_TO_WEAPON )
- {//If in an emplaced gun, never try to navigate!
- return qtrue;
- }
- if( NPC->s.eFlags & EF_HELD_BY_RANCOR )
- {//If in a rancor's hand, never try to navigate!
- return qtrue;
- }
- if( NPC->s.eFlags & EF_HELD_BY_WAMPA )
- {//If in a wampa's hand, never try to navigate!
- return qtrue;
- }
- if( NPC->s.eFlags & EF_HELD_BY_SAND_CREATURE )
- {//If in a worm's mouth, never try to navigate!
- return qtrue;
- }
- if ( NPC->watertype & CONTENTS_LADDER )
- {//Do we still want to do this?
- vec3_t dir;
- VectorSubtract( NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, dir );
- VectorNormalize( dir );
- NPC_LadderMove( dir );
- }
- bool moveSuccess = true;
- STEER::Activate(NPC);
- {
- // Attempt To Steer Directly To Our Goal
- //---------------------------------------
- moveSuccess = STEER::GoTo(NPC, NPCInfo->goalEntity, NPCInfo->goalRadius);
- // Perhaps Not Close Enough? Try To Use The Navigation Grid
- //-----------------------------------------------------------
- if (!moveSuccess)
- {
- moveSuccess = NAV::GoTo(NPC, NPCInfo->goalEntity);
- if (!moveSuccess)
- {
- STEER::Stop(NPC);
- }
- }
- }
- STEER::DeActivate(NPC, &ucmd);
- #if AI_TIMERS
- navTime += GetTime( startTime );
- #endif// AI_TIMERS
- return moveSuccess;
- }
- /*
- -------------------------
- void NPC_SlideMoveToGoal( void )
- Now assumes goal is goalEntity, if want to use tempGoal, you set that before calling the func
- -------------------------
- */
- qboolean NPC_SlideMoveToGoal( void )
- {
- float saveYaw = NPC->client->ps.viewangles[YAW];
- NPCInfo->combatMove = qtrue;
-
- qboolean ret = NPC_MoveToGoal( qtrue );
- NPCInfo->desiredYaw = saveYaw;
- return ret;
- }
- /*
- -------------------------
- NPC_ApplyRoff
- -------------------------
- */
- void NPC_ApplyRoff(void)
- {
- PlayerStateToEntityState( &NPC->client->ps, &NPC->s );
- VectorCopy ( NPC->currentOrigin, NPC->lastOrigin );
- // use the precise origin for linking
- gi.linkentity(NPC);
- }
|