mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
- New Feature: enemies roles allows the AI to use medics and officers, and the player can now identify high-value targets among the enemy.
Requires GameDir >= r1987. - If an enemy is spawned with a radio set, he is more likely to become a radio operator. - It is now possible to mount a gun on prone allies if you are facing them on the right angle. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7072 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
+14
-1
@@ -1472,6 +1472,19 @@ void LoadGameExternalOptions()
|
||||
gGameExternalOptions.fAccessOtherMercInventories = iniReader.ReadBoolean("Tactical Gameplay Settings","ACCESS_OTHER_MERC_INVENTORIES", TRUE);
|
||||
gGameExternalOptions.fBackPackWeightLowersAP = iniReader.ReadBoolean("Tactical Gameplay Settings","BACKPACKWEIGHT_LOWERS_AP", TRUE);
|
||||
|
||||
//################# Tactical Enemy Role Settings ##################
|
||||
// Flugente: enemy roles
|
||||
gGameExternalOptions.fEnemyRoles = iniReader.ReadBoolean("Tactical Enemy Role Settings", "ENEMYROLES", TRUE);
|
||||
gGameExternalOptions.usTurnsToUncover = iniReader.ReadInteger("Tactical Enemy Role Settings", "ENEMYROLES_TURNSTOUNCOVER", 6, 0, 20);
|
||||
gGameExternalOptions.fEnemyMedics = iniReader.ReadBoolean("Tactical Enemy Role Settings", "ENEMY_MEDICS", TRUE);
|
||||
gGameExternalOptions.dEnemyMedicMedKitDrainFactor = iniReader.ReadFloat ("Tactical Enemy Role Settings", "ENEMY_MEDICS_MEDKITDRAINFACTOR", 0.1f, 0.01f, 1.0f);
|
||||
gGameExternalOptions.sEnemyMedicsSearchRadius = iniReader.ReadInteger("Tactical Enemy Role Settings", "ENEMY_MEDICS_SEARCHRAIUS", 20, 5, 60);
|
||||
gGameExternalOptions.fEnemyOfficers = iniReader.ReadBoolean("Tactical Enemy Role Settings", "ENEMY_OFFICERS", TRUE);
|
||||
gGameExternalOptions.usEnemyOfficersPerTeamSize = iniReader.ReadInteger("Tactical Enemy Role Settings", "ENEMY_OFFICERS_REQUIREDTEAMSIZE", 10, 1, 64);
|
||||
gGameExternalOptions.usEnemyOfficersMax = iniReader.ReadInteger("Tactical Enemy Role Settings", "ENEMY_OFFICERS_MAX", 4, 1, 10);
|
||||
gGameExternalOptions.sEnemyOfficerSuppressionResistanceBonus = iniReader.ReadInteger("Tactical Enemy Role Settings", "ENEMY_OFFICERS_SUPPRESSION_RESISTANCE_BONUS", 10, 0, 50);
|
||||
gGameExternalOptions.dEnemyOfficerMoraleModifier = iniReader.ReadFloat ("Tactical Enemy Role Settings", "ENEMY_OFFICERS_MORALE_MODIFIER", 0.1f, 0.00f, 1.0f);
|
||||
|
||||
//################# Tactical Cover System Settings ##################
|
||||
|
||||
// CPT: Cover System Settings
|
||||
@@ -1768,7 +1781,7 @@ void LoadGameExternalOptions()
|
||||
gGameExternalOptions.ubPrisonerInterrogationPoints[PRISONER_REGULAR] = iniReader.ReadInteger("Strategic Gameplay Settings","PRISONER_INTERROGATION_POINTS_REGULAR", 100, gGameExternalOptions.ubPrisonerInterrogationPoints[PRISONER_ADMIN], 1000);
|
||||
gGameExternalOptions.ubPrisonerInterrogationPoints[PRISONER_ELITE] = iniReader.ReadInteger("Strategic Gameplay Settings","PRISONER_INTERROGATION_POINTS_ELITE", 100, gGameExternalOptions.ubPrisonerInterrogationPoints[PRISONER_REGULAR], 1000);
|
||||
gGameExternalOptions.ubPrisonerInterrogationPoints[PRISONER_SPECIAL] = iniReader.ReadInteger("Strategic Gameplay Settings","PRISONER_INTERROGATION_POINTS_SPECIAL", 100, gGameExternalOptions.ubPrisonerInterrogationPoints[PRISONER_ELITE], 1000);
|
||||
|
||||
|
||||
// CHRISL: Determine how Skyrider should handle landing in enemy occupied sectors
|
||||
gGameExternalOptions.ubSkyriderHotLZ = iniReader.ReadInteger("Strategic Gameplay Settings", "ALLOW_SKYRIDER_HOT_LZ", 0, 0, 3);
|
||||
|
||||
|
||||
@@ -1337,6 +1337,19 @@ typedef struct
|
||||
INT8 sMoraleModRacism;
|
||||
INT8 sMoraleModSexism;
|
||||
INT8 sMoraleModXenophobicBackGround;
|
||||
|
||||
// Flugente: enemy roles
|
||||
BOOLEAN fEnemyRoles;
|
||||
UINT8 usTurnsToUncover;
|
||||
BOOLEAN fEnemyMedics;
|
||||
FLOAT dEnemyMedicMedKitDrainFactor;
|
||||
INT16 sEnemyMedicsSearchRadius;
|
||||
BOOLEAN fEnemyOfficers;
|
||||
UINT16 usEnemyOfficersPerTeamSize;
|
||||
UINT16 usEnemyOfficersMax;
|
||||
INT8 sEnemyOfficerSuppressionResistanceBonus;
|
||||
FLOAT dEnemyOfficerMoraleModifier;
|
||||
|
||||
|
||||
// Sandro: Alternative weapon holding (rifles fired from hip / pistols fired one-handed)
|
||||
UINT8 ubAllowAlternativeWeaponHolding;
|
||||
|
||||
@@ -540,7 +540,6 @@ void CheckForAndHandleHospitalPatients( void );
|
||||
void HealHospitalPatient( SOLDIERTYPE *pPatient, UINT16 usHealingPtsLeft );
|
||||
|
||||
BOOLEAN MakeSureToolKitIsInHand( SOLDIERTYPE *pSoldier );
|
||||
BOOLEAN MakeSureMedKitIsInHand( SOLDIERTYPE *pSoldier );
|
||||
|
||||
void RepositionMouseRegions( void );
|
||||
void CheckAndUpdateTacticalAssignmentPopUpPositions( void );
|
||||
@@ -8634,7 +8633,6 @@ BOOLEAN MakeSureMedKitIsInHand( SOLDIERTYPE *pSoldier )
|
||||
{
|
||||
INT8 bPocket = 0;
|
||||
|
||||
|
||||
fTeamPanelDirty = TRUE;
|
||||
|
||||
// if there is a MEDICAL BAG in his hand, we're set
|
||||
|
||||
@@ -454,6 +454,8 @@ void HandleGatheringInformationBySoldier( SOLDIERTYPE* pSoldier );
|
||||
|
||||
BOOLEAN MercStaffsMilitaryHQ();
|
||||
|
||||
BOOLEAN MakeSureMedKitIsInHand( SOLDIERTYPE *pSoldier );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
+119
-2
@@ -253,6 +253,7 @@ UINT32 guiBURSTACCUM;
|
||||
UINT32 guiITEMPOINTERHATCHES;
|
||||
|
||||
UINT32 guiUNDERWATER; // added by Flugente
|
||||
UINT32 guiENEMYROLES; // added by Flugente
|
||||
|
||||
// UI Globals
|
||||
MOUSE_REGION gViewportRegion;
|
||||
@@ -299,6 +300,9 @@ extern void CalculateCoverForSoldier( SOLDIERTYPE* pForSoldier, const INT32& sTa
|
||||
|
||||
extern FLOAT Distance2D( FLOAT dDeltaX, FLOAT dDeltaY );
|
||||
|
||||
// Flugente: toggle display of enemy role indicators
|
||||
BOOLEAN gDisplayEnemyRoles = TRUE;
|
||||
|
||||
BOOLEAN InitializeFaceGearGraphics()
|
||||
{
|
||||
VOBJECT_DESC VObjectDesc;
|
||||
@@ -545,6 +549,12 @@ BOOLEAN InitializeTacticalInterface( )
|
||||
if( !AddVideoObject( &VObjectDesc, &guiUNDERWATER ) )
|
||||
AssertMsg(0, "Missing INTERFACE\\fish.sti" );
|
||||
|
||||
// Flugente: enemy role symbols
|
||||
VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE;
|
||||
FilenameForBPP("INTERFACE\\RoleIcons.sti", VObjectDesc.ImageFile);
|
||||
if( !AddVideoObject( &VObjectDesc, &guiENEMYROLES ) )
|
||||
AssertMsg(0, "Missing INTERFACE\\RoleIcons.sti" );
|
||||
|
||||
//CHRISL: Moved to seperate function so we can call seperately
|
||||
InitializeTacticalPortraits();
|
||||
|
||||
@@ -1822,6 +1832,11 @@ void DrawSelectedUIAboveGuy( UINT16 usSoldierID )
|
||||
}
|
||||
}
|
||||
|
||||
if ( gDisplayEnemyRoles && pSoldier->bTeam == ENEMY_TEAM )
|
||||
{
|
||||
ShowSoldierRoleSymbol(pSoldier);
|
||||
}
|
||||
|
||||
if ( !pSoldier->flags.fShowLocator )
|
||||
{
|
||||
// RETURN IF MERC IS NOT SELECTED
|
||||
@@ -2618,10 +2633,10 @@ void DrawSelectedUIAboveGuy( UINT16 usSoldierID )
|
||||
if(gGameExternalOptions.fEnemyRank)
|
||||
sY+=10;
|
||||
|
||||
iBack = RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sX, sY, (INT16)(sX + 11 ), (INT16)(sY + 14 ) );
|
||||
iBack = RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sX, sY, (INT16)(sX + 22 ), (INT16)(sY + 14 ) );
|
||||
if ( iBack != -1 )
|
||||
SetBackgroundRectFilled( iBack );
|
||||
|
||||
|
||||
DrawRankIcon( pSoldier->stats.bExpLevel, sX, sY );
|
||||
}
|
||||
}
|
||||
@@ -6279,3 +6294,105 @@ void DrawEnemyHealthBar( SOLDIERTYPE* pSoldier, INT32 sX, INT32 sY, UINT8 ubLine
|
||||
UnLockVideoSurface( FRAME_BUFFER );
|
||||
}
|
||||
|
||||
// Flugente: show enemy role
|
||||
BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier)
|
||||
{
|
||||
// this only works on enemy soldiers
|
||||
if ( pSoldier->bTeam != ENEMY_TEAM )
|
||||
return false;
|
||||
|
||||
if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] < gGameExternalOptions.usTurnsToUncover )
|
||||
return false;
|
||||
|
||||
INT16 sXPos = 0;
|
||||
INT16 sYPos = 0;
|
||||
INT32 iBack = 0;
|
||||
|
||||
GetSoldierAboveGuyPositions( pSoldier, &sXPos, &sYPos, TRUE );
|
||||
|
||||
// Adjust for bars!
|
||||
sXPos += 50;
|
||||
sYPos += 25;
|
||||
|
||||
// is this guy an officer?
|
||||
if ( pSoldier->bSoldierFlagMask & SOLDIER_ENEMY_OFFICER )
|
||||
{
|
||||
// Add bars
|
||||
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20 ), (INT16)(sYPos + 20 ) );
|
||||
|
||||
if ( iBack != -1 )
|
||||
{
|
||||
SetBackgroundRectFilled( iBack );
|
||||
}
|
||||
|
||||
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 0, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
|
||||
|
||||
sYPos += 20;
|
||||
}
|
||||
|
||||
// is this guy radio operator with a radio?
|
||||
if ( pSoldier->CanUseRadio(false) )
|
||||
{
|
||||
// Add bars
|
||||
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20 ), (INT16)(sYPos + 20 ) );
|
||||
|
||||
if ( iBack != -1 )
|
||||
{
|
||||
SetBackgroundRectFilled( iBack );
|
||||
}
|
||||
|
||||
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 3, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
|
||||
|
||||
sYPos += 20;
|
||||
}
|
||||
|
||||
// is this guy a doctor? we do not check for medical gear, as we would not have that information from a glance
|
||||
if ( HAS_SKILL_TRAIT( pSoldier, DOCTOR_NT) )
|
||||
{
|
||||
// Add bars
|
||||
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20 ), (INT16)(sYPos + 20 ) );
|
||||
|
||||
if ( iBack != -1 )
|
||||
{
|
||||
SetBackgroundRectFilled( iBack );
|
||||
}
|
||||
|
||||
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 1, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
|
||||
|
||||
sYPos += 20;
|
||||
}
|
||||
|
||||
// is this guy a sniper? Just check for the gun, trait is not necessary
|
||||
if ( pSoldier->HasSniper() )
|
||||
{
|
||||
// Add bars
|
||||
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20 ), (INT16)(sYPos + 20 ) );
|
||||
|
||||
if ( iBack != -1 )
|
||||
{
|
||||
SetBackgroundRectFilled( iBack );
|
||||
}
|
||||
|
||||
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 2, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
|
||||
|
||||
sYPos += 20;
|
||||
}
|
||||
|
||||
// is this guy a mortar guy? Just check for the gun, trait is not necessary
|
||||
if ( pSoldier->HasMortar() )
|
||||
{
|
||||
// Add bars
|
||||
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20 ), (INT16)(sYPos + 20 ) );
|
||||
|
||||
if ( iBack != -1 )
|
||||
{
|
||||
SetBackgroundRectFilled( iBack );
|
||||
}
|
||||
|
||||
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 4, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
|
||||
|
||||
sYPos += 20;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -533,4 +533,7 @@ BOOLEAN CoverColorCode( INT8 cover, INT16 &color8, INT16 &color16 );
|
||||
void DrawRankIcon( INT8 rank, INT32 baseX, INT32 baseY );
|
||||
void DrawLine( INT32 x1, INT32 y1, INT32 x2, INT32 y2, UINT16 color8, UINT16 color16, UINT8 *pDestBuf );
|
||||
|
||||
// Flugente: show enemy role
|
||||
BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier);
|
||||
|
||||
#endif
|
||||
+14
-5
@@ -129,21 +129,27 @@ INT8 GetMoraleModifier( SOLDIERTYPE * pSoldier )
|
||||
}
|
||||
else
|
||||
{
|
||||
INT8 morale = 0;
|
||||
|
||||
// use AI morale
|
||||
switch( pSoldier->aiData.bAIMorale )
|
||||
{
|
||||
case MORALE_HOPELESS:
|
||||
return( -15 );
|
||||
morale = -15;
|
||||
case MORALE_WORRIED:
|
||||
return( -7 );
|
||||
morale = -7;
|
||||
case MORALE_CONFIDENT:
|
||||
return( 2 );
|
||||
morale = -2;
|
||||
case MORALE_FEARLESS:
|
||||
return( 5 );
|
||||
morale = -5;
|
||||
default:
|
||||
return( 0 );
|
||||
morale = 0;
|
||||
}
|
||||
|
||||
// Flugente: morale modifiers
|
||||
morale = max(morale, morale * pSoldier->GetMoraleModifier());
|
||||
|
||||
return morale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +372,9 @@ void RefreshSoldierMorale( SOLDIERTYPE * pSoldier )
|
||||
}
|
||||
}
|
||||
|
||||
// Flugente: morale modifiers
|
||||
iActualMorale = iActualMorale * pSoldier->GetMoraleModifier();
|
||||
|
||||
// Flugente: ubMaxMorale can now be influenced by our food situation
|
||||
if ( gGameOptions.fFoodSystem )
|
||||
FoodMaxMoraleModifiy(pSoldier, &ubMaxMorale);
|
||||
|
||||
@@ -10619,3 +10619,28 @@ INT8 CalcEffectiveShockLevel( SOLDIERTYPE * pSoldier )
|
||||
}
|
||||
return bShockForCower;
|
||||
}
|
||||
|
||||
// Flugente: count number of enemy officers
|
||||
UINT8 HighestEnemyOfficersInSector( BOOL& aType )
|
||||
{
|
||||
SOLDIERTYPE* pSoldier;
|
||||
INT32 cnt = 0;
|
||||
UINT8 num = 0;
|
||||
UINT8 type = OFFICER_NONE;
|
||||
|
||||
for ( cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID, pSoldier = MercPtrs[cnt]; cnt < gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; pSoldier++, ++cnt )
|
||||
{
|
||||
if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 )
|
||||
{
|
||||
if ( pSoldier->bSoldierFlagMask & SOLDIER_ENEMY_OFFICER )
|
||||
{
|
||||
// officers with double squadleader trait are captains, otherwise lieutnant
|
||||
aType = max( aType, NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT) );
|
||||
|
||||
++num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
@@ -388,5 +388,17 @@ void KillOnePrisoner( SECTORINFO *pSectorInfo );
|
||||
// sevenfm: calc shock level with modifiers (traits, backgrounds, ...)
|
||||
INT8 CalcEffectiveShockLevel( SOLDIERTYPE * pSoldier );
|
||||
|
||||
// Flugente: enemy officers
|
||||
enum {
|
||||
OFFICER_NONE = 0,
|
||||
OFFICER_LIEUTNANT,
|
||||
OFFICER_CAPTAIN,
|
||||
|
||||
OFFICER_MAX,
|
||||
};
|
||||
|
||||
// Flugente: return number of enemy officers and highest rank found
|
||||
UINT8 HighestEnemyOfficersInSector(BOOL& aType);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -12636,8 +12636,10 @@ UINT32 SOLDIERTYPE::SoldierDressWound( SOLDIERTYPE *pVictim, INT16 sKitPts, INT1
|
||||
}
|
||||
|
||||
// if we are going to do the surgery
|
||||
// Flugente: AI medics are allowed to perform surgery without first aid kits
|
||||
if (pVictim->iHealableInjury > 0 && this->fDoingSurgery && this->ubID != pVictim->ubID && gGameOptions.fNewTraitSystem
|
||||
&& (NUM_SKILL_TRAITS( this, DOCTOR_NT ) >= gSkillTraitValues.ubDONumberTraitsNeededForSurgery) && Item[this->inv[ HANDPOS ].usItem].medicalkit)
|
||||
&& (NUM_SKILL_TRAITS( this, DOCTOR_NT ) >= gSkillTraitValues.ubDONumberTraitsNeededForSurgery)
|
||||
&& (Item[this->inv[ HANDPOS ].usItem].medicalkit || this->bTeam == ENEMY_TEAM) )
|
||||
{
|
||||
fOnSurgery = TRUE;
|
||||
}
|
||||
@@ -12877,9 +12879,12 @@ UINT32 SOLDIERTYPE::SoldierDressWound( SOLDIERTYPE *pVictim, INT16 sKitPts, INT1
|
||||
// We are finished !!!
|
||||
this->fDoingSurgery = FALSE;
|
||||
gTacticalStatus.ubLastRequesterSurgeryTargetID = NOBODY; // reset last target
|
||||
|
||||
// throw message
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[ MSG113_SURGERY_FINISHED ], pVictim->GetName() );
|
||||
|
||||
if ( this->bTeam != ENEMY_TEAM )
|
||||
{
|
||||
// throw message
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[ MSG113_SURGERY_FINISHED ], pVictim->GetName() );
|
||||
}
|
||||
|
||||
// add to record - another surgery undergoed
|
||||
if ( pVictim->ubProfile != NO_PROFILE && usLifeReturned >= 100 )
|
||||
@@ -14164,9 +14169,25 @@ BOOLEAN SOLDIERTYPE::IsWeaponMounted( void )
|
||||
{
|
||||
// for some reason I find EXTREMELY FRUSTRATING, we might get a heigth of 2 on a totally empty tile... so we check if we could occupy the tile
|
||||
if ( !IsLocationSittable( nextGridNoinSight, 0 ) )
|
||||
{
|
||||
// resting our gun on people would be rude - only allow if nobody is there
|
||||
if( WhoIsThere2( nextGridNoinSight, 0 ) == NOBODY )
|
||||
applybipod = TRUE;
|
||||
UINT usPersonID = WhoIsThere2( nextGridNoinSight, this->pathing.bLevel );
|
||||
if( usPersonID == NOBODY )
|
||||
applybipod = TRUE;
|
||||
else
|
||||
{
|
||||
SOLDIERTYPE* pSoldier = MercPtrs[ usPersonID ];
|
||||
|
||||
// if the other person is an ally an prone
|
||||
if ( this->bSide == pSoldier->bSide && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE )
|
||||
{
|
||||
// if we are facing the other guy in a 90 degree angle, we can mount our gun on his back
|
||||
// Once merc's relationship allows angering mercs through actions of others, add a penalty here
|
||||
if ( this->ubDirection == gTwoCCDirection[ pSoldier->ubDirection ] || this->ubDirection == gTwoCDirection[ pSoldier->ubDirection ] )
|
||||
applybipod = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( adjacenttileheight == 4 && (gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_CROUCH || (gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_STAND && (gAnimControl[ this->usAnimState ].uiFlags &(ANIM_ALT_WEAPON_HOLDING)))))
|
||||
@@ -16954,6 +16975,26 @@ INT8 SOLDIERTYPE::GetSuppressionResistanceBonus()
|
||||
|
||||
bonus += this->GetBackgroundValue(BG_RESI_SUPPRESSION);
|
||||
|
||||
// Flugente: enemy roles
|
||||
if ( this->bTeam == ENEMY_TEAM )
|
||||
{
|
||||
// bonus if we have an officer around
|
||||
BOOL officertype = OFFICER_NONE;
|
||||
if ( HighestEnemyOfficersInSector( officertype ) )
|
||||
{
|
||||
switch ( officertype )
|
||||
{
|
||||
case OFFICER_LIEUTNANT:
|
||||
bonus += gGameExternalOptions.sEnemyOfficerSuppressionResistanceBonus;
|
||||
break;
|
||||
|
||||
case OFFICER_CAPTAIN:
|
||||
bonus += gGameExternalOptions.sEnemyOfficerSuppressionResistanceBonus * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min( 100, max( -100, bonus) );
|
||||
}
|
||||
|
||||
@@ -17039,12 +17080,33 @@ UINT8 SOLDIERTYPE::GetMoraleThreshold()
|
||||
{
|
||||
UINT8 threshold = 100;
|
||||
UINT8 moraledamage = 0;
|
||||
|
||||
|
||||
moraledamage = (moraledamage * (100 - GetFearResistanceBonus())) / 100;
|
||||
|
||||
return min(threshold, max(0, threshold - moraledamage));
|
||||
}
|
||||
|
||||
FLOAT SOLDIERTYPE::GetMoraleModifier()
|
||||
{
|
||||
FLOAT mod = 1.0f;
|
||||
|
||||
BOOL officertype = OFFICER_NONE;
|
||||
if ( HighestEnemyOfficersInSector( officertype ) )
|
||||
{
|
||||
switch ( officertype )
|
||||
{
|
||||
case OFFICER_LIEUTNANT:
|
||||
mod += gGameExternalOptions.dEnemyOfficerMoraleModifier;
|
||||
break;
|
||||
|
||||
case OFFICER_CAPTAIN:
|
||||
mod += gGameExternalOptions.dEnemyOfficerMoraleModifier * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
INT16 SOLDIERTYPE::GetInterruptModifier( UINT8 usDistance )
|
||||
{
|
||||
INT16 bonus = 0;
|
||||
@@ -17086,12 +17148,22 @@ void SOLDIERTYPE::SoldierPropertyUpkeep()
|
||||
// effects eventually run out
|
||||
for (UINT8 counter = 0; counter < SOLDIER_COUNTER_MAX; ++counter)
|
||||
{
|
||||
if ( counter == SOLDIER_COUNTER_SPOTTER && usSkillCounter[counter] > 0 )
|
||||
if ( counter == SOLDIER_COUNTER_ROLE_OBSERVED )
|
||||
continue;
|
||||
else if ( counter == SOLDIER_COUNTER_SPOTTER && usSkillCounter[counter] > 0 )
|
||||
usSkillCounter[counter] = min(255, usSkillCounter[counter] + 1 );
|
||||
else
|
||||
usSkillCounter[counter] = max(0, usSkillCounter[counter] - 1 );
|
||||
}
|
||||
|
||||
// if soldier was seen this turn, increase his observed counter
|
||||
if ( this->bSoldierFlagMask & SOLDIER_ENEMY_OBSERVEDTHISTURN )
|
||||
{
|
||||
this->bSoldierFlagMask &= ~SOLDIER_ENEMY_OBSERVEDTHISTURN;
|
||||
|
||||
++usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED];
|
||||
}
|
||||
|
||||
// if there is a combat going and we are in sector, note that in the battle report
|
||||
if ( this->bInSector && (gTacticalStatus.uiFlags & INCOMBAT || gTacticalStatus.fEnemyInSector) )
|
||||
{
|
||||
@@ -18199,6 +18271,94 @@ BOOLEAN SOLDIERTYPE::BecomeSpotter( INT32 sTargetGridNo )
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Flugente: enemy roles
|
||||
BOOLEAN SOLDIERTYPE::HasSniper()
|
||||
{
|
||||
INT8 invsize = (INT8)inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop)
|
||||
{
|
||||
if (inv[bLoop].exists() && Item[inv[ bLoop ].usItem].usItemClass == IC_GUN && Weapon[Item[inv[bLoop].usItem].ubClassIndex].ubWeaponType == GUN_SN_RIFLE )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// AI-only: can we heal a wounded ally? Do NOT, repeat, NOT use this with mercs!
|
||||
BOOLEAN SOLDIERTYPE::CanMedicAI()
|
||||
{
|
||||
if ( !gGameExternalOptions.fEnemyRoles || !gGameExternalOptions.fEnemyMedics || this->bTeam != ENEMY_TEAM )
|
||||
return FALSE;
|
||||
|
||||
if ( HAS_SKILL_TRAIT( this, DOCTOR_NT) )
|
||||
{
|
||||
if ( FindFirstAidKit(this) != NO_SLOT || FindMedKit(this) != NO_SLOT )
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// AI-only: heal a nearby friend. Do NOT, repeat, NOT use this with mercs!
|
||||
BOOLEAN SOLDIERTYPE::AIDoctorFriend()
|
||||
{
|
||||
if ( this->bTeam != ENEMY_TEAM )
|
||||
return FALSE;
|
||||
|
||||
// we can only free people we are facing
|
||||
INT32 nextGridNoinSight = NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) );
|
||||
|
||||
UINT8 target = WhoIsThere2( nextGridNoinSight, this->pathing.bLevel );
|
||||
|
||||
// is there somebody?
|
||||
if ( target != NOBODY )
|
||||
{
|
||||
SOLDIERTYPE* pSoldier = MercPtrs[target];
|
||||
|
||||
if ( pSoldier->bTeam != ENEMY_TEAM )
|
||||
return FALSE;
|
||||
|
||||
// if this guy is wounded should always be the case, otherwise this function was calleed needlessly
|
||||
if ( pSoldier->stats.bLife < pSoldier->stats.bLifeMax )
|
||||
{
|
||||
// move medkit into hand - if we don't have a medkit in our hands, abort
|
||||
if ( !MakeSureMedKitIsInHand( this ) )
|
||||
return FALSE;
|
||||
|
||||
if( gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_CROUCH )
|
||||
{
|
||||
this->EVENT_InitNewSoldierAnim( START_AID, 0 , FALSE );
|
||||
}
|
||||
|
||||
// AI medics always perform surgery
|
||||
this->fDoingSurgery = TRUE;
|
||||
|
||||
UINT16 usKitPts = TotalPoints( &(this->inv[ HANDPOS ] ) );
|
||||
|
||||
// note the current hp
|
||||
INT8 oldlife = pSoldier->stats.bLife;
|
||||
|
||||
UINT16 uiPointsUsed = this->SoldierDressWound( pSoldier, usKitPts, usKitPts );
|
||||
|
||||
UseKitPoints( &(this->inv[ HANDPOS ] ), (UINT16)(uiPointsUsed * gGameExternalOptions.dEnemyMedicMedKitDrainFactor), this );
|
||||
|
||||
// healing done will be displayed the next time the player sees this soldier
|
||||
pSoldier->flags.fDisplayDamage = TRUE;
|
||||
pSoldier->sDamage -= pSoldier->stats.bLife - oldlife;
|
||||
|
||||
// alert both soldiers
|
||||
this->aiData.bAlertStatus = min(this->aiData.bAlertStatus, STATUS_RED);
|
||||
pSoldier->aiData.bAlertStatus = min(pSoldier->aiData.bAlertStatus, STATUS_RED);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
|
||||
{
|
||||
|
||||
@@ -384,10 +384,10 @@ enum
|
||||
|
||||
#define SOLDIER_BATTLE_PARTICIPATION 0x01000000 //16777216 // campaign stats: soldier took part in this battle
|
||||
#define SOLDIER_RAISED_REDALERT 0x02000000 //33554432 // this (AI) soldier has raised red alert. Don't allow him to do so again this turn - either it already worked, or the signal is blocked
|
||||
/*#define PLAYER_NET_3_LVL_3 0x04000000 //67108864
|
||||
#define PLAYER_NET_4_LVL_3 0x08000000 //134217728
|
||||
#define SOLDIER_ENEMY_OFFICER 0x04000000 //67108864 // soldier is an enemy officer
|
||||
#define SOLDIER_ENEMY_OBSERVEDTHISTURN 0x08000000 //134217728 // enemy soldier was seen by the player this turn
|
||||
|
||||
#define PLAYER_NET_1_LVL_4 0x10000000 //268435456
|
||||
/*#define PLAYER_NET_1_LVL_4 0x10000000 //268435456
|
||||
#define PLAYER_NET_2_LVL_4 0x20000000 //536870912
|
||||
#define WH40K_SOLDIER_ILLUSION 0x40000000 //1073741824 // Soldier is an Illusion
|
||||
#define WH40K_SOLDIER_KILLTHISTURN 0x80000000 //2147483648 // Soldier is on a kill streak*/
|
||||
@@ -565,6 +565,7 @@ enum{
|
||||
enum {
|
||||
SOLDIER_COUNTER_RADIO_ARTILLERY, // there is actually no need for an artillery timer, but we use one to forbid the same AI guy ordering multiple strikes at once
|
||||
SOLDIER_COUNTER_SPOTTER, // used to determine wether we are a spotter
|
||||
SOLDIER_COUNTER_ROLE_OBSERVED, // every turn that the player observes an enemy, the enemies counter is increased. If it is high enough, we know his role
|
||||
|
||||
SOLDIER_COUNTER_MAX = 20, // enough space for fillers
|
||||
};
|
||||
@@ -1779,6 +1780,7 @@ public:
|
||||
INT16 GetAPBonus();
|
||||
INT8 GetFearResistanceBonus(); // fear resistance lowers shock and morale damage from horror
|
||||
UINT8 GetMoraleThreshold();
|
||||
FLOAT GetMoraleModifier();
|
||||
INT16 GetInterruptModifier( UINT8 usDistance );
|
||||
|
||||
void SoldierPropertyUpkeep(); // update functions for various properties (updating counters, resetting flags etc.)
|
||||
@@ -1820,6 +1822,11 @@ public:
|
||||
BOOLEAN CanSpot( INT32 sTargetGridNo = -1 );
|
||||
BOOLEAN BecomeSpotter( INT32 sTargetGridNo );
|
||||
|
||||
// Flugente: enemy roles
|
||||
BOOLEAN HasSniper();
|
||||
BOOLEAN CanMedicAI(); // AI-only: can we heal a wounded ally? Do NOT, repeat, NOT use this with mercs!
|
||||
BOOLEAN AIDoctorFriend(); // AI-only: heal a wounded friend. Do NOT, repeat, NOT use this with mercs!
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}; // SOLDIERTYPE;
|
||||
|
||||
+92
-71
@@ -1669,56 +1669,25 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT
|
||||
// We have a function for this
|
||||
type = pSoldier->GetSoldierProfileType( pCreateStruct->bTeam );
|
||||
|
||||
/* if ( pCreateStruct->bTeam == ENEMY_TEAM && gGameExternalOptions.fSoldierProfiles_Enemy )
|
||||
{
|
||||
switch( pCreateStruct->ubSoldierClass )
|
||||
if ( type > -1 )
|
||||
{
|
||||
case SOLDIER_CLASS_ELITE :
|
||||
type = 2;
|
||||
break;
|
||||
case SOLDIER_CLASS_ARMY :
|
||||
type = 1;
|
||||
break;
|
||||
case SOLDIER_CLASS_ADMINISTRATOR :
|
||||
type = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( pCreateStruct->bTeam == MILITIA_TEAM && gGameExternalOptions.fSoldierProfiles_Militia )
|
||||
{
|
||||
switch( pCreateStruct->ubSoldierClass )
|
||||
{
|
||||
case SOLDIER_CLASS_ELITE_MILITIA :
|
||||
type = 5;
|
||||
break;
|
||||
case SOLDIER_CLASS_REG_MILITIA :
|
||||
type = 4;
|
||||
break;
|
||||
case SOLDIER_CLASS_GREEN_MILITIA :
|
||||
type = 3;
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
INT16 availablenames[NUM_SOLDIER_PROFILES];
|
||||
UINT16 cnt = 0;
|
||||
|
||||
if ( type > -1 )
|
||||
{
|
||||
INT16 availablenames[NUM_SOLDIER_PROFILES];
|
||||
UINT16 cnt = 0;
|
||||
for (UINT16 i = 1; i < num_found_soldier_profiles[type]; ++i)
|
||||
{
|
||||
// make sure the name isn't already currently in use!
|
||||
if ( !IsProfileInUse(pCreateStruct->bTeam, type, i) )
|
||||
availablenames[cnt++] = i;
|
||||
}
|
||||
|
||||
for (UINT16 i = 1; i < num_found_soldier_profiles[type]; ++i)
|
||||
{
|
||||
// make sure the name isn't already currently in use!
|
||||
if ( !IsProfileInUse(pCreateStruct->bTeam, type, i) )
|
||||
availablenames[cnt++] = i;
|
||||
}
|
||||
|
||||
if ( cnt > 0 )
|
||||
{
|
||||
pSoldier->usSoldierProfile = availablenames[Random(cnt)];
|
||||
if ( cnt > 0 )
|
||||
{
|
||||
pSoldier->usSoldierProfile = availablenames[Random(cnt)];
|
||||
|
||||
if ( zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType > 0 && zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType < 5 )
|
||||
pSoldier->ubBodyType = zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType - 1;
|
||||
if ( zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType > 0 && zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType < 5 )
|
||||
pSoldier->ubBodyType = zSoldierProfile[type][pSoldier->usSoldierProfile].uiBodyType - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1737,6 +1706,22 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT
|
||||
AssignTraitsToSoldier( pSoldier, pCreateStruct );
|
||||
if ( gGameExternalOptions.fAssignTraitsToMilitia && SOLDIER_CLASS_MILITIA( pSoldier->ubSoldierClass ) )
|
||||
AssignTraitsToSoldier( pSoldier, pCreateStruct );
|
||||
|
||||
// Flugente: enemy roles
|
||||
if ( gGameExternalOptions.fEnemyRoles && gGameExternalOptions.fEnemyOfficers && SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass ) )
|
||||
{
|
||||
if ( HAS_SKILL_TRAIT( pSoldier, SQUADLEADER_NT) )
|
||||
{
|
||||
UINT8 numenemies = NumEnemyInSector();
|
||||
BOOL officertype = OFFICER_NONE;
|
||||
UINT8 numofficers = HighestEnemyOfficersInSector( officertype );
|
||||
|
||||
// this guy becomes an officer if there are enough soldiers around, and we aren't already at max of officers
|
||||
if ( numenemies > gGameExternalOptions.usEnemyOfficersPerTeamSize * numofficers && numofficers < gGameExternalOptions.usEnemyOfficersMax )
|
||||
pSoldier->bSoldierFlagMask |= SOLDIER_ENEMY_OFFICER;
|
||||
}
|
||||
}
|
||||
|
||||
// SANDRO - If neither of these two options are activated, use the original code
|
||||
// however, "no traits" means no traits at all, so commented out
|
||||
/*if ( !(gGameExternalOptions.fAssignTraitsToEnemy) && !(gGameExternalOptions.fAssignTraitsToMilitia) )
|
||||
@@ -4649,6 +4634,8 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre
|
||||
BOOLEAN foundHtH = FALSE;
|
||||
BOOLEAN foundMelee = FALSE;
|
||||
BOOLEAN fRadioSetFound = FALSE;
|
||||
BOOLEAN fFirstAidKitFound = FALSE;
|
||||
BOOLEAN fMedKitFound = FALSE;
|
||||
|
||||
// FIRST FIND OUT THE COMPOSITION OF OUR GEAR
|
||||
INT8 invsize = (INT8)pSoldier->inv.size();
|
||||
@@ -4675,10 +4662,69 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre
|
||||
foundMelee = TRUE;
|
||||
else if ( HasItemFlag(pCreateStruct->Inv[bLoop].usItem, RADIO_SET) )
|
||||
fRadioSetFound = TRUE;
|
||||
else if ( Item[pCreateStruct->Inv[bLoop].usItem].firstaidkit )
|
||||
fFirstAidKitFound = TRUE;
|
||||
else if ( Item[pCreateStruct->Inv[bLoop].usItem].medicalkit )
|
||||
{
|
||||
// Flugente: for enemy medic purposes, med kits also count as first aid kits
|
||||
fFirstAidKitFound = TRUE;
|
||||
fMedKitFound = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOW BASED ON WHAT WE HAVE FOUND, ASSIGN UNASSIGNED TRAITS
|
||||
// Flugente: radio operator
|
||||
if ( gGameOptions.fNewTraitSystem && (!ATraitAssigned || !BTraitAssigned || !CTraitAssigned ) )
|
||||
{
|
||||
// if we have a radio set, give us the corresponding trait so we can use it...
|
||||
if ( fRadioSetFound )
|
||||
{
|
||||
// this is a minor trait, so try to first fill the minor slot if possible
|
||||
if ( ubSolClass == SOLDIER_CLASS_ELITE || ubSolClass == SOLDIER_CLASS_ELITE_MILITIA )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[2] = RADIO_OPERATOR_NT;
|
||||
CTraitAssigned = TRUE;
|
||||
return( TRUE ); // We no longer need to continue from here
|
||||
}
|
||||
else if ( !ATraitAssigned )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[0] = RADIO_OPERATOR_NT;
|
||||
ATraitAssigned = TRUE;
|
||||
}
|
||||
else if ( !BTraitAssigned )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[1] = RADIO_OPERATOR_NT;
|
||||
BTraitAssigned = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flugente: enemy roles allow medics
|
||||
if ( gGameExternalOptions.fEnemyRoles && gGameExternalOptions.fEnemyMedics && gGameOptions.fNewTraitSystem && (!ATraitAssigned || !BTraitAssigned ) )
|
||||
{
|
||||
// if we have a radio set, give us the corresponding trait so we can use it...
|
||||
if ( fFirstAidKitFound )
|
||||
{
|
||||
if ( !ATraitAssigned )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[0] = DOCTOR_NT;
|
||||
ATraitAssigned = TRUE;
|
||||
|
||||
// if we found a medkit, chance to become a full doctor
|
||||
if ( !BTraitAssigned && fMedKitFound && Chance(50) )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[1] = DOCTOR_NT;
|
||||
BTraitAssigned = TRUE;
|
||||
}
|
||||
}
|
||||
else if ( !BTraitAssigned )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[1] = DOCTOR_NT;
|
||||
BTraitAssigned = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HEAVY WEAPONS TRAIT
|
||||
if ( foundMortar || foundRocketlauncher || foundGrenadelauncher )
|
||||
@@ -5213,31 +5259,6 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flugente: new traits
|
||||
if ( gGameOptions.fNewTraitSystem && (!ATraitAssigned || !BTraitAssigned || !CTraitAssigned ) )
|
||||
{
|
||||
// if we have a radio set, give us the corresponding trait so we can use it...
|
||||
if ( fRadioSetFound )
|
||||
{
|
||||
if ( !ATraitAssigned )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[0] = RADIO_OPERATOR_NT;
|
||||
ATraitAssigned = TRUE;
|
||||
}
|
||||
else if ( !BTraitAssigned )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[1] = RADIO_OPERATOR_NT;
|
||||
BTraitAssigned = TRUE;
|
||||
}
|
||||
else if ( ubSolClass == SOLDIER_CLASS_ELITE || ubSolClass == SOLDIER_CLASS_ELITE_MILITIA )
|
||||
{
|
||||
pSoldier->stats.ubSkillTraits[2] = RADIO_OPERATOR_NT;
|
||||
CTraitAssigned = TRUE;
|
||||
return( TRUE ); // We no longer need to continue from here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// RETURN TRUE IF ALL TRAITS ASSIGNED OTHERWISE FALSE
|
||||
if (( !gGameOptions.fNewTraitSystem && ATraitAssigned && BTraitAssigned ) || ( gGameOptions.fNewTraitSystem && ATraitAssigned && BTraitAssigned && CTraitAssigned ) )
|
||||
|
||||
@@ -276,6 +276,9 @@ void SwapMercPortraits ( SOLDIERTYPE *pSoldier, INT8 bDirection );
|
||||
extern INT8 GetTeamSlotFromPlayerID( UINT8 ubID );
|
||||
extern FACETYPE *gpCurrentTalkingFace;
|
||||
|
||||
// Flugente: toggle display of enemy role indicators
|
||||
extern BOOLEAN gDisplayEnemyRoles;
|
||||
|
||||
// sevenfm: new mouse commands
|
||||
void HandleAltMouseTBWheel( void );
|
||||
void HandleAltMouseTBMButton( UINT32 *puiNewEvent );
|
||||
@@ -2999,7 +3002,7 @@ void GetKeyboardInput( UINT32 *puiNewEvent )
|
||||
else
|
||||
{
|
||||
HandleTacticalCoverMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -7820,7 +7823,8 @@ void HandleTacticalCoverMenu( void )
|
||||
}
|
||||
gzUserDefinedButtonColor[0] = 0;
|
||||
gzUserDefinedButtonColor[1] = FONT_MCOLOR_LTYELLOW;
|
||||
gzUserDefinedButtonColor[2] = FONT_MCOLOR_LTYELLOW;
|
||||
gzUserDefinedButtonColor[2] = FONT_MCOLOR_LTYELLOW;
|
||||
gzUserDefinedButtonColor[4] = FONT_LTRED;
|
||||
gzUserDefinedButtonColor[8] = FONT_ORANGE;
|
||||
gzUserDefinedButtonColor[9] = FONT_ORANGE;
|
||||
gzUserDefinedButtonColor[10] = FONT_ORANGE;
|
||||
@@ -7853,7 +7857,8 @@ void TacticalCoverMessageBoxCallBack( UINT8 ubExitValue )
|
||||
break;
|
||||
case 4:
|
||||
break;
|
||||
case 5:
|
||||
case 5:
|
||||
gDisplayEnemyRoles = !gDisplayEnemyRoles;
|
||||
break;
|
||||
case 6:
|
||||
break;
|
||||
|
||||
@@ -2170,7 +2170,12 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo,
|
||||
if ( pOpponent->UsesScubaGear() )
|
||||
return;
|
||||
// Flugente: update our sight concerning this guy, otherwise we could get way with open attacks because this does not get updated
|
||||
pSoldier->RecognizeAsCombatant(pOpponent->ubID);
|
||||
if ( pSoldier->RecognizeAsCombatant(pOpponent->ubID) )
|
||||
{
|
||||
// Flugente: note that this enemy has been seen by mercs this turn
|
||||
if ( pOpponent->bTeam == ENEMY_TEAM && pSoldier->bTeam == OUR_TEAM )
|
||||
pOpponent->bSoldierFlagMask |= SOLDIER_ENEMY_OBSERVEDTHISTURN;
|
||||
}
|
||||
|
||||
// if we're seeing a guy we didn't see on our last chance to look for him
|
||||
if (pSoldier->aiData.bOppList[pOpponent->ubID] != SEEN_CURRENTLY)
|
||||
@@ -2506,7 +2511,7 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo,
|
||||
BOOLEAN fAddAsOpponent = FALSE;
|
||||
if ( !CONSIDERED_NEUTRAL( pSoldier, pOpponent ) && (pSoldier->bSide != pOpponent->bSide) && pSoldier->RecognizeAsCombatant(pOpponent->ubID) )
|
||||
{
|
||||
// ... check wether he is not neutral against us (account for the fact that we might be covert!)
|
||||
// ... check wether he is not neutral against us (account for the fact that we might be covert!)
|
||||
// if we are an NPC assassin
|
||||
if ( pSoldier->bSoldierFlagMask & SOLDIER_ASSASSIN && pSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) )
|
||||
{
|
||||
|
||||
@@ -2305,6 +2305,13 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
break;
|
||||
|
||||
case AI_ACTION_DOCTOR:
|
||||
{
|
||||
pSoldier->AIDoctorFriend();
|
||||
ActionDone( pSoldier );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef BETAVERSION
|
||||
NumMessage("ExecuteAction - Illegal action type = ",pSoldier->aiData.bAction);
|
||||
|
||||
+68
-15
@@ -594,6 +594,10 @@ BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier)
|
||||
bMinPointsNeeded = 10; // TODO
|
||||
break;
|
||||
|
||||
case AI_ACTION_DOCTOR:
|
||||
bMinPointsNeeded = 20; // TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef BETAVERSION
|
||||
//NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction);
|
||||
@@ -3064,40 +3068,44 @@ INT32 CalcStraightThreatValue( SOLDIERTYPE *pEnemy )
|
||||
// Flugente: get the id of the closest soldier with a specific flag that we can currently see
|
||||
UINT8 GetClosestFlaggedSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTeam, UINT32 aFlag )
|
||||
{
|
||||
UINT8 id = NOBODY;
|
||||
|
||||
UINT8 id = NOBODY;
|
||||
UINT32 uiLoop;
|
||||
BOOLEAN fRangeRestricted = FALSE, fFound = FALSE;
|
||||
SOLDIERTYPE * pFriend;
|
||||
INT16 range = aRange;
|
||||
|
||||
// go through each soldier, looking for "friends" (soldiers on same side)
|
||||
for (uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop)
|
||||
// go through each soldier, looking for "friends" (soldiers on same team)
|
||||
for (uiLoop = gTacticalStatus.Team[ auTeam ].bFirstID; uiLoop < gTacticalStatus.Team[ auTeam ].bLastID; ++uiLoop)
|
||||
{
|
||||
pFriend = MercSlots[ uiLoop ];
|
||||
pFriend = MercPtrs[ uiLoop ];
|
||||
|
||||
// if this merc is inactive, not in sector, or dead
|
||||
if (!pFriend)
|
||||
continue;
|
||||
|
||||
// skip ourselves
|
||||
if (pFriend->ubID == pSoldier->ubID)
|
||||
continue;
|
||||
|
||||
// must be on the same level
|
||||
if ( pFriend->pathing.bLevel != pSoldier->pathing.bLevel )
|
||||
continue;
|
||||
|
||||
if ( auTeam != pFriend->bTeam )
|
||||
// skip if this guy is dead
|
||||
if ( pFriend->stats.bLife <= 0 )
|
||||
continue;
|
||||
|
||||
// check for flag
|
||||
if ( !(pFriend->bSoldierFlagMask & aFlag) )
|
||||
continue;
|
||||
|
||||
// skip ourselves
|
||||
if (pFriend->ubID == pSoldier->ubID)
|
||||
continue;
|
||||
|
||||
// if we're not already neighbors
|
||||
if (SpacesAway(pSoldier->sGridNo, pFriend->sGridNo) < range)
|
||||
|
||||
// are we close enough?
|
||||
INT16 friendrange = SpacesAway(pSoldier->sGridNo, pFriend->sGridNo);
|
||||
if ( friendrange < range)
|
||||
{
|
||||
// can we see this guy?
|
||||
if ( SoldierTo3DLocationLineOfSightTest( pSoldier, pFriend->sGridNo, pSoldier->pathing.bLevel, 3, TRUE, CALC_FROM_WANTED_DIR ) )
|
||||
{
|
||||
range = SpacesAway(pSoldier->sGridNo,pFriend->sGridNo);
|
||||
range = friendrange;
|
||||
id = pFriend->ubID;
|
||||
}
|
||||
}
|
||||
@@ -3105,3 +3113,48 @@ UINT8 GetClosestFlaggedSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 au
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
// get the id of the closest soldier (closer than x tiles) of a specific team that is wounded that we can currently see
|
||||
UINT8 GetClosestWoundedSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTeam )
|
||||
{
|
||||
UINT8 id = NOBODY;
|
||||
UINT32 uiLoop;
|
||||
SOLDIERTYPE * pFriend;
|
||||
INT16 range = aRange;
|
||||
|
||||
// go through each soldier, looking for "friends" (soldiers on same team)
|
||||
for (uiLoop = gTacticalStatus.Team[ auTeam ].bFirstID; uiLoop < gTacticalStatus.Team[ auTeam ].bLastID; ++uiLoop)
|
||||
{
|
||||
pFriend = MercPtrs[ uiLoop ];
|
||||
|
||||
// if this merc is inactive, not in sector, or dead
|
||||
if (!pFriend)
|
||||
continue;
|
||||
|
||||
// skip ourselves
|
||||
if (pFriend->ubID == pSoldier->ubID)
|
||||
continue;
|
||||
|
||||
// must be on the same level
|
||||
if ( pFriend->pathing.bLevel != pSoldier->pathing.bLevel )
|
||||
continue;
|
||||
|
||||
// skip if this guy is dead, not wounded or cannot be healed
|
||||
if ( pFriend->stats.bLife <= 0 || pFriend->iHealableInjury <= 0 || pFriend->stats.bLife >= pFriend->stats.bLifeMax )
|
||||
continue;
|
||||
|
||||
// are we close enough?
|
||||
INT16 friendrange = SpacesAway(pSoldier->sGridNo, pFriend->sGridNo);
|
||||
if ( friendrange < range)
|
||||
{
|
||||
// can we see this guy?
|
||||
if ( SoldierTo3DLocationLineOfSightTest( pSoldier, pFriend->sGridNo, pSoldier->pathing.bLevel, 3, TRUE, CALC_FROM_WANTED_DIR ) )
|
||||
{
|
||||
range = friendrange;
|
||||
id = pFriend->ubID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
+126
-1
@@ -930,6 +930,48 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier)
|
||||
return( AI_ACTION_RED_ALERT );
|
||||
}
|
||||
}
|
||||
|
||||
// if we are a doctor with medical gear, we might be able to help a wounded ally
|
||||
if ( pSoldier->CanMedicAI() )
|
||||
{
|
||||
UINT8 ubPerson = GetClosestWoundedSoldierID( pSoldier, gGameExternalOptions.sEnemyMedicsSearchRadius, pSoldier->bTeam);
|
||||
|
||||
if ( ubPerson != NOBODY )
|
||||
{
|
||||
if ( PythSpacesAway(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo) < 2 )
|
||||
{
|
||||
// see if we are facing this person
|
||||
UINT8 ubDesiredMercDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(MercPtrs[ubPerson]->sGridNo),CenterY(MercPtrs[ubPerson]->sGridNo));
|
||||
|
||||
// if not already facing in that direction,
|
||||
if ( pSoldier->ubDirection != ubDesiredMercDir )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ubDesiredMercDir;
|
||||
|
||||
return( AI_ACTION_CHANGE_FACING );
|
||||
}
|
||||
|
||||
// if not already crouched, crouch down first
|
||||
if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_CROUCH && IsValidStance( pSoldier, ANIM_CROUCH ) && GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ANIM_CROUCH;
|
||||
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
|
||||
return(AI_ACTION_DOCTOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, MercPtrs[ubPerson]->sGridNo, 20, AI_ACTION_SEEK_FRIEND, 0);
|
||||
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
|
||||
{
|
||||
return(AI_ACTION_SEEK_FRIEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//ddd}
|
||||
|
||||
@@ -1528,7 +1570,6 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
|
||||
// IF YOU SEE CAPTURED FRIENDS, FREE THEM!
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Flugente: if we see one of our buddies in handcuffs, its a clear sign of enemy activity!
|
||||
// Flugente: if we see one of our buddies captured, it is a clear sign of enemy activity!
|
||||
if ( gGameExternalOptions.fAllowPrisonerSystem && pSoldier->bTeam == ENEMY_TEAM )
|
||||
{
|
||||
@@ -1572,6 +1613,48 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we are a doctor with medical gear, we might be able to help a wounded ally
|
||||
if ( pSoldier->CanMedicAI() )
|
||||
{
|
||||
UINT8 ubPerson = GetClosestWoundedSoldierID( pSoldier, gGameExternalOptions.sEnemyMedicsSearchRadius, pSoldier->bTeam);
|
||||
|
||||
if ( ubPerson != NOBODY )
|
||||
{
|
||||
if ( PythSpacesAway(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo) < 2 )
|
||||
{
|
||||
// see if we are facing this person
|
||||
UINT8 ubDesiredMercDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(MercPtrs[ubPerson]->sGridNo),CenterY(MercPtrs[ubPerson]->sGridNo));
|
||||
|
||||
// if not already facing in that direction,
|
||||
if ( pSoldier->ubDirection != ubDesiredMercDir )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ubDesiredMercDir;
|
||||
|
||||
return( AI_ACTION_CHANGE_FACING );
|
||||
}
|
||||
|
||||
// if not already crouched, crouch down first
|
||||
if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_CROUCH && IsValidStance( pSoldier, ANIM_CROUCH ) && GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ANIM_CROUCH;
|
||||
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
|
||||
return(AI_ACTION_DOCTOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, MercPtrs[ubPerson]->sGridNo, 20, AI_ACTION_SEEK_FRIEND, 0);
|
||||
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
|
||||
{
|
||||
return(AI_ACTION_SEEK_FRIEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@@ -2854,6 +2937,48 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we are a doctor with medical gear, we might be able to help a wounded ally
|
||||
if ( pSoldier->CanMedicAI() )
|
||||
{
|
||||
UINT8 ubPerson = GetClosestWoundedSoldierID( pSoldier, gGameExternalOptions.sEnemyMedicsSearchRadius, pSoldier->bTeam);
|
||||
|
||||
if ( ubPerson != NOBODY )
|
||||
{
|
||||
if ( PythSpacesAway(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo) < 2 )
|
||||
{
|
||||
// see if we are facing this person
|
||||
UINT8 ubDesiredMercDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(MercPtrs[ubPerson]->sGridNo),CenterY(MercPtrs[ubPerson]->sGridNo));
|
||||
|
||||
// if not already facing in that direction,
|
||||
if ( pSoldier->ubDirection != ubDesiredMercDir )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ubDesiredMercDir;
|
||||
|
||||
return( AI_ACTION_CHANGE_FACING );
|
||||
}
|
||||
|
||||
// if not already crouched, crouch down first
|
||||
if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_CROUCH && IsValidStance( pSoldier, ANIM_CROUCH ) && GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ANIM_CROUCH;
|
||||
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
|
||||
return(AI_ACTION_DOCTOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, MercPtrs[ubPerson]->sGridNo, 20, AI_ACTION_SEEK_FRIEND, 0);
|
||||
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
|
||||
{
|
||||
return(AI_ACTION_SEEK_FRIEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -104,6 +104,7 @@ typedef enum
|
||||
AI_ACTION_JUMP_WINDOW, // added by Flugente: jump through a window
|
||||
AI_ACTION_FREE_PRISONER, // added by Flugente: free a prisoner
|
||||
AI_ACTION_USE_SKILL, // added by Flugente: perform a skill, which one is stored in usAISkillUse
|
||||
AI_ACTION_DOCTOR, // added by Flugente: AI-ONLY! bandage/surgery on fellow AI. DO NOT USE THIS FOR MERCS!!!
|
||||
} ActionType;
|
||||
|
||||
|
||||
@@ -262,4 +263,7 @@ UINT8 NumberOfTeamMatesAdjacent( SOLDIERTYPE * pSoldier, INT32 sGridNo );
|
||||
// Flugente: get the id of the closest soldier (coser than x tiles) of a specific team with a specific flag that we can currently see
|
||||
UINT8 GetClosestFlaggedSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTeam, UINT32 aFlag );
|
||||
|
||||
// get the id of the closest soldier (closer than x tiles) of a specific team that is wounded that we can currently see
|
||||
UINT8 GetClosestWoundedSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTeam );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8479,7 +8479,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8493,7 +8493,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8477,7 +8477,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles",
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8475,7 +8475,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc.",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8303,7 +8303,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8485,7 +8485,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8503,7 +8503,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8482,7 +8482,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
@@ -8493,7 +8493,7 @@ STR16 szTacticalCoverDialogString[]=
|
||||
L"Merc",
|
||||
L"",
|
||||
|
||||
L"",
|
||||
L"Roles", // TODO.Translate
|
||||
L"",
|
||||
L"",
|
||||
L"",
|
||||
|
||||
Reference in New Issue
Block a user