/* ================ AI_Debug.cpp ================ */ #include "../../idlib/precompiled.h" #pragma hdrstop #include "../Game_local.h" #include "AI_Manager.h" #include "AI_Util.h" const char *aiMoveCommandString[ NUM_MOVE_COMMANDS ] = { "MOVE_NONE", "MOVE_FACE_ENEMY", "MOVE_FACE_ENTITY", "MOVE_TO_ENEMY", "MOVE_TO_ENTITY", "MOVE_TO_ATTACK", "MOVE_TO_HELPER", "MOVE_TO_TETHER", "MOVE_TO_COVER", "MOVE_TO_HIDE", "MOVE_TO_POSITION", "MOVE_OUT_OF_RANGE", "MOVE_SLIDE_TO_POSITION", "MOVE_WANDER" }; const char* aiMoveStatusString[ NUM_MOVE_STATUS ] = { "MOVE_STATUS_DONE", "MOVE_STATUS_MOVING", "MOVE_STATUS_WAITING", "MOVE_STATUS_DEST_NOT_FOUND", "MOVE_STATUS_DEST_UNREACHABLE", "MOVE_STATUS_BLOCKED_BY_WALL", "MOVE_STATUS_BLOCKED_BY_OBJECT", "MOVE_STATUS_BLOCKED_BY_ENEMY", "MOVE_STATUS_BLOCKED_BY_MONSTER", "MOVE_STATUS_BLOCKED_BY_PLAYER", "MOVE_STATUS_DISABLED" }; const char* aiMoveDirectionString [ MOVEDIR_MAX ] = { "MOVEDIR_FORWARD", "MOVEDIR_BACKWARD", "MOVEDIR_LEFT", "MOVEDIR_RIGHT" }; const char* aiTacticalString [ AITACTICAL_MAX ] = { "AITACTICAL_NONE", "AITACTICAL_MELEE", "AITACTICAL_MOVE_FOLLOW", "AITACTICAL_MOVE_TETHER", "AITACTICAL_MOVE_PLAYERPUSH", "AITACTICAL_COVER", "AITACTICAL_COVER_FLANK", "AITACTICAL_COVER_ADVANCE", "AITACTICAL_COVER_RETREAT", "AITACTICAL_COVER_AMBUSH", "AITACTICAL_RANGED", "AITACTICAL_TURRET", "AITACTICAL_HIDE", "AITACTICAL_PASSIVE", }; const char* aiFocusString [ AIFOCUS_MAX ] = { "AIFOCUS_NONE", "AIFOCUS_LEADER", "AIFOCUS_TARGET", "AIFOCUS_TALK", "AIFOCUS_PLAYER", "AIFOCUS_ENEMY", "AIFOCUS_COVER", "AIFOCUS_COVERLOOK", "AIFOCUS_HELPER", "AIFOCUS_TETHER", }; const char* aiActionStatusString [ rvAIAction::STATUS_MAX ] = { "Unused", "OK", "Disabled", "Failed: timer", "Failed: external timer", "Failed: within min range", "Failed: out of max range", "Failed: random chance", "Failed: bad animation", "Failed: condition", "Failed: no enemy", }; /* ===================== idAI::GetDebugInfo ===================== */ void idAI::GetDebugInfo ( debugInfoProc_t proc, void* userData ) { // Base class first idActor::GetDebugInfo ( proc, userData ); proc ( "idAI", "aifl.damage", aifl.damage ? "true" : "false", userData ); proc ( "idAI", "aifl.undying", aifl.undying ? "true" : "false", userData ); proc ( "idAI", "aifl.pain", aifl.pain ? "true" : "false", userData ); proc ( "idAI", "aifl.dead", aifl.dead ? "true" : "false", userData ); proc ( "idAI", "aifl.activated", aifl.activated ? "true" : "false", userData ); proc ( "idAI", "aifl.jump", aifl.jump ? "true" : "false", userData ); proc ( "idAI", "aifl.hitEnemy", aifl.hitEnemy ? "true" : "false", userData ); proc ( "idAI", "aifl.pushed", aifl.pushed ? "true" : "false", userData ); proc ( "idAI", "aifl.lookAtPlayer", aifl.lookAtPlayer ? "true" : "false", userData ); proc ( "idAI", "aifl.disableAttacks", aifl.disableAttacks ? "true" : "false", userData ); proc ( "idAI", "aifl.simpleThink", aifl.simpleThink ? "true" : "false", userData ); proc ( "idAI", "aifl.action", aifl.action ? "true" : "false", userData ); proc ( "idAI", "aifl.scripted", aifl.scripted? "true" : "false", userData ); proc ( "idAI", "leader", leader.GetEntity() ? leader.GetEntity()->GetName() : "", userData ); proc ( "idAI", "move.followRangeMin", va("%g",move.followRange[0]), userData ); proc ( "idAI", "move.followRangeMax", va("%g",move.followRange[1]), userData ); proc ( "idAI", "combat.fl.aware", combat.fl.aware ? "true" : "false", userData ); proc ( "idAI", "combat.fl.ignoreEnemies", combat.fl.ignoreEnemies ? "true" : "false", userData ); proc ( "idAI", "enemy", enemy.ent ? enemy.ent->GetName() : "", userData ); proc ( "idAI", "enemy.fl.inFov", enemy.fl.inFov ? "true" : "false", userData ); proc ( "idAI", "enemy.fl.visible", enemy.fl.visible ? "true" : "false", userData ); proc ( "idAI", "combat.fl.seenEnemyDirectly",combat.fl.seenEnemyDirectly ? "true" : "false", userData ); proc ( "idAI", "enemy.lastVisibleTime", va("%d",enemy.lastVisibleTime), userData ); proc ( "idAI", "combat.maxLostVisTime", va("%d",combat.maxLostVisTime), userData ); proc ( "idAI", "enemy.range", va("%g",enemy.range), userData ); proc ( "idAI", "enemy.ranged2d", va("%g",enemy.range2d), userData ); proc ( "idAI", "lastAttackTime", va("%d",lastAttackTime), userData ); proc ( "idAI", "move.fl.done", move.fl.done ? "true" : "false", userData ); proc ( "idAI", "move.fl.disabled", move.fl.disabled ? "true" : "false", userData ); proc ( "idAI", "move.fl.onGround", move.fl.onGround ? "true" : "false", userData ); proc ( "idAI", "move.fl.blocked", move.fl.blocked ? "true" : "false", userData ); proc ( "idAI", "move.fl.obstacleInPath", move.fl.obstacleInPath ? "true" : "false", userData ); proc ( "idAI", "move.fl.goalUnreachable", move.fl.goalUnreachable ? "true" : "false", userData ); proc ( "idAI", "move.fl.moving", move.fl.moving ? "true" : "false", userData ); proc ( "idAI", "move.command", aiMoveCommandString[move.moveCommand], userData ); proc ( "idAI", "move.status", aiMoveStatusString[move.moveStatus], userData ); proc ( "idAI", "move.fl.allowDirectional", move.fl.allowDirectional ? "true" : "false", userData ); proc ( "idAI", "move.direction_ideal", aiMoveDirectionString[move.idealDirection ], userData ); proc ( "idAI", "move.direction_current", aiMoveDirectionString[move.currentDirection ], userData ); proc ( "idAI", "move.yaw_ideal", va("%d",(int)move.ideal_yaw), userData ); proc ( "idAI", "move.yaw_current", va("%d",(int)move.current_yaw), userData ); proc ( "idAI", "move.fly_roll", va("%g", move.fly_roll ), userData ); proc ( "idAI", "move.fly_pitch", va("%g", move.fly_pitch ), userData ); proc ( "idAI", "tether", tether!=NULL?tether->GetName():"", userData ); proc ( "idAI", "IsTethered()", IsTethered()?"true":"false", userData ); proc ( "idAI", "lookTarget", lookTarget.GetEntity() ? lookTarget.GetEntity()->GetName() : "", userData ); proc ( "idAI", "talkTarget", talkTarget.GetEntity() ? talkTarget.GetEntity()->GetName() : "", userData ); proc ( "idAI", "focusType", aiFocusString[focusType], userData ); proc ( "idAI", "look.yaw", va("%g", lookAng.yaw ), userData ); proc ( "idAI", "look.pitch", va("%g", lookAng.pitch ), userData ); proc ( "idAI", "combat.attackRangeMin", va("%g",combat.attackRange[0]), userData ); proc ( "idAI", "combat.attackRangeMax", va("%g",combat.attackRange[1]), userData ); proc ( "idAI", "combat.tacticalCurrent", aiTacticalString[combat.tacticalCurrent], userData ); proc ( "idAI", "action_rangedAttack", aiActionStatusString[actionRangedAttack.status], userData ); proc ( "idAI", "action_meleeAttack", aiActionStatusString[actionMeleeAttack.status], userData ); proc ( "idAI", "action_leapAttack", aiActionStatusString[actionLeapAttack.status], userData ); proc ( "idAI", "action_jumpBack", aiActionStatusString[actionJumpBack.status], userData ); proc ( "idAI", "action_evadeLeft", aiActionStatusString[actionEvadeLeft.status], userData ); proc ( "idAI", "action_evadeRight", aiActionStatusString[actionEvadeRight.status], userData ); proc ( "idAI", "npc_name", spawnArgs.FindKey("npc_name")?common->GetLocalizedString(spawnArgs.GetString( "npc_name", "")):"", userData ); } /* ===================== idAI::DrawRoute ===================== */ void idAI::DrawRoute( void ) const { if ( aas && move.toAreaNum && move.moveCommand != MOVE_NONE && move.moveCommand != MOVE_WANDER && move.moveCommand != MOVE_FACE_ENEMY && move.moveCommand != MOVE_FACE_ENTITY ) { if ( move.moveType == MOVETYPE_FLY ) { aas->ShowFlyPath( physicsObj.GetOrigin(), move.toAreaNum, move.moveDest ); } else { aas->ShowWalkPath( physicsObj.GetOrigin(), move.toAreaNum, move.moveDest ); } } } /* ===================== idAI::DrawTactical Draw the debug tactical information ===================== */ void idAI::DrawTactical ( void ) { if ( !DebugFilter(ai_debugTactical) ) { return; } // Colors For Lines In This File //------------------------------- // Majenta = Move Dest // Green / Red = Enemy (On Player Side, On Enemy Side) // Pink = Tether Radius // Grey = FOV // Colors From AAST Draw Debug Info //---------------------------------- // Blue = Reserved Feature // Orange = Near Feature // Yellow = Look Feature // Get Origin //------------ idVec3 origin = GetPhysics()->GetOrigin(); origin += (GetPhysics()->GetGravityNormal() * 5.0f); // Draw FOV (Must be close to player and on enemy team) //------------------------------------------------------ if (team && DistanceTo(gameLocal.GetLocalPlayer())<500.0f) { if (!combat.fl.aware) { gameRenderWorld->DebugFOV(colorLtGrey, origin, viewAxis[0], fovDot, 300.0f, fovCloseDot, fovCloseRange, 0.3f, 10); } else if (!combat.fl.seenEnemyDirectly) { gameRenderWorld->DebugFOV(colorMdGrey, origin, viewAxis[0], fovDot, 300.0f, -1.0f, combat.awareRange, 0.3f, 10); } else { float alpha = (1.0f - ((float)(gameLocal.GetTime() - enemy.lastVisibleTime) / (float)combat.maxLostVisTime)) * 0.35f; gameRenderWorld->DebugFOV(colorDkGrey, origin, viewAxis[0], fovDot, 300.0f, -1.0f, combat.awareRange, Max(alpha, 0.1f), 10); } } // Move Over Head //---------------- origin -= GetPhysics()->GetGravityNormal() * (GetPhysics()->GetBounds()[ 1 ].z - GetPhysics()->GetBounds()[ 0 ].z); // Draw Enemy Related Info //------------------------- if ( enemy.ent ) { // Draw Lost Time origin -= (GetPhysics()->GetGravityNormal() * 5.0f); if ( enemy.lastVisibleTime && (gameLocal.GetTime() - enemy.lastVisibleTime) > combat.maxLostVisTime ) { gameRenderWorld->DrawText ( va("losttime: %d", (gameLocal.GetTime() - enemy.lastVisibleTime)), origin, 0.12f, colorRed, gameLocal.GetLocalPlayer()->viewAxis ); } else if ( enemy.lastVisibleTime && (gameLocal.GetTime() - enemy.lastVisibleTime) > ( combat.maxLostVisTime/2 ) ) { gameRenderWorld->DrawText ( va("losttime: %d", (gameLocal.GetTime() - enemy.lastVisibleTime)), origin, 0.12f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis ); } else if ( enemy.lastVisibleTime ) { gameRenderWorld->DrawText ( va("losttime: %d", (gameLocal.GetTime() - enemy.lastVisibleTime)), origin, 0.12f, colorGreen, gameLocal.GetLocalPlayer()->viewAxis ); } // Enemy Chest position for enemy lines idVec3 enemyEyePosition; idVec3 offset; if ( enemy.ent->IsType ( idActor::GetClassType() ) ){ enemyEyePosition = static_cast(enemy.ent.GetEntity())->GetEyePosition ( ); } else { enemyEyePosition = enemy.ent->GetPhysics()->GetOrigin(); } offset = idVec3(0,0,team*2.0f); gameRenderWorld->DebugLine ( aiTeamColor[team], GetEyePosition() + offset, enemy.lastVisibleFromEyePosition + offset, 4.0f ); if ( enemyEyePosition != enemy.lastVisibleEyePosition ) { gameRenderWorld->DebugLine ( aiTeamColor[team], enemy.lastVisibleFromEyePosition + offset, enemy.lastVisibleEyePosition + offset, 4.0f ); gameRenderWorld->DebugArrow ( aiTeamColor[team], enemy.lastVisibleEyePosition + offset, enemyEyePosition + offset, 4.0f ); } else { gameRenderWorld->DebugArrow ( aiTeamColor[team], enemy.lastVisibleFromEyePosition + offset, enemyEyePosition + offset, 4.0f ); } } // Ivalid Cover Timer //-------------------- if ( IsBehindCover() && combat.coverValidTime && combat.coverValidTime < gameLocal.time) { origin -= (GetPhysics()->GetGravityNormal() * 5.0f); gameRenderWorld->DrawText ( va("invalid cover time: %d", (gameLocal.GetTime() - combat.coverValidTime)), origin, 0.12f, colorRed, gameLocal.GetLocalPlayer()->viewAxis ); } // Vis Crouch gameRenderWorld->DebugArrow ( colorBrown, GetPhysics()->GetOrigin ( ) - GetPhysics()->GetGravityNormal() * combat.visCrouchHeight, GetPhysics()->GetOrigin ( ) - GetPhysics()->GetGravityNormal() * combat.visCrouchHeight + viewAxis[0] * 16.0f, 10.0f ); // Vis Stand gameRenderWorld->DebugArrow ( colorBrown, GetPhysics()->GetOrigin ( ) - GetPhysics()->GetGravityNormal() * combat.visStandHeight, GetPhysics()->GetOrigin ( ) - GetPhysics()->GetGravityNormal() * combat.visStandHeight + viewAxis[0] * 16.0f, 10.0f ); // Aggression if ( combat.aggressiveScale != 1.0f ) { origin -= (GetPhysics()->GetGravityNormal() * 5.0f); gameRenderWorld->DrawText ( va("aggressive: %g", combat.aggressiveScale), origin, 0.12f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis ); } // Draw anim prefix //----------------------- if ( animPrefix.Length ( ) ) { origin -= (GetPhysics()->GetGravityNormal() * 5.0f); gameRenderWorld->DrawText ( va("anim: %s", animPrefix.c_str()), origin, 0.12f, colorCyan, gameLocal.GetLocalPlayer()->viewAxis ); } // Draw focus type //----------------------- if ( focusType != AIFOCUS_NONE ) { origin -= (GetPhysics()->GetGravityNormal() * 5.0f); gameRenderWorld->DrawText ( aiFocusString[focusType], origin, 0.12f, colorCyan, gameLocal.GetLocalPlayer()->viewAxis ); } // Draw Tactical Current //----------------------- origin -= (GetPhysics()->GetGravityNormal() * 5.0f); gameRenderWorld->DrawText ( aiTacticalString[combat.tacticalCurrent], origin, 0.12f, colorYellow, gameLocal.GetLocalPlayer()->viewAxis ); // Draw My Name //-------------- origin -= (GetPhysics()->GetGravityNormal() * 5.0f); gameRenderWorld->DrawText(name, origin, 0.12f, colorWhite, gameLocal.GetLocalPlayer()->viewAxis); // Draw the tethered radius if ( IsTethered ( ) ) { tether->DebugDraw ( ); } // Draw Move Destination if ( !move.fl.done ) { gameRenderWorld->DebugArrow ( colorMagenta, GetPhysics()->GetOrigin(), move.moveDest, 5 ); } }