void NPC_TempLookTarget( gentity_t *self, int lookEntNum, int minLookTime, int maxLookTime ) { if ( !self->client ) { return; } if ( (self->client->ps.eFlags2&EF2_HELD_BY_MONSTER) ) { //lookTarget is set by and to the monster that's holding you, no other operations can change that return; } if ( !minLookTime ) { minLookTime = 1000; } if ( !maxLookTime ) { maxLookTime = 1000; } if ( !NPC_CheckLookTarget( self ) ) { //Not already looking at something else //Look at him for 1 to 3 seconds NPC_SetLookTarget( self, lookEntNum, level.time + Q_irand( minLookTime, maxLookTime ) ); } }
void NPC_TempLookTarget( gentity_t *self, int lookEntNum, int minLookTime, int maxLookTime ) { if ( !self->client ) { return; } if ( !minLookTime ) { minLookTime = 1000; } if ( !maxLookTime ) { maxLookTime = 1000; } if ( !NPC_CheckLookTarget( self ) ) {//Not already looking at something else //Look at him for 1 to 3 seconds NPC_SetLookTarget( self, lookEntNum, level.time + Q_irand( minLookTime, maxLookTime ) ); } }
/* =============== NPC_ExecuteBState MCG NPC Behavior state thinking =============== */ void NPC_ExecuteBState ( gentity_t *self)//, int msec ) { bState_t bState; NPC_HandleAIFlags(); //FIXME: these next three bits could be a function call, some sort of setup/cleanup func //Lookmode must be reset every think cycle if(NPC->aimDebounceTime < level.time) { NPCInfo->lookMode = LT_NONE; } if(NPC->delayScriptTime && NPC->delayScriptTime <= level.time) { G_ActivateBehavior( NPC, BSET_DELAYED); NPC->delayScriptTime = 0; } //Clear this and let bState set it itself, so it automatically handles changing bStates... but we need a set bState wrapper func NPCInfo->combatMove = qfalse; //Execute our bState if(NPCInfo->tempBehavior) {//Overrides normal behavior until cleared bState = NPCInfo->tempBehavior; } else { if(!NPCInfo->behaviorState) NPCInfo->behaviorState = NPCInfo->defaultBehavior; bState = NPCInfo->behaviorState; } //Pick the proper bstate for us and run it NPC_RunBehavior( self->client->playerTeam, bState ); //FIXME: Make these a func call if(bState != BS_FORMATION) {//So we know to re-acquire our closest squadpath point self->NPC->lastSquadPoint = -1; // NPCInfo->aiFlags |= NPCAI_OFF_PATH; } if(bState != BS_POINT_COMBAT && NPCInfo->combatPoint != -1) { //level.combatPoints[NPCInfo->combatPoint].occupied = qfalse; //NPCInfo->combatPoint = -1; } //Here we need to see what the scripted stuff told us to do //Only process snapshot if independant and in combat mode- this would pick enemies and go after needed items // ProcessSnapshot(); //Ignore my needs if I'm under script control- this would set needs for items // CheckSelf(); //Back to normal? All decisions made? //FIXME: don't walk off ledges unless we can get to our goal faster that way, or that's our goal's surface //NPCPredict(); if ( NPC->enemy ) { if ( !NPC->enemy->inuse ) {//just in case bState doesn't catch this G_ClearEnemy( NPC ); } } if ( !NPC_CheckLookTarget( NPC ) ) { if ( NPC->enemy ) { if ( NPC->client->ps.weapon != WP_IMPERIAL_BLADE && NPC->client->ps.weapon != WP_KLINGON_BLADE ) {//looking right at enemy during melee looks odd NPC_SetLookTarget( NPC, NPC->enemy->s.number, 0 ); } } } if ( NPC->enemy ) { if(NPC->enemy->flags & FL_DONT_SHOOT) { ucmd.buttons &= ~BUTTON_ATTACK; } if(client->ps.weaponstate == WEAPON_IDLE) { client->ps.weaponstate = WEAPON_READY; } } else { if(client->ps.weaponstate == WEAPON_READY) { client->ps.weaponstate = WEAPON_IDLE; } } if(!(ucmd.buttons & BUTTON_ATTACK) && NPC->attackDebounceTime > level.time) {//We just shot but aren't still shooting, so hold the gun up for a while if(client->ps.weapon == WP_PHASER ) {//One-handed NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); } else if(client->ps.weapon == WP_COMPRESSION_RIFLE) {//Sniper pose NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); } /*//FIXME: What's the proper solution here? else {//heavy weapon NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); } */ } else if (!NPC->enemy && bState != BS_FORMATION)//HACK! { if(client->ps.weapon != WP_TRICORDER) { if((NPC->s.torsoAnim&~ANIM_TOGGLEBIT) == TORSO_WEAPONREADY1 || (NPC->s.torsoAnim&~ANIM_TOGGLEBIT) == TORSO_WEAPONREADY2) {//we look ready for action, using one of the first 2 weapon, let's rest our weapon on our shoulder NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); } } } NPC_CheckAttackHold(); NPC_ApplyScriptFlags(); //cliff and wall avoidance NPC_AvoidWallsAndCliffs(); // run the bot through the server like it was a real client //=== Save the ucmd for the second no-think Pmove ============================ ucmd.serverTime = level.time - 50; NPCInfo->last_ucmd = ucmd; if ( !NPCInfo->attackHoldTime ) { NPCInfo->last_ucmd.buttons &= ~BUTTON_ATTACK;//so we don't fire twice in one think } //============================================================================ NPC_CheckAttackScript(); NPC_KeepCurrentFacing(); if ( !NPC->next_roff_time || NPC->next_roff_time < level.time ) {//If we were following a roff, we don't do normal pmoves. ClientThink( NPC->s.number, &ucmd ); } else { NPC_ApplyRoff(); } //Had to leave this in, some legacy code must still be using s.angles //Shouldn't interfere with interpolation of angles, should it? VectorCopy( client->ps.viewangles, NPC->currentAngles ); // end of thinking cleanup NPCInfo->touchedByPlayer = NULL; NPC_CheckPlayerAim(); NPC_CheckAllClear(); /*if( ucmd.forwardmove || ucmd.rightmove ) { int i, la = -1, ta = -1; for(i = 0; i < MAX_ANIMATIONS; i++) { if((NPC->client->ps.legsAnim&~ANIM_TOGGLEBIT) == i) { la = i; } if((NPC->client->ps.torsoAnim&~ANIM_TOGGLEBIT) == i) { ta = i; } if(la != -1 && ta != -1) { break; } } if(la != -1 && ta != -1) {//FIXME: should never play same frame twice or restart an anim before finishing it gi.Printf("LegsAnim: %s(%d) TorsoAnim: %s(%d)\n", animTable[la].name, NPC->renderInfo.legsFrame, animTable[ta].name, NPC->client->renderInfo.torsoFrame); } }*/ }
/* =============== NPC_ExecuteBState MCG NPC Behavior state thinking =============== */ void NPC_ExecuteBState ( gentity_t *self)//, int msec ) { bState_t bState; NPC_HandleAIFlags(); //FIXME: these next three bits could be a function call, some sort of setup/cleanup func //Lookmode must be reset every think cycle if(NPC->delayScriptTime && NPC->delayScriptTime <= level.time) { G_ActivateBehavior( NPC, BSET_DELAYED); NPC->delayScriptTime = 0; } //Clear this and let bState set it itself, so it automatically handles changing bStates... but we need a set bState wrapper func NPCInfo->combatMove = qfalse; //Execute our bState if(NPCInfo->tempBehavior) {//Overrides normal behavior until cleared bState = NPCInfo->tempBehavior; } else { if(!NPCInfo->behaviorState) NPCInfo->behaviorState = NPCInfo->defaultBehavior; bState = NPCInfo->behaviorState; } //Pick the proper bstate for us and run it NPC_RunBehavior( self->client->playerTeam, bState ); // if(bState != BS_POINT_COMBAT && NPCInfo->combatPoint != -1) // { //level.combatPoints[NPCInfo->combatPoint].occupied = qfalse; //NPCInfo->combatPoint = -1; // } //Here we need to see what the scripted stuff told us to do //Only process snapshot if independant and in combat mode- this would pick enemies and go after needed items // ProcessSnapshot(); //Ignore my needs if I'm under script control- this would set needs for items // CheckSelf(); //Back to normal? All decisions made? //FIXME: don't walk off ledges unless we can get to our goal faster that way, or that's our goal's surface //NPCPredict(); if ( NPC->enemy ) { if ( !NPC->enemy->inuse ) {//just in case bState doesn't catch this G_ClearEnemy( NPC ); } } if ( NPC->client->ps.saberLockTime && NPC->client->ps.saberLockEnemy != ENTITYNUM_NONE ) { NPC_SetLookTarget( NPC, NPC->client->ps.saberLockEnemy, level.time+1000 ); } else if ( !NPC_CheckLookTarget( NPC ) ) { if ( NPC->enemy ) { NPC_SetLookTarget( NPC, NPC->enemy->s.number, 0 ); } } if ( NPC->enemy ) { if(NPC->enemy->flags & FL_DONT_SHOOT) { ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons &= ~BUTTON_ALT_ATTACK; } else if ( NPC->client->playerTeam != NPCTEAM_ENEMY && NPC->enemy->NPC && (NPC->enemy->NPC->surrenderTime > level.time || (NPC->enemy->NPC->scriptFlags&SCF_FORCED_MARCH)) ) {//don't shoot someone who's surrendering if you're a good guy ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons &= ~BUTTON_ALT_ATTACK; } if(client->ps.weaponstate == WEAPON_IDLE) { client->ps.weaponstate = WEAPON_READY; } } else { if(client->ps.weaponstate == WEAPON_READY) { client->ps.weaponstate = WEAPON_IDLE; } } if(!(ucmd.buttons & BUTTON_ATTACK) && NPC->attackDebounceTime > level.time) {//We just shot but aren't still shooting, so hold the gun up for a while if(client->ps.weapon == WP_SABER ) {//One-handed NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); } else if(client->ps.weapon == WP_BRYAR_PISTOL) {//Sniper pose NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); } /*//FIXME: What's the proper solution here? else {//heavy weapon NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); } */ } else if ( !NPC->enemy )//HACK! { // if(client->ps.weapon != WP_TRICORDER) { if( NPC->s.torsoAnim == TORSO_WEAPONREADY1 || NPC->s.torsoAnim == TORSO_WEAPONREADY3 ) {//we look ready for action, using one of the first 2 weapon, let's rest our weapon on our shoulder NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); } } } NPC_CheckAttackHold(); NPC_ApplyScriptFlags(); //cliff and wall avoidance NPC_AvoidWallsAndCliffs(); // run the bot through the server like it was a real client //=== Save the ucmd for the second no-think Pmove ============================ ucmd.serverTime = level.time - 50; memcpy( &NPCInfo->last_ucmd, &ucmd, sizeof( usercmd_t ) ); if ( !NPCInfo->attackHoldTime ) { NPCInfo->last_ucmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);//so we don't fire twice in one think } //============================================================================ NPC_CheckAttackScript(); NPC_KeepCurrentFacing(); if ( !NPC->next_roff_time || NPC->next_roff_time < level.time ) {//If we were following a roff, we don't do normal pmoves. ClientThink( NPC->s.number, &ucmd ); } else { NPC_ApplyRoff(); } // end of thinking cleanup NPCInfo->touchedByPlayer = NULL; NPC_CheckPlayerAim(); NPC_CheckAllClear(); /*if( ucmd.forwardmove || ucmd.rightmove ) { int i, la = -1, ta = -1; for(i = 0; i < MAX_ANIMATIONS; i++) { if( NPC->client->ps.legsAnim == i ) { la = i; } if( NPC->client->ps.torsoAnim == i ) { ta = i; } if(la != -1 && ta != -1) { break; } } if(la != -1 && ta != -1) {//FIXME: should never play same frame twice or restart an anim before finishing it Com_Printf("LegsAnim: %s(%d) TorsoAnim: %s(%d)\n", animTable[la].name, NPC->renderInfo.legsFrame, animTable[ta].name, NPC->client->renderInfo.torsoFrame); } }*/ }
/* =============== NPC_ExecuteBState MCG NPC Behavior state thinking =============== */ void NPC_ExecuteBState ( gentity_t *self)//, int msec ) { bState_t bState; NPC_HandleAIFlags(); //FIXME: these next three bits could be a function call, some sort of setup/cleanup func //Lookmode must be reset every think cycle if(NPC->delayScriptTime && NPC->delayScriptTime <= level.time) { G_ActivateBehavior( NPC, BSET_DELAYED); NPC->delayScriptTime = 0; } //Clear this and let bState set it itself, so it automatically handles changing bStates... but we need a set bState wrapper func NPCInfo->combatMove = qfalse; //Execute our bState if(NPCInfo->tempBehavior) {//Overrides normal behavior until cleared bState = NPCInfo->tempBehavior; } else { if(!NPCInfo->behaviorState) NPCInfo->behaviorState = NPCInfo->defaultBehavior; bState = NPCInfo->behaviorState; } //Pick the proper bstate for us and run it NPC_RunBehavior( self->client->playerTeam, bState ); if ( NPC->enemy ) { if ( !NPC->enemy->inuse ) {//just in case bState doesn't catch this G_ClearEnemy( NPC ); } } if ( NPC->client->ps.saberLockTime && NPC->client->ps.saberLockEnemy != ENTITYNUM_NONE ) { NPC_SetLookTarget( NPC, NPC->client->ps.saberLockEnemy, level.time+1000 ); } else if ( !NPC_CheckLookTarget( NPC ) ) { if ( NPC->enemy ) { NPC_SetLookTarget( NPC, NPC->enemy->s.number, 0 ); } } if ( NPC->enemy ) { if(NPC->enemy->flags & FL_DONT_SHOOT) { ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons &= ~BUTTON_ALT_ATTACK; } else if ( NPC->client->playerTeam != NPCTEAM_ENEMY && NPC->enemy->NPC && (NPC->enemy->NPC->surrenderTime > level.time || (NPC->enemy->NPC->scriptFlags&SCF_FORCED_MARCH)) ) {//don't shoot someone who's surrendering if you're a good guy ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons &= ~BUTTON_ALT_ATTACK; } if(client->ps.weaponstate == WEAPON_IDLE) { client->ps.weaponstate = WEAPON_READY; } } else { if(client->ps.weaponstate == WEAPON_READY) { client->ps.weaponstate = WEAPON_IDLE; } } if(!(ucmd.buttons & BUTTON_ATTACK) && NPC->attackDebounceTime > level.time) {//We just shot but aren't still shooting, so hold the gun up for a while if(client->ps.weapon == WP_SABER ) {//One-handed NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); } else if(client->ps.weapon == WP_BRYAR_PISTOL) {//Sniper pose NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); } } else if ( !NPC->enemy )//HACK! { { if( NPC->s.torsoAnim == TORSO_WEAPONREADY1 || NPC->s.torsoAnim == TORSO_WEAPONREADY3 ) {//we look ready for action, using one of the first 2 weapon, let's rest our weapon on our shoulder NPC_SetAnim(NPC,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); } } } NPC_CheckAttackHold(); NPC_ApplyScriptFlags(); //cliff and wall avoidance NPC_AvoidWallsAndCliffs(); // run the bot through the server like it was a real client //=== Save the ucmd for the second no-think Pmove ============================ ucmd.serverTime = level.time - 50; memcpy( &NPCInfo->last_ucmd, &ucmd, sizeof( usercmd_t ) ); if ( !NPCInfo->attackHoldTime ) { NPCInfo->last_ucmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);//so we don't fire twice in one think } //============================================================================ NPC_CheckAttackScript(); NPC_KeepCurrentFacing(); if ( !NPC->next_roff_time || NPC->next_roff_time < level.time ) {//If we were following a roff, we don't do normal pmoves. ClientThink( NPC->s.number, &ucmd ); } else { NPC_ApplyRoff(); } // end of thinking cleanup NPCInfo->touchedByPlayer = NULL; NPC_CheckPlayerAim(); NPC_CheckAllClear(); }