New feature: turncoats can be created from enemies and ordered to switch sides

Requires GameDir >= r2535

For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&goto=360191&#msg_360191

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8811 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2020-05-30 00:13:42 +00:00
parent 0f13599775
commit c5022fa1e5
44 changed files with 1587 additions and 140 deletions
+2 -2
View File
@@ -2395,9 +2395,9 @@ void HandleRenderFaceAdjustments( FACETYPE *pFace, BOOLEAN fDisplayBuffer, BOOLE
fDoIcon_Assignment = TRUE;
fShowCustomText = TRUE;
if (pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY])
if (pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] > 10)
{
swprintf(sString, L"Hide %dh", pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY]);
swprintf(sString, L"Hide %dh", pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] / 10);
}
else
{
+124 -108
View File
@@ -6283,10 +6283,7 @@ BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier)
// this only works on enemy soldiers
if ( pSoldier->bTeam != ENEMY_TEAM && pSoldier->bTeam != CIV_TEAM )
return false;
if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] < gGameExternalOptions.usTurnsToUncover )
return false;
INT16 sXPos = 0;
INT16 sYPos = 0;
INT32 iBack = 0;
@@ -6297,113 +6294,29 @@ BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier)
sXPos += 50;
sYPos += 25;
// are we a VIP? show that only when the player knows a VIP is in this sector. otherwise, don't even show our officer property
if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP && !pSoldier->bSectorZ )
if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] >= gGameExternalOptions.usTurnsToUncover )
{
if ( PlayerKnowsAboutVIP( pSoldier->sSectorX, pSoldier->sSectorY ) )
// are we a VIP? show that only when the player knows a VIP is in this sector. otherwise, don't even show our officer property
if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP && !pSoldier->bSectorZ )
{
// Add bars
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20), (INT16)(sYPos + 20) );
if ( iBack != -1 )
if ( PlayerKnowsAboutVIP( pSoldier->sSectorX, pSoldier->sSectorY ) )
{
SetBackgroundRectFilled( iBack );
// 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, 6, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
sYPos += 20;
}
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 6, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
sYPos += 20;
}
}
// is this guy an officer?
if ( pSoldier->usSoldierFlagMask & SOLDIER_ENEMY_OFFICER )
{
// Add bars
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos + 20 ), (INT16)(sYPos + 20 ) );
if ( iBack != -1 )
{
SetBackgroundRectFilled( iBack );
}
// if we look at this guy long enough, we might even learn that he is an advanced officer
if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] > 1.5 * gGameExternalOptions.usTurnsToUncover && NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT ) > 1 )
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 5, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
else
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;
}
// do we carry a key?
for ( int ubLoop = 0; ubLoop < NUM_KEYS; ++ubLoop )
{
if ( SoldierHasKey( pSoldier, ubLoop ) )
// is this guy an officer?
if ( pSoldier->usSoldierFlagMask & SOLDIER_ENEMY_OFFICER )
{
// Add bars
iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)( sXPos + 20 ), (INT16)( sYPos + 20 ) );
@@ -6413,12 +6326,115 @@ BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier)
SetBackgroundRectFilled( iBack );
}
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 11, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
// if we look at this guy long enough, we might even learn that he is an advanced officer
if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] > 1.5 * gGameExternalOptions.usTurnsToUncover && NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT ) > 1 )
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 5, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
else
BltVideoObjectFromIndex( FRAME_BUFFER, guiENEMYROLES, 0, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
sYPos += 20;
break;
}
// 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;
}
// do we carry a key?
for ( int ubLoop = 0; ubLoop < NUM_KEYS; ++ubLoop )
{
if ( SoldierHasKey( pSoldier, ubLoop ) )
{
// 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, 11, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
sYPos += 20;
break;
}
}
}
// turncoats are instantly visible
if ( gSkillTraitValues.fCOTurncoats && pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
{
// 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, 12, sXPos, sYPos, VO_BLT_TRANSSHADOW, NULL );
sYPos += 20;
}
return true;
+246 -7
View File
@@ -114,6 +114,8 @@
#include "DynamicDialogue.h" // added by Flugente for HandleDynamicOpinions()
#include "DropDown.h" // added by Flugente
#include "Creature Spreading.h" // added by Flugente forResetCreatureAttackVariables()
#include "finances.h" // added by Flugente
#include "MilitiaIndividual.h" // added by Flugente
#endif
#include "connect.h"
@@ -153,6 +155,10 @@ class OBJECTTYPE;
class SOLDIERTYPE;
extern void HandleBestSightingPositionInRealtime();
extern INT32 GetCurrentBalance( void );
extern CHAR16 gzUserDefinedButton1[128];
extern CHAR16 gzUserDefinedButton2[128];
void UpdateFastForwardMode(SOLDIERTYPE* pSoldier, INT8 bAction); // sevenfm: to call in HandleAtNewGridNo
@@ -4214,15 +4220,13 @@ void SetSoldierNeutral( SOLDIERTYPE * pSoldier )
}
void MakeCivHostile(SOLDIERTYPE *pSoldier)
{
INT8 bNewSide;
if ( pSoldier->ubBodyType == COW )
{
return;
}
// default is hostile to player, allied to army
bNewSide = 1;
INT8 bNewSide = 1;
switch( pSoldier->ubProfile )
{
@@ -4267,6 +4271,11 @@ void MakeCivHostile(SOLDIERTYPE *pSoldier)
{
bNewSide = zCivGroupName[pSoldier->ubCivilianGroup].bSide;
}
// Flugente: enemies are already hostile, let's assume we want to make them friendly instead
else if ( pSoldier->bTeam == ENEMY_TEAM )
{
bNewSide = 0;
}
if ( !pSoldier->aiData.bNeutral && bNewSide == pSoldier->bSide )
{
@@ -4302,6 +4311,63 @@ void MakeCivHostile(SOLDIERTYPE *pSoldier)
pSoldier->aiData.bOrders = FARPATROL;
}
// Flugente: turncoats
if ( pSoldier->bTeam == ENEMY_TEAM )
{
// change to enemy team
SetSoldierNonNeutral( pSoldier );
pSoldier->bSide = bNewSide;
// remember old class and switch to its equivalent
UINT8 oldclass = pSoldier->ubSoldierClass;
UINT16 sector = SECTOR( pSoldier->sSectorX, pSoldier->sSectorY );
// extract militia profile from pSoldier, so that a fitting profile is ready to be taken during creation
CreateNewIndividualMilitiaFromSoldier( pSoldier, MO_DEFECTOR );
pSoldier = ChangeSoldierTeam( pSoldier, MILITIA_TEAM );
// it doesn't make sense to force a militia reset when all changes are in the sector, so set this back to original value afterwards
//BOOLEAN tmp = gfStrategicMilitiaChangesMade;
//gfStrategicMilitiaChangesMade = tmp;
if ( oldclass == SOLDIER_CLASS_ARMY )
{
pSoldier->ubSoldierClass = SOLDIER_CLASS_REG_MILITIA;
SectorInfo[sector].ubNumTroops = max( 0, SectorInfo[sector].ubNumTroops - 1 );
SectorInfo[sector].ubTroopsInBattle = max( 0, SectorInfo[sector].ubTroopsInBattle - 1 );
}
else if ( oldclass == SOLDIER_CLASS_ELITE )
{
pSoldier->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA;
SectorInfo[sector].ubNumElites = max( 0, SectorInfo[sector].ubNumElites - 1 );
SectorInfo[sector].ubElitesInBattle = max( 0, SectorInfo[sector].ubElitesInBattle - 1 );
}
else
{
pSoldier->ubSoldierClass = SOLDIER_CLASS_GREEN_MILITIA;
SectorInfo[sector].ubNumAdmins = max( 0, SectorInfo[sector].ubNumAdmins - 1 );
SectorInfo[sector].ubAdminsInBattle = max( 0, SectorInfo[sector].ubAdminsInBattle - 1 );
}
StrategicAddMilitiaToSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->ubSoldierClass - 4, 1 );
// rehandle sight for everybody
SOLDIERTYPE* pTeamSoldier;
UINT16 iLoop = gTacticalStatus.Team[OUR_TEAM].bFirstID;
for ( pTeamSoldier = MercPtrs[iLoop]; iLoop <= gTacticalStatus.Team[CIV_TEAM].bLastID; ++iLoop, ++pTeamSoldier )
{
if ( pTeamSoldier->bActive && pTeamSoldier->bInSector && pTeamSoldier->stats.bLife > 0 )
{
RecalculateOppCntsDueToNoLongerNeutral( pTeamSoldier );
}
}
}
pSoldier->bSide = bNewSide;
if ( pSoldier->aiData.bNeutral )
@@ -6903,6 +6969,10 @@ void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
if ( pTeamSoldier->usSoldierFlagMask & SOLDIER_VIP )
DeleteVIP( pTeamSoldier->sSectorX, pTeamSoldier->sSectorY );
// Flugente: turncoats
if ( pTeamSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
RemoveOneTurncoat( sMapX, sMapY, pTeamSoldier->ubSoldierClass );
// Flugente: campaign stats
gCurrentIncident.AddStat( pTeamSoldier, CAMPAIGNHISTORY_TYPE_PRISONER );
@@ -10520,7 +10590,138 @@ void HandleDisplayingOfPlayerLostDialogue( )
}
#endif
static UINT8 prisonerdialoguetargetID = 0;
static UINT8 prisonerdialoguetargetID = NOBODY;
void TurnCoatAttemptMessageBoxCallBack( UINT8 ubExitValue )
{
// check ubExitValue to see whether we actually want to go through (2)
if ( ubExitValue != 2
|| prisonerdialoguetargetID == NOBODY
|| gusSelectedSoldier == NOBODY )
return;
SOLDIERTYPE* pSoldier = MercPtrs[prisonerdialoguetargetID];
if ( !pSoldier )
return;
INT16 approachselected = DropDownTemplate<DROPDOWNNR_MSGBOX_1>::getInstance().GetSelectedEntryKey();
UINT8 approachchance = MercPtrs[gusSelectedSoldier]->GetTurncoatConvinctionChance( prisonerdialoguetargetID, approachselected );
// you can never turn a VIP (though we don't tell the player if someone is a VIP, lest they have an exploit to find out)
if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP )
approachchance = 0;
// as using random numbers to pass the check would result in players savescumming, use a number based on the soldier's stats
UINT32 soldierconsistentnumber = ( 23 * EffectiveAgility( pSoldier, FALSE ) + 83 * EffectiveExplosive( pSoldier ) + 19 * EffectiveMarksmanship( pSoldier ) + 92 * pSoldier->stats.bLifeMax ) % 100;
if ( soldierconsistentnumber < approachchance )
{
pSoldier->usSoldierFlagMask2 |= SOLDIER_TURNCOAT;
AddOneTurncoat( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->ubSoldierClass );
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szTurncoatText[0], pSoldier->GetName() );
// increase intel penalty. We can only try to convert enemies if the penalty is low, and having a high penalty means we can't mine intel for a few hours but have to hide
MercPtrs[gusSelectedSoldier]->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] += 1;
StatChange( MercPtrs[gusSelectedSoldier], EXPERAMT, 2, FROM_SUCCESS );
StatChange( MercPtrs[gusSelectedSoldier], LDRAMT, 4, FROM_SUCCESS );
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szTurncoatText[1], pSoldier->GetName() );
// increase intel penalty. We can only try to convert enemies if the penalty is low, and having a high penalty means we can't mine intel for a few hours but have to hide
MercPtrs[gusSelectedSoldier]->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] += 4;
StatChange( MercPtrs[gusSelectedSoldier], EXPERAMT, 1, FROM_FAILURE );
StatChange( MercPtrs[gusSelectedSoldier], LDRAMT, 1, FROM_FAILURE );
}
// explain that suspicion is so high that we have to stop
if ( MercPtrs[gusSelectedSoldier]->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] >= 20 )
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szTurncoatText[2] );
// use up resources spent, regardless of whether or not we were successful
if ( approachselected == 3 )
{
INT32 bribeamount = 10 * min( 10, CurrentPlayerProgressPercentage() );
// substract money spent
AddTransactionToPlayersBook ( TRANSFER_FUNDS_TO_MERC, MercPtrs[gusSelectedSoldier]->ubProfile, GetWorldTotalMin(), -bribeamount );
}
else if ( approachselected == 4 )
{
int intelbribeneeded = max( 2, ( CurrentPlayerProgressPercentage() + 5 ) / 10 );
AddIntel( -intelbribeneeded, TRUE );
}
//ReduceAttackBusyCount();
}
CHAR16 gTurncoatDropdownText[4][256];
void HandleTurncoatAttempt( SOLDIERTYPE* pSoldier )
{
// abort if bad pointer, or not an enemy and not a capturable civilian
if ( pSoldier
&& pSoldier->bTeam == ENEMY_TEAM
&& pSoldier->ubProfile == NO_PROFILE
&& !( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
&& !gTacticalStatus.Team[ENEMY_TEAM].bAwareOfOpposition
&& !pSoldier->RecognizeAsCombatant( gusSelectedSoldier ) )
{
// remember the target's ID
prisonerdialoguetargetID = pSoldier->ubID;
CHAR16 buf[256];
UINT8 ubCurrentProgress = CurrentPlayerProgressPercentage();
std::vector<std::pair<INT16, STR16> > dropdownvector_1;
int cnt = 1;
UINT8 chance = MercPtrs[gusSelectedSoldier]->GetTurncoatConvinctionChance( prisonerdialoguetargetID, cnt );
swprintf( gTurncoatDropdownText[cnt-1], szTurncoatText[3], chance );
dropdownvector_1.push_back( std::make_pair( cnt, gTurncoatDropdownText[cnt - 1] ) );
++cnt;
chance = MercPtrs[gusSelectedSoldier]->GetTurncoatConvinctionChance( prisonerdialoguetargetID, cnt );
swprintf( gTurncoatDropdownText[cnt - 1], szTurncoatText[4], chance );
dropdownvector_1.push_back( std::make_pair( cnt, gTurncoatDropdownText[cnt - 1] ) );
++cnt;
INT32 balance = GetCurrentBalance();
INT32 bribeamount = 10 * min( 10, ubCurrentProgress );
if ( bribeamount <= balance )
{
chance = MercPtrs[gusSelectedSoldier]->GetTurncoatConvinctionChance( prisonerdialoguetargetID, cnt );
swprintf( gTurncoatDropdownText[cnt - 1], szTurncoatText[5], bribeamount, chance );
dropdownvector_1.push_back( std::make_pair( cnt, gTurncoatDropdownText[cnt - 1] ) );
}
++cnt;
FLOAT intelreserve = GetIntel();
int intelbribeneeded = max( 2, ( ubCurrentProgress + 5 ) / 10 );
if ( intelbribeneeded <= intelreserve )
{
chance = MercPtrs[gusSelectedSoldier]->GetTurncoatConvinctionChance( prisonerdialoguetargetID, cnt );
swprintf( gTurncoatDropdownText[cnt - 1], szTurncoatText[6], intelbribeneeded, chance );
dropdownvector_1.push_back( std::make_pair( cnt, gTurncoatDropdownText[cnt - 1] ) );
}
DropDownTemplate<DROPDOWNNR_MSGBOX_1>::getInstance().SetEntries( dropdownvector_1 );
wcscpy( gzUserDefinedButton1, szTurncoatText[8] );
wcscpy( gzUserDefinedButton2, pEpcMenuStrings[4] );
DoMessageBox( MSG_BOX_BASIC_STYLE, szTurncoatText[7], guiCurrentScreen, ( MSG_BOX_FLAG_GENERIC_TWO_BUTTONS | MSG_BOX_FLAG_DROPDOWN_1 ),
TurnCoatAttemptMessageBoxCallBack, NULL );
}
}
void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue )
{
@@ -10679,11 +10880,11 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue )
}
}
}
// we offered to surrender OURSELVES TO the enemy
// we offered to surrender OURSELVES TO the enemy, or are attempting to turn a soldier into a turncoat
else if ( ubExitValue == 2 )
{
// we cannot surrender to bandits, talk instead
if ( MercPtrs[prisonerdialoguetargetID]->bTeam != CREATURE_TEAM )
if ( MercPtrs[prisonerdialoguetargetID]->bTeam == CREATURE_TEAM )
{
// normal dialog
StartCivQuote( MercPtrs[prisonerdialoguetargetID] );
@@ -10740,7 +10941,7 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue )
else if ( ubExitValue == 3 )
{
// Flugente: if we are disguised and talk to a non-profile NPC, we will continue to 'chat' with the enemy as long as we aren't ordered to do something else.
// This way we can easily order our spies to 'distract 'enemies'
// This way we can easily order our spies to 'distract' enemies
if ( GetSoldier( &pSoldier, gusSelectedSoldier ) &&
pSoldier->bTeam == gbPlayerNum &&
MercPtrs[prisonerdialoguetargetID] &&
@@ -11128,6 +11329,27 @@ UINT16 NumSoldiersWithFlagInSector( UINT8 aTeam, UINT32 aFlag )
return num;
}
UINT16 NumSoldiersofClassWithFlag2InSector( UINT8 aTeam, UINT8 aSoldierClass, UINT32 aFlag )
{
SOLDIERTYPE* pSoldier;
INT32 cnt = 0;
UINT16 num = 0;
for ( cnt = gTacticalStatus.Team[aTeam].bFirstID, pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[aTeam].bLastID; pSoldier++, ++cnt )
{
if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 )
{
if ( (pSoldier->usSoldierFlagMask2 & aFlag)
&& pSoldier->ubSoldierClass == aSoldierClass )
{
++num;
}
}
}
return num;
}
INT32 GetClosestSoldierWithFlag( UINT8 aTeam, UINT32 aFlag )
{
INT32 sBestGridNo = NOWHERE;
@@ -11414,3 +11636,20 @@ BOOLEAN IsCivFactionMemberAliveInSector( UINT8 usCivilianGroup )
return FALSE;
}
BOOLEAN IsFreeSlotAvailable( int aTeam )
{
SOLDIERTYPE *pSoldier;
INT32 cnt = gTacticalStatus.Team[aTeam].bFirstID;
// run through list
for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[aTeam].bLastID; ++cnt, ++pSoldier )
{
if ( !pSoldier->bActive )
{
return TRUE;
}
}
return FALSE;
}
+6
View File
@@ -357,6 +357,9 @@ extern UINT8 NumZombiesInSector();
// Flugente: offer the enemy the chance to surrender
void HandleSurrenderOffer( SOLDIERTYPE* pSoldier );
// Flugente: attempt to convince a soldier to become a turncoat
void HandleTurncoatAttempt( SOLDIERTYPE* pSoldier );
// Flugente: disguised mercs can distract enemies by talking to them. In order to display that, we sometimes display excerpts of their 'chats'
void CheckChatPartners();
@@ -401,6 +404,7 @@ UINT8 HighestEnemyOfficersInSector(UINT8& aType);
// count all soldiers in the current sector that have a specific flag set
UINT16 NumSoldiersWithFlagInSector(UINT8 aTeam, UINT32 aFlag);
UINT16 NumSoldiersofClassWithFlag2InSector( UINT8 aTeam, UINT8 aSoldierClass, UINT32 aFlag );
INT32 GetClosestSoldierWithFlag( UINT8 aTeam, UINT32 aFlag );
// Flugente: VIP targets
@@ -426,5 +430,7 @@ void VIPFleesToMeduna();
// Flugente: is any member of a specific civilian faction still alive in the current sector?
BOOLEAN IsCivFactionMemberAliveInSector( UINT8 usCivilianGroup );
BOOLEAN IsFreeSlotAvailable( int aTeam );
#endif
+5
View File
@@ -300,6 +300,11 @@ void ReducePointsForFatigue( SOLDIERTYPE *pSoldier, UINT32 *pusPoints )
*pusPoints -= (*pusPoints * GetPenaltyForFatigue( pSoldier )) / 100;
}
void ReducePointsForFatigue( SOLDIERTYPE *pSoldier, INT32 *psPoints )
{
*psPoints -= ( *psPoints * GetPenaltyForFatigue( pSoldier ) ) / 100;
}
INT32 GetSkillCheckPenaltyForFatigue( SOLDIERTYPE *pSoldier, INT32 iSkill )
{
// use only half the full effect of fatigue for skill checks
+1
View File
@@ -5,6 +5,7 @@
#include "Soldier Control.h"
void ReducePointsForFatigue( SOLDIERTYPE *pSoldier, UINT32 *pusPoints );
void ReducePointsForFatigue( SOLDIERTYPE *pSoldier, INT32 *psPoints );
INT32 GetSkillCheckPenaltyForFatigue( SOLDIERTYPE *pSoldier, INT32 iSkill );
INT32 SkillCheck( SOLDIERTYPE *pSoldier, INT8 bReason, INT8 bDifficulty );
INT16 CalcTrapDetectLevel( SOLDIERTYPE *pSoldier, BOOLEAN fExamining );
+4 -4
View File
@@ -249,7 +249,7 @@ SkillSelection::Setup( UINT32 aVal )
pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction<void, UINT32>( &Wrapper_Function_SkillSelection, uiCounter ) );
// if we cannot perform this skill, grey it out
if ( !( pSoldier->CanUseSkill( uiCounter, TRUE ) ) )
if ( !( pSoldier->CanUseSkill( uiCounter, TRUE, sTraitsMenuTargetGridNo ) ) )
{
// Set this option off.
pOption->setAvail( new popupCallbackFunction<bool, void*>( &Popup_OptionOff, NULL ) );
@@ -309,7 +309,7 @@ SkillSelection::Setup( UINT32 aVal )
{
for(UINT32 uiCounter = SKILLS_RADIO_FIRST; uiCounter <= SKILLS_RADIO_LAST; ++uiCounter)
{
SetRegionFastHelpText( &(GetPopup()->MenuRegion[cnt++]), pSoldier->PrintSkillDesc(uiCounter) );
SetRegionFastHelpText( &(GetPopup()->MenuRegion[cnt++]), pSoldier->PrintSkillDesc( uiCounter, sTraitsMenuTargetGridNo ) );
}
}
break;
@@ -318,7 +318,7 @@ SkillSelection::Setup( UINT32 aVal )
{
for ( UINT32 uiCounter = SKILLS_INTEL_FIRST; uiCounter <= SKILLS_INTEL_LAST; ++uiCounter )
{
SetRegionFastHelpText( &( GetPopup()->MenuRegion[cnt++] ), pSoldier->PrintSkillDesc( uiCounter ) );
SetRegionFastHelpText( &( GetPopup()->MenuRegion[cnt++] ), pSoldier->PrintSkillDesc( uiCounter, sTraitsMenuTargetGridNo ) );
}
}
break;
@@ -327,7 +327,7 @@ SkillSelection::Setup( UINT32 aVal )
{
for(UINT32 uiCounter = SKILLS_VARIOUS_FIRST; uiCounter <= SKILLS_VARIOUS_LAST; ++uiCounter)
{
SetRegionFastHelpText( &(GetPopup()->MenuRegion[cnt++]), pSoldier->PrintSkillDesc(uiCounter) );
SetRegionFastHelpText( &(GetPopup()->MenuRegion[cnt++]), pSoldier->PrintSkillDesc(uiCounter, sTraitsMenuTargetGridNo ) );
}
}
break;
+6
View File
@@ -114,6 +114,8 @@ extern UINT8 gubInterruptProvoker;
extern UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady, BOOLEAN fHipStance );
extern bool RemoveOneTurncoat( INT16 sSectorX, INT16 sSectorY, UINT8 aSoldierClass );
// Animation code explanations!
//
// 0-399: Actual animation frame indices
@@ -4131,6 +4133,10 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse )
{
DeleteVIP( pSoldier->sSectorX, pSoldier->sSectorY );
}
// Flugente: turncoats
if ( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
RemoveOneTurncoat( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->ubSoldierClass );
// Flugente: additional dialogue
if ( pSoldier->ubProfile != NO_PROFILE )
+389 -5
View File
@@ -10168,6 +10168,14 @@ UINT8 SOLDIERTYPE::SoldierTakeDamage( INT8 bHeight, INT16 sLifeDeduct, INT16 sBr
if ( this->ubBodyType != CROW && this->ubBodyType != COW )
HandleDynamicOpinionChange( MercPtrs[ubAttacker], OPINIONEVENT_CIV_ATTACKER, TRUE, TRUE );
}
// if we are a turncoat, lose the flag if we were attacked by player forces
if ( (this->usSoldierFlagMask2 & SOLDIER_TURNCOAT) && MercPtrs[ubAttacker]->bSide == 0 )
{
this->usSoldierFlagMask2 &= ~SOLDIER_TURNCOAT;
RemoveOneTurncoat( this->sSectorX, this->sSectorY, this->ubSoldierClass );
}
}
// CJC Jan 21 99: add check to see if we are hurting an enemy in an enemy-controlled
@@ -16200,7 +16208,7 @@ void SOLDIERTYPE::LooseDisguise( void )
// rehandle sight for everybody
SOLDIERTYPE* pSoldier;
UINT8 iLoop = gTacticalStatus.Team[OUR_TEAM].bFirstID;
for ( pSoldier = MercPtrs[iLoop]; iLoop <= gTacticalStatus.Team[LAST_TEAM].bLastID; ++iLoop, ++pSoldier )
for ( pSoldier = MercPtrs[iLoop]; iLoop <= gTacticalStatus.Team[CIV_TEAM].bLastID; ++iLoop, ++pSoldier )
{
if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 )
{
@@ -17794,6 +17802,16 @@ BOOLEAN SOLDIERTYPE::CanUseSkill( INT8 iSkill, BOOLEAN fAPCheck, INT32 sGridNo )
canuse = TRUE;
break;
case SKILLS_RADIO_ACTIVATE_TURNCOATS_ALL:
if ( ( !fAPCheck || EnoughPoints( this, APBPConstants[AP_RADIO], APBPConstants[BP_RADIO], FALSE ) )
&& CanUseRadio()
&& gSkillTraitValues.fCOTurncoats
&& !gbWorldSectorZ
&& gTacticalStatus.ubInterruptPending == DISABLED_INTERRUPT
&& IsFreeSlotAvailable( MILITIA_TEAM ) )
canuse = TRUE;
break;
case SKILLS_INTEL_CONCEAL:
case SKILLS_INTEL_GATHERINTEL:
// in order to conceal, we need:
@@ -17855,6 +17873,52 @@ BOOLEAN SOLDIERTYPE::CanUseSkill( INT8 iSkill, BOOLEAN fAPCheck, INT32 sGridNo )
}
break;
case SKILLS_CREATE_TURNCOAT:
// in order to try to create a turncoat, we need:
// - a non-profile, not-already-turncoat enemy soldier
// - enemy team not aware of us
// - valid disguise
{
SOLDIERTYPE* pSoldier = SimpleFindSoldier( sGridNo, gsInterfaceLevel );
if ( pSoldier
&& InPositionForTurncoatAttempt( pSoldier->ubID ) )
{
canuse = TRUE;
}
}
break;
case SKILLS_ACTIVATE_TURNCOATS:
// not during an interrupt
if ( gSkillTraitValues.fCOTurncoats
&& !gbWorldSectorZ
&& gTacticalStatus.ubInterruptPending == DISABLED_INTERRUPT
&& IsFreeSlotAvailable( MILITIA_TEAM ) )
{
SOLDIERTYPE* pSoldier = SimpleFindSoldier( sGridNo, gsInterfaceLevel );
if ( pSoldier
&& pSoldier->bTeam == ENEMY_TEAM
&& pSoldier->ubProfile == NO_PROFILE
&& ( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
&& SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass ) )
{
canuse = TRUE;
}
}
break;
case SKILLS_ACTIVATE_TURNCOATS_ALL:
// not during an interrupt
if ( gSkillTraitValues.fCOTurncoats
&& !gbWorldSectorZ
&& gTacticalStatus.ubInterruptPending == DISABLED_INTERRUPT
&& !gSkillTraitValues.fCOTurncoats_SectorActivationRequiresRadioOperator
&& IsFreeSlotAvailable( MILITIA_TEAM ) )
{
canuse = TRUE;
}
break;
case SKILLS_SPOTTER:
if ( (!fAPCheck || EnoughPoints( this, APBPConstants[AP_SPOTTER], 0, FALSE )) && CanSpot( ) )
canuse = TRUE;
@@ -17928,6 +17992,10 @@ BOOLEAN SOLDIERTYPE::UseSkill( UINT8 iSkill, INT32 usMapPos, UINT32 ID )
return SwitchOffRadio( );
break;
case SKILLS_RADIO_ACTIVATE_TURNCOATS_ALL:
return RadioOrderAllTurnCoatToSwitchSides();
break;
case SKILLS_INTEL_CONCEAL:
case SKILLS_INTEL_GATHERINTEL:
{
@@ -17955,6 +18023,18 @@ BOOLEAN SOLDIERTYPE::UseSkill( UINT8 iSkill, INT32 usMapPos, UINT32 ID )
}
break;
case SKILLS_CREATE_TURNCOAT:
AttemptToCreateTurncoat ( ID );
break;
case SKILLS_ACTIVATE_TURNCOATS:
OrderTurnCoatToSwitchSides( ID );
break;
case SKILLS_ACTIVATE_TURNCOATS_ALL:
OrderAllTurnCoatToSwitchSides();
break;
case SKILLS_SPOTTER:
return BecomeSpotter( usMapPos );
break;
@@ -18001,9 +18081,9 @@ BOOLEAN SOLDIERTYPE::IsAIAllowedtoUseSkill( INT8 iSkill )
static CHAR16 skilldescarray[500];
// print a small description of the skill if we can use it, or its requirements if we cannot
STR16 SOLDIERTYPE::PrintSkillDesc( INT8 iSkill )
STR16 SOLDIERTYPE::PrintSkillDesc( INT8 iSkill, INT32 sGridNo )
{
if ( CanUseSkill( iSkill, TRUE ) )
if ( CanUseSkill( iSkill, TRUE, sGridNo ) )
{
return pTraitSkillsMenuDescStrings[iSkill];
}
@@ -18036,6 +18116,25 @@ STR16 SOLDIERTYPE::PrintSkillDesc( INT8 iSkill )
break;
case SKILLS_RADIO_ACTIVATE_TURNCOATS_ALL:
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_TXT], gzMercSkillTextNew[RADIO_OPERATOR_NT] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_TXT], New113Message[MSG113_WORKING_RADIO_SET] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_NOT_DURING_INTERRUPT] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_TURNED_ENEMY] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_SURFACELEVEL] );
wcscat( skilldescarray, atStr );
break;
case SKILLS_INTEL_CONCEAL:
case SKILLS_INTEL_GATHERINTEL:
@@ -18056,6 +18155,39 @@ STR16 SOLDIERTYPE::PrintSkillDesc( INT8 iSkill )
break;
case SKILLS_CREATE_TURNCOAT:
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_ENEMY] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_NOALARM] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_DISGUISE_CIV_OR_MIL] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_SURFACELEVEL] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_STRATEGIC_SUSPICION] );
wcscat( skilldescarray, atStr );
break;
case SKILLS_ACTIVATE_TURNCOATS:
case SKILLS_ACTIVATE_TURNCOATS_ALL:
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_NOT_DURING_INTERRUPT] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_TURNED_ENEMY] );
wcscat( skilldescarray, atStr );
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_SURFACELEVEL] );
wcscat( skilldescarray, atStr );
break;
case SKILLS_SPOTTER:
swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_AP], APBPConstants[AP_SPOTTER] );
wcscat( skilldescarray, atStr );
@@ -18836,6 +18968,21 @@ BOOLEAN SOLDIERTYPE::SwitchOffRadio( )
return TRUE;
}
BOOLEAN SOLDIERTYPE::RadioOrderAllTurnCoatToSwitchSides()
{
// not possible if already scanning
if ( !gSkillTraitValues.fCOTurncoats )
return FALSE;
// use the radio, this handles animation, batteries etc.
if ( !UseRadio() )
return FALSE;
OrderAllTurnCoatToSwitchSides();
return TRUE;
}
// display and error sound used either when the radio set fails or the sector is jammed - the player knows of the error, but cannot be sure of the cause
void
SOLDIERTYPE::RadioFail( )
@@ -20233,7 +20380,7 @@ UINT8 SOLDIERTYPE::GetUncoverRisk()
// if we do this disguised as a soldier, risk will be much higer, as we are under much more scrutiny. This makes up for the increased gain in soldier disguise
// less risk if we are asleep, just hiding or forced to hide
UINT8 typemultiplier = ( this->usSoldierFlagMask & SOLDIER_COVERT_SOLDIER ) ? 5 : 2;
if ( ( this->bAssignment == CONCEALED ) || this->flags.fMercAsleep || this->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] )
if ( ( this->bAssignment == CONCEALED ) || this->flags.fMercAsleep || this->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] > 10 )
typemultiplier = 1;
// we now take the sector coolness as a measurement of how important the sector is, and thus how intel we gain
@@ -20259,7 +20406,7 @@ FLOAT SOLDIERTYPE::GetIntelGain()
return 0.0f;
// if we're asleep, or on a penalty, we accomplish nothing
if ( this->flags.fMercAsleep || this->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] )
if ( this->flags.fMercAsleep || this->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] > 10 )
return 0.0f;
// the covert trait isn't that important in determining the intel gain. It is much more important in mitigating the risk of exposure, however
@@ -20624,6 +20771,243 @@ UINT8 SOLDIERTYPE::GetThiefEvadeDetectionChance()
return totalvalue;
}
BOOLEAN SOLDIERTYPE::InPositionForTurncoatAttempt( UINT16 usID )
{
if ( !gSkillTraitValues.fCOTurncoats
|| gbWorldSectorZ
|| gTacticalStatus.Team[ENEMY_TEAM].bAwareOfOpposition )
return FALSE;
if ( this->stats.bLife < OKLIFE
|| this->flags.fMercAsleep
|| this->bCollapsed
|| ( this->usSoldierFlagMask & SOLDIER_POW )
|| this->usSkillCooldown[SOLDIER_COOLDOWN_INTEL_PENALTY] > 20
|| usID == NOBODY )
return FALSE;
SOLDIERTYPE* pSoldier = MercPtrs[usID];
if ( !pSoldier
|| pSoldier->bTeam != ENEMY_TEAM
|| pSoldier->ubProfile != NO_PROFILE
|| pSoldier->stats.bLife != pSoldier->stats.bLifeMax
|| pSoldier->bCollapsed
|| ( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
|| !SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass )
|| !SeemsLegit( pSoldier->ubID ) )
return FALSE;
// additional checks if we want to know wether we can target a specific location
if ( PythSpacesAway( this->sGridNo, pSoldier->sGridNo ) < 10 )
{
INT32 val = SoldierToVirtualSoldierLineOfSightTest( this, pSoldier->sGridNo, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, FALSE, 10 );
// error if we cannot see the target
if ( !val )
return FALSE;
else
return TRUE;
}
return FALSE;
}
UINT8 SOLDIERTYPE::GetTurncoatConvinctionChance( UINT16 usID, UINT8 usApproach )
{
if ( usID >= NOBODY )
return 0;
SOLDIERTYPE* pSoldier = MercPtrs[usID];
if ( !pSoldier
|| pSoldier->bTeam != ENEMY_TEAM )
return 0;
if ( this->stats.bLife < OKLIFE )
return 0;
// determine effectiveness of merc
// nominally in [0; 1000]
INT32 basestatrating = 6 * EffectiveLeadership( this ) + 40 * EffectiveExpLevel( this, FALSE );
FLOAT recruitmodifier = ( 100 + this->GetBackgroundValue( BG_PERC_APPROACH_RECRUIT ) ) / 100.0f;
// personality/disability modifiers
FLOAT persmodifier = 1.0f;
if ( DoesMercHaveDisability( this, NERVOUS ) ) persmodifier -= 0.10f;
if ( gGameOptions.fNewTraitSystem )
{
if ( DoesMercHavePersonality( this, CHAR_TRAIT_SOCIABLE ) ) persmodifier += 0.08f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_LONER ) ) persmodifier -= 0.04f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_ASSERTIVE ) ) persmodifier += 0.05f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_AGGRESSIVE ) ) persmodifier -= 0.05f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_PHLEGMATIC ) ) persmodifier -= 0.02f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_DAUNTLESS ) ) persmodifier += 0.03f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_SHOWOFF ) ) persmodifier += 0.04f;
if ( DoesMercHavePersonality( this, CHAR_TRAIT_COWARD ) ) persmodifier -= 0.07f;
}
// nominally in [0; 100]
INT32 recruitrating = basestatrating * recruitmodifier * persmodifier * gMercProfiles[this->ubProfile].usApproachFactor[3] / 1000;
// optional ini bonus
recruitrating += gSkillTraitValues.sCOTurncoats_PlayerConvinctionBonus;
ReducePointsForFatigue( this, &recruitrating );
// determine resistance of soldier to our subversion
INT32 ubLocationModifier = 2 * max( 1, min( 20, gCoolnessBySector[SECTOR( this->sSectorX, this->sSectorY )] ) );
// enemy resistance is dependent on their level, class and the sector rating
INT32 enemyresistancerating = ubLocationModifier + 8 * EffectiveExpLevel( pSoldier, FALSE );
switch ( pSoldier->ubSoldierClass )
{
case SOLDIER_CLASS_ADMINISTRATOR: enemyresistancerating -= 30; break;
case SOLDIER_CLASS_ELITE: enemyresistancerating += 30; break;
default: break;
}
if ( pSoldier->usSoldierFlagMask & SOLDIER_ENEMY_OFFICER )
enemyresistancerating += 30;
ReducePointsForFatigue( pSoldier, &enemyresistancerating );
switch ( usApproach )
{
// base approach
case 1:
break;
// we use our looks for seduction
// thus looking attractive lowers enemy resistance, while being ugly can increase it
// however this fails if the soldier is not attracted to us
case 2:
{
// determine whether the soldier is attracted to us in the first place (don't display this, otherwise people will want to set sexual orientation and whatnot)
INT32 stat_dependant_roll = ( 37 * EffectiveStrength( pSoldier, FALSE ) + 92 * EffectiveMedical( pSoldier ) + 51 * EffectiveDexterity( pSoldier, FALSE ) + 61 * pSoldier->stats.bLife ) % 100;
bool samesexattraction = ( stat_dependant_roll < 8 );
bool female_player = ( this->ubBodyType == REGFEMALE );
bool female_soldier = ( pSoldier->ubBodyType == REGFEMALE );
bool fittingattraction = false;
if ( female_player != female_soldier && !samesexattraction )
fittingattraction = true;
else if ( female_player == female_soldier && samesexattraction )
fittingattraction = true;
if ( gMercProfiles[this->ubProfile].bAppearance == APPEARANCE_UGLY ) enemyresistancerating += 50 - ( fittingattraction ? 5 : 0 );
else if ( gMercProfiles[this->ubProfile].bAppearance == APPEARANCE_HOMELY ) enemyresistancerating += 40 - ( fittingattraction ? 15 : 0 );
else if ( gMercProfiles[this->ubProfile].bAppearance == APPEARANCE_AVERAGE ) enemyresistancerating += 30 - ( fittingattraction ? 30 : 0 );
else if ( gMercProfiles[this->ubProfile].bAppearance == APPEARANCE_ATTRACTIVE ) enemyresistancerating += 20 - ( fittingattraction ? 45 : 0 );
else if ( gMercProfiles[this->ubProfile].bAppearance == APPEARANCE_BABE ) enemyresistancerating += 10 - ( fittingattraction ? 60 : 0 );
// seduction works better in civilian clothing
if ( this->usSoldierFlagMask & SOLDIER_COVERT_CIV )
enemyresistancerating -= 5;
}
break;
// we try to bribe the soldier with money
case 3:
{
// the amount of money depends on progress and unimportant in this case
// the worse the location, the poorer the soldier, thus the more effective money is
enemyresistancerating -= 30 + (40 - ubLocationModifier);
}
break;
// we try to bribe the soldier with intel
case 4:
{
// the amount of intel depends on progress and unimportant in this case
enemyresistancerating -= 80;
}
break;
default:
break;
}
if ( enemyresistancerating > recruitrating )
return 0;
return max( 0, min( 100, recruitrating - enemyresistancerating ) );
}
void SOLDIERTYPE::AttemptToCreateTurncoat( UINT16 usID )
{
if ( usID >= NOBODY )
return;
SOLDIERTYPE* pSoldier = MercPtrs[usID];
if ( !pSoldier
|| pSoldier->bTeam != ENEMY_TEAM
|| ( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT ) )
return;
HandleTurncoatAttempt( pSoldier );
}
BOOLEAN SOLDIERTYPE::OrderTurnCoatToSwitchSides( UINT16 usID )
{
if ( usID >= NOBODY )
return FALSE;
SOLDIERTYPE* pSoldier = MercPtrs[usID];
if (!pSoldier
|| pSoldier->bTeam != ENEMY_TEAM
|| !( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT ) )
return FALSE;
if ( IsFreeSlotAvailable( MILITIA_TEAM ) )
{
// remove turncoat property
pSoldier->usSoldierFlagMask2 &= ~SOLDIER_TURNCOAT;
RemoveOneTurncoat( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->ubSoldierClass );
MakeCivHostile( pSoldier );
return TRUE;
}
return FALSE;
}
void SOLDIERTYPE::OrderAllTurnCoatToSwitchSides()
{
SOLDIERTYPE *pSoldier;
INT32 cnt = gTacticalStatus.Team[ENEMY_TEAM].bFirstID;
// run through list
for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[ENEMY_TEAM].bLastID; ++cnt, ++pSoldier )
{
if ( pSoldier->bActive && pSoldier->bInSector )
{
if ( pSoldier->usSoldierFlagMask2 & SOLDIER_TURNCOAT )
{
if ( IsFreeSlotAvailable( MILITIA_TEAM ) )
{
// remove turncoat property
pSoldier->usSoldierFlagMask2 &= ~SOLDIER_TURNCOAT;
RemoveOneTurncoat( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->ubSoldierClass );
MakeCivHostile( pSoldier );
}
else
{
return;
}
}
}
}
}
INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
{
INT8 bBandaged; //,savedOurTurn;
+16 -3
View File
@@ -426,6 +426,7 @@ enum
#define SOLDIER_DRAG_SOUND 0x00080000 // played sound when started dragging
#define SOLDIER_SPENT_AP 0x00100000 // soldier has spent some AP this turn (including realtime)
#define SOLDIER_TURNCOAT 0x00200000 // this enemy soldier will switch to the militia team if ordered to
#define SOLDIER_INTERROGATE_ALL 0x000001F8 // all interrogation flags
// ----------------------------------------------------------------
@@ -597,13 +598,17 @@ enum{
SKILLS_RADIO_LISTEN,
SKILLS_RADIO_CALLREINFORCEMENTS,
SKILLS_RADIO_TURNOFF,
SKILLS_RADIO_LAST = SKILLS_RADIO_TURNOFF,
SKILLS_RADIO_ACTIVATE_TURNCOATS_ALL, // order all enemy turncoats to turn into militia right now (in case this requires a radio)
SKILLS_RADIO_LAST = SKILLS_RADIO_ACTIVATE_TURNCOATS_ALL,
// spy
SKILLS_INTEL_FIRST,
SKILLS_INTEL_CONCEAL = SKILLS_INTEL_FIRST, // assignment: spy hides among the population
SKILLS_INTEL_GATHERINTEL, // assignment: spy gathers information while disguised
SKILLS_INTEL_LAST = SKILLS_INTEL_GATHERINTEL,
SKILLS_CREATE_TURNCOAT,
SKILLS_ACTIVATE_TURNCOATS, // order enemy turncoat to turn into militia right now
SKILLS_ACTIVATE_TURNCOATS_ALL, // order all enemy turncoats to turn into militia right now
SKILLS_INTEL_LAST = SKILLS_ACTIVATE_TURNCOATS_ALL,
// various
SKILLS_VARIOUS_FIRST,
@@ -1915,7 +1920,7 @@ public:
BOOLEAN IsAIAllowedtoUseSkill( INT8 iSkill );
// print a small description of the skill if we can use it, or its requirements if we cannot
STR16 PrintSkillDesc( INT8 iSkill);
STR16 PrintSkillDesc( INT8 iSkill, INT32 sGridNo = -1 );
// Flugente: functions for the radio operator trait
BOOLEAN CanUseRadio(BOOLEAN fCheckForAP = TRUE); // can we use radio, if we even have one?
@@ -1932,6 +1937,7 @@ public:
BOOLEAN RadioListen();
BOOLEAN RadioCallReinforcements( UINT32 usSector, UINT16 sNumber );
BOOLEAN SwitchOffRadio();
BOOLEAN RadioOrderAllTurnCoatToSwitchSides();
void RadioFail(); // display and error sound used either when the radio set fails or the sector is jammed - the player knows of the error, but cannot be sure of the cause
// Flugente: spotter
@@ -2025,6 +2031,13 @@ public:
// Flugente: those with the <scrounging> background occasionally steal money from the locals
UINT8 GetThiefStealMoneyChance();
UINT8 GetThiefEvadeDetectionChance();
// Flugente: turncoats
BOOLEAN InPositionForTurncoatAttempt( UINT16 usID );
UINT8 GetTurncoatConvinctionChance( UINT16 usID, UINT8 usApproach );
void AttemptToCreateTurncoat( UINT16 usID );
BOOLEAN OrderTurnCoatToSwitchSides( UINT16 usID );
void OrderAllTurnCoatToSwitchSides();
//////////////////////////////////////////////////////////////////////////////
}; // SOLDIERTYPE;
+9
View File
@@ -1864,6 +1864,15 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT
}
}
}
// Flugente: turncoats
if ( gSkillTraitValues.fCOTurncoats && pCreateStruct->bTeam == ENEMY_TEAM && SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass ) )
{
if ( NumSoldiersofClassWithFlag2InSector( pCreateStruct->bTeam, pSoldier->ubSoldierClass, SOLDIER_TURNCOAT ) < NumTurncoatsOfClassInSector( gWorldSectorX, gWorldSectorY, pSoldier->ubSoldierClass ) )
{
pSoldier->usSoldierFlagMask2 |= SOLDIER_TURNCOAT;
}
}
// SANDRO - If neither of these two options are activated, use the original code
// however, "no traits" means no traits at all, so commented out
+5
View File
@@ -1587,6 +1587,11 @@ SOLDIERTYPE *ChangeSoldierTeam( SOLDIERTYPE *pSoldier, UINT8 ubTeam )
MercCreateStruct.fPlayerMerc = TRUE;
}
if ( ubTeam == MILITIA_TEAM )
{
MercCreateStruct.ubSoldierClass = pSoldier->ubSoldierClass;
}
if ( TacticalCreateSoldier( &MercCreateStruct, &ubID ) )
{
pNewSoldier = MercPtrs[ ubID ];