diff --git a/GameSettings.cpp b/GameSettings.cpp index a3f31c3f..7ddbed49 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2550,7 +2550,8 @@ void LoadSkillTraitsExternalSettings() gSkillTraitValues.sCOCloseDetectionRangeSoldierCorpse = iniReader.ReadInteger("Covert Ops","COVERT_CLOSE_DETECTION_RANGE_SOLDIER_CORPSE", 5, 0, 100); gSkillTraitValues.usCOEliteUncoverRadius = iniReader.ReadInteger("Covert Ops","COVERT_ELITES_UNCOVER_RADIUS", 6, 0, 20 ); gSkillTraitValues.fCODetectIfBleeding = iniReader.ReadBoolean("Covert Ops","COVERT_DETECTEDIFBLEEDING", FALSE ); - + gSkillTraitValues.fCOStripIfUncovered = iniReader.ReadBoolean("Covert Ops","COVERT_STRIPIFUNCOVERED", FALSE ); + // Flugente: RADIO OPERATOR gSkillTraitValues.fROAllowArtillery = iniReader.ReadBoolean("Radio Operator","RADIO_OPERATOR_ARTILLERY", TRUE); gSkillTraitValues.fROArtilleryDistributedOverTurns = iniReader.ReadBoolean("Radio Operator","RADIO_OPERATOR_ARTILLERY_DISTRIBUTED_OVER_TURNS", FALSE); diff --git a/GameSettings.h b/GameSettings.h index d6a14bc7..ad532e5e 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1836,6 +1836,7 @@ typedef struct INT16 sCOCloseDetectionRangeSoldierCorpse; UINT16 usCOEliteUncoverRadius; BOOLEAN fCODetectIfBleeding; // will we be discovered if we are bleeding? + BOOLEAN fCOStripIfUncovered; // RADIO OPERATOR BOOLEAN fROAllowArtillery; // artillery can be forbidden for all teams with this setting diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 043f2560..710950cf 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -155,7 +155,6 @@ void StartBombMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo ); void StartTacticalFunctionSelectionMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo, INT8 bLevel ); void CleanWeapons( BOOLEAN fEntireTeam ); void UpdateGear(); -void Strip( SOLDIERTYPE * pSoldier ); void StartCorpseMessageBox( SOLDIERTYPE * pSoldier, INT32 sGridNo, INT8 bLevel ); BOOLEAN HandleCheckForBadChangeToGetThrough( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, INT32 sTargetGridNo , INT8 bLevel ) @@ -5177,12 +5176,6 @@ void UpdateGear() } } -// undisguise or take off custom clothes -void Strip( SOLDIERTYPE * pSoldier ) -{ - pSoldier->Strip(); -} - void BombMessageBoxCallBack( UINT8 ubExitValue ) { if (gpTempSoldier) @@ -5331,7 +5324,7 @@ void TacticalFunctionSelectionMessageBoxCallBack( UINT8 ubExitValue ) break; case 2: // undisguise or take off custom clothes - Strip(gpTempSoldier); + gpTempSoldier->Strip(); break; case 3: // clean weapons - in realtime of the entire team, in turnbased only for the selected merc diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 7beb9793..d50a6a35 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -60,6 +60,7 @@ #include "CampaignStats.h" // added by Flugente #include "DisplayCover.h" // added by Flugente #include "Map Information.h" + #include "ai.h" // added by Flugente #endif #ifdef JA2UB @@ -9782,44 +9783,8 @@ BOOLEAN ApplyClothes( SOLDIERTYPE * pSoldier, UINT16 usItem, UINT16 usPointsToUs pSoldier->CreateSoldierPalettes(); } - if ( pSoldier->usSoldierFlagMask & SOLDIER_NEW_VEST && pSoldier->usSoldierFlagMask & SOLDIER_NEW_PANTS ) - { - // first, remove the covert flags, and then reapply the correct ones, in case we switch between civilian and military clothes - pSoldier->usSoldierFlagMask &= ~(SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER); - - // we can onyl dsiguise sucessfully if we are not seen - INT8 bCover = MAX_COVER; - CalculateCoverForSoldier( pSoldier, pSoldier->sGridNo, pSoldier->pathing.bLevel, bCover ); - if ( bCover == MAX_COVER ) - { - // we now have to determine wether we are currently wearing civilian or military clothes - for ( UINT8 i = UNIFORM_ENEMY_ADMIN; i <= UNIFORM_ENEMY_ELITE; ++i ) - { - // both parts have to fit. We cant mix different uniforms and get soldier disguise - if ( COMPARE_PALETTEREP_ID(pSoldier->VestPal, gUniformColors[ i ].vest) && COMPARE_PALETTEREP_ID(pSoldier->PantsPal, gUniformColors[ i ].pants) ) - { - pSoldier->usSoldierFlagMask |= SOLDIER_COVERT_SOLDIER; - - if ( pSoldier->bTeam == OUR_TEAM ) - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_DISGUISED_AS_SOLDIER], pSoldier->GetName() ); - - break; - } - } - - // if not dressed as a soldier, we must be dressed as a civilian - if ( !(pSoldier->usSoldierFlagMask & SOLDIER_COVERT_SOLDIER) ) - { - pSoldier->usSoldierFlagMask |= SOLDIER_COVERT_CIV; - - if ( pSoldier->bTeam == OUR_TEAM ) - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_DISGUISED_AS_CIVILIAN], pSoldier->GetName() ); - } - } - - // reevaluate sight - otherwise we could hide by changing clothes in plain sight! - OtherTeamsLookForMan(pSoldier); - } + // apply covert properties depending on our disguise + pSoldier->ApplyCovert( TRUE ); } return( TRUE ); diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 317f755e..806f12a2 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -10591,7 +10591,8 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) { MercPtrs[ gusSelectedSoldier ]->LooseDisguise(); - MercPtrs[ gusSelectedSoldier ]->Strip(); + if ( gSkillTraitValues.fCOStripIfUncovered ) + MercPtrs[ gusSelectedSoldier ]->Strip(); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_SURRENDER_FAILED] ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVER_SINGLE], MercPtrs[ gusSelectedSoldier ]->GetName() ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index b33956b2..b15d62de 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -15740,6 +15740,16 @@ BOOLEAN SOLDIERTYPE::SeemsLegit( UINT8 ubObserverID ) } } + // uncover if merc is using flashlight and alert is raised + if ( pSoldier->bTeam == ENEMY_TEAM && + pSoldier->aiData.bAlertStatus >= STATUS_RED && + (NightTime( ) || gbWorldSectorZ > 0) && + this->GetBestEquippedFlashLightRange( ) > 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s has flashlight!", this->GetName( ) ); + return FALSE; + } + return TRUE; } @@ -15810,11 +15820,12 @@ BOOLEAN SOLDIERTYPE::RecognizeAsCombatant( UINT8 ubTargetID ) // aha, he/she's a spy! Blow cover if ( pSoldier->usSoldierFlagMask & (SOLDIER_COVERT_CIV | SOLDIER_COVERT_SOLDIER) ) { - pSoldier->LooseDisguise( ); + pSoldier->LooseDisguise(); - pSoldier->Strip( ); + if ( gSkillTraitValues.fCOStripIfUncovered ) + pSoldier->Strip(); - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], this->GetName( ), pSoldier->GetName( ) ); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], this->GetName(), pSoldier->GetName() ); // we have uncovered a spy! Get alerted, if we aren't already if ( this->aiData.bAlertStatus < STATUS_BLACK ) @@ -15853,9 +15864,67 @@ void SOLDIERTYPE::LooseDisguise( void ) } } +void SOLDIERTYPE::Disguise() +{ + // this will only work with the new trait system + if (!gGameOptions.fNewTraitSystem) + return; + + // check if we already disguised + if( this->usSoldierFlagMask & (SOLDIER_COVERT_CIV | SOLDIER_COVERT_SOLDIER | SOLDIER_COVERT_NPC_SPECIAL) ) + return; + + // check that soldier is active and in sector + if ( !this->bActive || !this->bInSector ) + return; + + ApplyCovert( FALSE ); +} + +void SOLDIERTYPE::ApplyCovert( BOOLEAN aWithMessage ) +{ + // check that we have correct clothes + if ( this->usSoldierFlagMask & SOLDIER_NEW_VEST && this->usSoldierFlagMask & SOLDIER_NEW_PANTS ) + { + // first, remove the covert flags, and then reapply the correct ones, in case we switch between civilian and military clothes + this->usSoldierFlagMask &= ~(SOLDIER_COVERT_CIV | SOLDIER_COVERT_SOLDIER); + + // we can only disguise successfully if we are not seen + if ( !EnemySeenSoldierRecently( this ) ) + { + // we now have to determine wether we are currently wearing civilian or military clothes + for ( UINT8 i = UNIFORM_ENEMY_ADMIN; i <= UNIFORM_ENEMY_ELITE; ++i ) + { + // both parts have to fit. We cant mix different uniforms and get soldier disguise + if ( COMPARE_PALETTEREP_ID( this->VestPal, gUniformColors[i].vest ) && COMPARE_PALETTEREP_ID( this->PantsPal, gUniformColors[i].pants ) ) + { + this->usSoldierFlagMask |= SOLDIER_COVERT_SOLDIER; + + if ( aWithMessage && this->bTeam == OUR_TEAM ) + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_DISGUISED_AS_SOLDIER], this->GetName( ) ); + + break; + } + } + + // if not dressed as a soldier, we must be dressed as a civilian + if ( !(this->usSoldierFlagMask & SOLDIER_COVERT_SOLDIER) ) + { + this->usSoldierFlagMask |= SOLDIER_COVERT_CIV; + + if ( aWithMessage && this->bTeam == OUR_TEAM ) + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_DISGUISED_AS_CIVILIAN], this->GetName( ) ); + } + } + + // reevaluate sight - otherwise we could hide by changing clothes in plain sight! + OtherTeamsLookForMan( this ); + } +} + // undisguise or take off any clothes item and switch back to original clothes // no - this function does not do what you think it does. Leave Fox alone, you perv. -void SOLDIERTYPE::Strip( ) +void SOLDIERTYPE::Strip() { // if covert, loose that ability if ( this->usSoldierFlagMask & (SOLDIER_COVERT_CIV | SOLDIER_COVERT_SOLDIER) ) @@ -15863,9 +15932,9 @@ void SOLDIERTYPE::Strip( ) LooseDisguise( ); } // if already not covert, take off clothes - else if ( this->usSoldierFlagMask & (SOLDIER_NEW_VEST | SOLDIER_NEW_PANTS) ) + else if ( this->usSoldierFlagMask & (SOLDIER_NEW_VEST|SOLDIER_NEW_PANTS) ) { - // if we have undamaged clothes, spawn them, the graphci will be removed anyway + // if we have undamaged clothes, spawn them, the graphic will be removed anyway if ( (this->usSoldierFlagMask & SOLDIER_NEW_VEST) && !(this->usSoldierFlagMask & SOLDIER_DAMAGED_VEST) ) { UINT16 vestitem = 0; @@ -17241,6 +17310,10 @@ void SOLDIERTYPE::SoldierPropertyUpkeep( ) { this->usSoldierFlagMask &= ~SOLDIER_BATTLE_PARTICIPATION; } + + // sevenfm: disguise automatically + if ( !gSkillTraitValues.fCOStripIfUncovered ) + this->Disguise(); } // check if Soldier can use the spell skillwise, with fAPCheck = TRUE also check current APs @@ -19974,13 +20047,15 @@ void SOLDIERTYPE::EVENT_SoldierHandcuffPerson( INT32 sGridNo, UINT8 ubDirection this->DoMercBattleSound( BATTLE_SOUND_CURSE1 ); // if we are disguised, there is a chance that he'll uncover us - if ( this->usSoldierFlagMask & (SOLDIER_COVERT_CIV | SOLDIER_COVERT_SOLDIER) ) + if ( this->usSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) { - this->LooseDisguise( ); - this->Strip( ); + this->LooseDisguise(); - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_ACTIVITIES], this->GetName( ) ); - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], pSoldier->GetName( ), this->GetName( ) ); + if ( gSkillTraitValues.fCOStripIfUncovered ) + this->Strip(); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_ACTIVITIES], this->GetName() ); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], pSoldier->GetName(), this->GetName() ); // alert the soldier pSoldier->aiData.bAlertStatus = min( pSoldier->aiData.bAlertStatus, STATUS_RED ); @@ -20033,13 +20108,15 @@ void SOLDIERTYPE::EVENT_SoldierApplyItemToPerson( INT32 sGridNo, UINT8 ubDirecti success = FALSE; // if we are disguised, there is a chance that he'll uncover us - if ( this->usSoldierFlagMask & (SOLDIER_COVERT_CIV | SOLDIER_COVERT_SOLDIER) ) + if ( this->usSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) ) { - this->LooseDisguise( ); - this->Strip( ); - - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_APPLYITEM_STEAL_FAIL], this->GetName( ), pSoldier->GetName( ) ); - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], pSoldier->GetName( ), this->GetName( ) ); + this->LooseDisguise(); + + if ( gSkillTraitValues.fCOStripIfUncovered ) + this->Strip(); + + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_APPLYITEM_STEAL_FAIL], this->GetName(), pSoldier->GetName() ); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], pSoldier->GetName(), this->GetName() ); } // alert the soldier diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 63306e9e..d9d03add 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1788,6 +1788,11 @@ public: // loose covert property void LooseDisguise( void ); + // sevenfm: auto disguise if have correct clothes + void Disguise(); + + void ApplyCovert(BOOLEAN aWithMessage); + // lose disguise or take off any clothes item and switch back to original clothes void Strip(); diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index cca5ce4a..9eb294c0 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -3404,7 +3404,9 @@ BOOLEAN UseHandToHand( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, BOOLEAN fStea if ( fUncovered ) { pSoldier->LooseDisguise(); - pSoldier->Strip(); + + if ( gSkillTraitValues.fCOStripIfUncovered ) + pSoldier->Strip( ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_STEAL_FAIL], pSoldier->GetName() ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szCovertTextStr[STR_COVERT_UNCOVERED], pTargetSoldier->GetName(), pSoldier->GetName() ); diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index c1ab7242..0532efd5 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -1084,7 +1084,7 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK, // sevenfm: only if we don't know enemy location or noise source is close and we have not seen enemy recently if (iPathCost != 0 && !AICheckIsFlanking(pSoldier) && - (TileIsOutOfBounds(sClosestDisturbance) || iPathCost < iShortestPath && !GuySawEnemyThisTurnOrBefore(pSoldier))) + (TileIsOutOfBounds( sClosestDisturbance ) || iPathCost < iShortestPath && !GuySawEnemy( pSoldier )) ) { if (fClimbingNecessary) { @@ -1119,7 +1119,7 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK, // sevenfm: only if we don't know enemy location or noise source is close and we have not seen enemy recently if (iPathCost != 0 && !AICheckIsFlanking(pSoldier) && - (TileIsOutOfBounds(sClosestDisturbance) || iPathCost < iShortestPath && !GuySawEnemyThisTurnOrBefore(pSoldier))) + (TileIsOutOfBounds(sClosestDisturbance) || iPathCost < iShortestPath && !GuySawEnemy(pSoldier))) { if (fClimbingNecessary) { @@ -1767,22 +1767,45 @@ INT16 EstimatePathCostToLocation( SOLDIERTYPE * pSoldier, INT32 sDestGridNo, INT return( sPathCost ); } -BOOLEAN GuySawEnemyThisTurnOrBefore( SOLDIERTYPE * pSoldier ) +BOOLEAN GuySawEnemy( SOLDIERTYPE * pSoldier, UINT8 ubMax ) { UINT8 ubTeamLoop; UINT8 ubIDLoop; + SOLDIERTYPE *pOpponent; - for ( ubTeamLoop = 0; ubTeamLoop < MAXTEAMS; ubTeamLoop++ ) + for ( ubTeamLoop = 0; ubTeamLoop < MAXTEAMS; ++ubTeamLoop ) { if(!gTacticalStatus.Team[ubTeamLoop].bTeamActive)//dnl ch58 070913 skip any inactive teams continue; + if ( gTacticalStatus.Team[ ubTeamLoop ].bSide != pSoldier->bSide ) { // consider guys in this team, which isn't on our side - for ( ubIDLoop = gTacticalStatus.Team[ ubTeamLoop ].bFirstID; ubIDLoop <= gTacticalStatus.Team[ ubTeamLoop ].bLastID; ubIDLoop++ ) + for ( ubIDLoop = gTacticalStatus.Team[ ubTeamLoop ].bFirstID; ubIDLoop <= gTacticalStatus.Team[ ubTeamLoop ].bLastID; ++ubIDLoop ) { + pOpponent = MercPtrs[ubIDLoop]; + + // if this merc is inactive, at base, on assignment, or dead + if (!pOpponent) + { + continue; + } + + // if this merc is neutral/on same side, he's not an opponent + if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide) ) + { + continue; + } + + // sevenfm: ignore empty vehicles + if( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 ) + { + continue; + } + // if this guy SAW an enemy recently... - if ( pSoldier->aiData.bOppList[ ubIDLoop ] >= SEEN_CURRENTLY ) + if( pSoldier->aiData.bOppList[ ubIDLoop ] >= SEEN_CURRENTLY && + pSoldier->aiData.bOppList[ ubIDLoop ] <= ubMax ) { return( TRUE ); } @@ -1832,7 +1855,7 @@ INT32 ClosestReachableFriendInTrouble(SOLDIERTYPE *pSoldier, BOOLEAN * pfClimbin // CJC: restrict "last one to radio" to only if that guy saw us this turn or last turn // if this friend is not under fire, and isn't the last one to radio - if ( ! ( pFriend->aiData.bUnderFire || (pFriend->ubID == gTacticalStatus.Team[pFriend->bTeam].ubLastMercToRadio && GuySawEnemyThisTurnOrBefore( pFriend ) ) ) ) + if ( !(pFriend->aiData.bUnderFire || (pFriend->ubID == gTacticalStatus.Team[pFriend->bTeam].ubLastMercToRadio && GuySawEnemy( pFriend ))) ) { continue; // next merc } @@ -3867,3 +3890,79 @@ BOOLEAN SightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot ) return TRUE; } + +BOOLEAN EnemySeenSoldierRecently( SOLDIERTYPE *pSoldier, UINT8 ubMax ) +{ + UINT32 uiLoop; + SOLDIERTYPE *pOpponent; + + //loop through all the enemies and determine the cover + for ( uiLoop = 0; uiLoopstats.bLife < OKLIFE ) + { + continue; // next merc + } + + // if this man is neutral / on the same side, he's not an opponent + if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide) ) + { + continue; // next merc + } + + // sevenfm: ignore empty vehicles + if ( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 ) + { + continue; + } + + // if opponent is collapsed/breath collapsed + if ( pOpponent->bCollapsed || pOpponent->bBreathCollapsed ) + { + continue; + } + + // check that this opponent sees us + if ( pOpponent->aiData.bOppList[pSoldier->ubID] >= SEEN_CURRENTLY && + pOpponent->aiData.bOppList[pSoldier->ubID] <= ubMax ) + { + return(TRUE); + } + } + + return FALSE; +} + +UINT8 CountTeamSeeSoldier( INT8 bTeam, SOLDIERTYPE *pSoldier ) +{ + SOLDIERTYPE *pFriend; + UINT8 ubFriends = 0; + + CHECKF( pSoldier ); + + if ( bTeam >= MAXTEAMS ) + return 0; + + for ( UINT16 cnt = gTacticalStatus.Team[bTeam].bFirstID; cnt <= gTacticalStatus.Team[bTeam].bLastID; ++cnt ) + { + pFriend = MercPtrs[cnt]; + + if ( pFriend->bActive && + pFriend->bInSector && + pFriend->stats.bLife >= OKLIFE && + !pFriend->bCollapsed && + !pFriend->bBreathCollapsed ) + { + if ( pFriend->aiData.bOppList[pSoldier->ubID] == SEEN_CURRENTLY || + pFriend->aiData.bOppList[pSoldier->ubID] == SEEN_THIS_TURN ) + { + ++ubFriends; + } + } + } + + return ubFriends; +} diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 1843aae8..bfef2a7b 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -3462,7 +3462,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) } if( !TileIsOutOfBounds(sFlankGridNo) && - !GuySawEnemyThisTurnOrBefore(pSoldier) && + !GuySawEnemy(pSoldier) && !pSoldier->aiData.bUnderFire && !Water(pSoldier->sGridNo, pSoldier->pathing.bLevel) && pSoldier->bInitialActionPoints >= APBPConstants[AP_MINIMUM] && @@ -3626,7 +3626,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) } // sevenfm: don't help if seen enemy recently or under fire - if( GuySawEnemyThisTurnOrBefore(pSoldier) || pSoldier->aiData.bUnderFire ) + if( GuySawEnemy(pSoldier) || pSoldier->aiData.bUnderFire ) { bHelpPts -= 10; } @@ -3658,7 +3658,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) // if there is an opponent reachable // sevenfm: allow seeking in prone stance if we haven't seen enemy for several turns if (!TileIsOutOfBounds(sClosestDisturbance) && - ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE || !GuySawEnemyThisTurnOrBefore(pSoldier) ) ) + (gAnimControl[pSoldier->usAnimState].ubHeight != ANIM_PRONE || !GuySawEnemy( pSoldier )) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: seek opponent"); ////////////////////////////////////////////////////////////////////// @@ -3750,7 +3750,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) ( pSoldier->aiData.bOrders == SEEKENEMY || pSoldier->aiData.bOrders == FARPATROL || pSoldier->aiData.bOrders == CLOSEPATROL && NightTime() ) && - (!GuySawEnemyThisTurnOrBefore( pSoldier ) || fOvercrowded ) && + (!GuySawEnemy( pSoldier ) || fOvercrowded ) && !Water(pSoldier->sGridNo, pSoldier->pathing.bLevel) && pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] && ( CountFriendsInDirection( pSoldier, sClosestDisturbance ) > 1 || NightTime() || fOvercrowded) ) @@ -10261,7 +10261,7 @@ INT8 ArmedVehicleDecideActionRed( SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK ) } if ( !TileIsOutOfBounds( sFlankGridNo ) && - !GuySawEnemyThisTurnOrBefore( pSoldier ) && + !GuySawEnemy( pSoldier ) && !pSoldier->aiData.bUnderFire && !Water( pSoldier->sGridNo, pSoldier->pathing.bLevel ) && pSoldier->bInitialActionPoints >= APBPConstants[AP_MINIMUM] && @@ -10379,7 +10379,7 @@ INT8 ArmedVehicleDecideActionRed( SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK ) } // sevenfm: don't help if seen enemy recently or under fire - if ( GuySawEnemyThisTurnOrBefore( pSoldier ) || pSoldier->aiData.bUnderFire ) + if ( GuySawEnemy( pSoldier ) || pSoldier->aiData.bUnderFire ) { bHelpPts -= 10; } @@ -10411,7 +10411,7 @@ INT8 ArmedVehicleDecideActionRed( SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK ) // if there is an opponent reachable // sevenfm: allow seeking in prone stance if we haven't seen enemy for several turns if ( !TileIsOutOfBounds( sClosestDisturbance ) && - (gAnimControl[pSoldier->usAnimState].ubHeight != ANIM_PRONE || !GuySawEnemyThisTurnOrBefore( pSoldier )) ) + (gAnimControl[pSoldier->usAnimState].ubHeight != ANIM_PRONE || !GuySawEnemy( pSoldier )) ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "ArmedVehicleDecideActionRed: seek opponent" ); ////////////////////////////////////////////////////////////////////// @@ -10460,7 +10460,7 @@ INT8 ArmedVehicleDecideActionRed( SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK ) (pSoldier->aiData.bOrders == SEEKENEMY || pSoldier->aiData.bOrders == FARPATROL || pSoldier->aiData.bOrders == CLOSEPATROL && NightTime( )) && - (!GuySawEnemyThisTurnOrBefore( pSoldier ) || fOvercrowded) && + (!GuySawEnemy( pSoldier ) || fOvercrowded) && !Water( pSoldier->sGridNo, pSoldier->pathing.bLevel ) && pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] && (CountFriendsInDirection( pSoldier, sClosestDisturbance ) > 1 || NightTime( ) || fOvercrowded) ) diff --git a/TacticalAI/Knowledge.cpp b/TacticalAI/Knowledge.cpp index 38965e80..608d9dd6 100644 --- a/TacticalAI/Knowledge.cpp +++ b/TacticalAI/Knowledge.cpp @@ -12,6 +12,8 @@ #include "Soldier macros.h" #endif +extern SECTOR_EXT_DATA SectorExternalData[256][4]; + void CallAvailableEnemiesTo( INT32 sGridNo ) { INT32 iLoop; @@ -176,8 +178,15 @@ INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN pbPersOL = pSoldier->aiData.bOppList; pbPublOL = gbPublicOpplist[pSoldier->bTeam]; + // sevenfm: sector information + UINT8 sectordata = 0; + if ( gbWorldSectorZ > 0 ) // underground we are always suspicious + sectordata = 2; + else + sectordata = SectorExternalData[SECTOR( gWorldSectorX, gWorldSectorY )][gbWorldSectorZ].usCurfewValue; + // look through this man's personal & public opplists for opponents heard - for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++) + for (uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop) { pTemp = MercSlots[ uiLoop ]; @@ -195,7 +204,11 @@ INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN // green AI state: always ignore // yellow AI state: 50% chance to ignore if ( pSoldier->aiData.bAlertStatus == STATUS_GREEN || (pSoldier->aiData.bAlertStatus == STATUS_YELLOW && Random(2) ) ) - continue; // next merc + continue; + + // sevenfm: ignore noise if some friends already see opponent + if ( CountTeamSeeSoldier( pSoldier->bTeam, pTemp ) > sectordata ) + continue; } pbPersOL = pSoldier->aiData.bOppList + pTemp->ubID; @@ -232,7 +245,6 @@ INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN bBestLevel = gbPublicLastKnownOppLevel[pSoldier->bTeam][pTemp->ubID]; } } - } // if any "misc. noise" was also heard recently @@ -258,8 +270,7 @@ INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN pSoldier->aiData.ubNoiseVolume = 0; } } - - + // if any recent PUBLIC "misc. noise" is also known if ( (pSoldier->bTeam != CIV_TEAM) || ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) ) { @@ -280,7 +291,6 @@ INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN } } } - } if (!TileIsOutOfBounds(sBestGridNo) && pfReachable ) diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index 217cf079..36d3c72b 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -278,12 +278,15 @@ UINT8 GetClosestMedicSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTe INT16 MaxNormalVisionDistance( void ); UINT8 MinFlankDirections( SOLDIERTYPE *pSoldier ); UINT8 CountFriendsInDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo ); -BOOLEAN GuySawEnemyThisTurnOrBefore( SOLDIERTYPE * pSoldier ); +BOOLEAN GuySawEnemy( SOLDIERTYPE * pSoldier, UINT8 ubMax = SEEN_3_TURNS_AGO ); UINT8 CountNearbyFriends( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance ); UINT8 CountNearbyFriendsLastAttackHit( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance ); UINT8 CountFriendsFlankSameSpot( SOLDIERTYPE *pSoldier ); UINT8 CountFriendsBlack( SOLDIERTYPE *pSoldier, INT32 sClosestOpponent = NOWHERE ); +BOOLEAN EnemySeenSoldierRecently( SOLDIERTYPE *pSoldier, UINT8 ubMax = SEEN_3_TURNS_AGO ); +UINT8 CountTeamSeeSoldier( INT8 bTeam, SOLDIERTYPE *pSoldier ); + BOOLEAN AICheckFriendsNoContact( SOLDIERTYPE *pSoldier ); BOOLEAN AICheckIsFlanking( SOLDIERTYPE *pSoldier );