mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Every midnight snitches will give you a report about their collegues' opinions. Information includes: - merc whining about high death rate, - merc whining about player reputation, - merc whining about his low morale, - merc whining about some other merc (unliked, not only hated), - merc planning to quit (leave without renewing contract, only AIM mercs) next day, - merc whining about player inactivity, - merc whining about player owing them money (you can only owe money to MERC mercs). List of snitches (trait 22) Beth Bud Buzz Carp Ears Gary Hurl Lance Leon Weasel see: http://www.bears-pit.com/board/ubbthreads.php/topics/329412/Snitches_+_Externalised_morale.html git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6723 3b4a5df2-a311-0410-b5c6-a8a6f20db521
450 lines
13 KiB
C++
450 lines
13 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Strategic All.h"
|
|
#else
|
|
#include "Strategic Status.h"
|
|
#include "Inventory Choosing.h"
|
|
#include "FileMan.h"
|
|
#include "Soldier Profile.h"
|
|
#include "Campaign.h"
|
|
#include "Strategic Mines.h"
|
|
#include "email.h"
|
|
#include "Game Clock.h"
|
|
#include "Debug.h"
|
|
#include "GameSettings.h"
|
|
#include "history.h"
|
|
#include "Strategic Town Loyalty.h"
|
|
#endif
|
|
|
|
|
|
STRATEGIC_STATUS gStrategicStatus;
|
|
|
|
void InitStrategicStatus(void)
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitStrategicStatus: InitArmyGunTypes");
|
|
memset( &gStrategicStatus, 0, sizeof( STRATEGIC_STATUS ) );
|
|
//Add special non-zero start conditions here...
|
|
|
|
InitArmyGunTypes();
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitStrategicStatus: InitArmyGunTypes Done");
|
|
}
|
|
|
|
BOOLEAN SaveStrategicStatusToSaveGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesWritten;
|
|
|
|
//Save the Strategic Status structure to the saved game file
|
|
FileWrite( hFile, &gStrategicStatus, sizeof( STRATEGIC_STATUS ), &uiNumBytesWritten );
|
|
if( uiNumBytesWritten != sizeof( STRATEGIC_STATUS ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN LoadStrategicStatusFromSaveGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesRead;
|
|
|
|
//Load the Strategic Status structure from the saved game file
|
|
FileRead( hFile, &gStrategicStatus, sizeof( STRATEGIC_STATUS ), &uiNumBytesRead );
|
|
if( uiNumBytesRead != sizeof( STRATEGIC_STATUS ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
#define DEATH_RATE_SEVERITY 1.0f // increase to make death rates higher for same # of deaths/time
|
|
|
|
UINT8 CalcDeathRate(void)
|
|
{
|
|
UINT32 uiDeathRate = 0;
|
|
|
|
// give the player a grace period of 1 day
|
|
if (gStrategicStatus.uiManDaysPlayed > 0)
|
|
{
|
|
// calculates the player's current death rate
|
|
uiDeathRate = (UINT32) ((gStrategicStatus.ubMercDeaths * DEATH_RATE_SEVERITY * 100) / gStrategicStatus.uiManDaysPlayed);
|
|
}
|
|
|
|
return((UINT8) uiDeathRate);
|
|
}
|
|
|
|
|
|
void ModifyPlayerReputation(INT8 bRepChange)
|
|
{
|
|
INT32 iNewBadRep;
|
|
INT8 bRepChangeValue = gReputationSettings.bValues[bRepChange];
|
|
// subtract, so that a negative reputation change results in an increase in bad reputation
|
|
iNewBadRep = (INT32) gStrategicStatus.ubBadReputation - bRepChangeValue;
|
|
|
|
// keep within a 0-100 range (0 = Saint, 100 = Satan)
|
|
iNewBadRep = __max( 0, iNewBadRep );
|
|
iNewBadRep = __min( 100, iNewBadRep );
|
|
|
|
gStrategicStatus.ubBadReputation = (UINT8) iNewBadRep;
|
|
}
|
|
|
|
|
|
BOOLEAN MercThinksDeathRateTooHigh( UINT8 ubProfileID )
|
|
{
|
|
INT8 bDeathRateTolerance;
|
|
|
|
bDeathRateTolerance = gMercProfiles[ ubProfileID ].bDeathRate;
|
|
|
|
// if he couldn't care less what it is
|
|
if (bDeathRateTolerance == 101)
|
|
{
|
|
// then obviously it CAN'T be too high...
|
|
return(FALSE);
|
|
}
|
|
|
|
if (CalcDeathRate() > bDeathRateTolerance)
|
|
{
|
|
// too high - sorry
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
// within tolerance
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN MercThinksBadReputationTooHigh( UINT8 ubProfileID )
|
|
{
|
|
INT8 bRepTolerance;
|
|
|
|
bRepTolerance = gMercProfiles[ ubProfileID ].bReputationTolerance;
|
|
|
|
// if he couldn't care less what it is
|
|
if (bRepTolerance == 101)
|
|
{
|
|
// then obviously it CAN'T be too high...
|
|
return(FALSE);
|
|
}
|
|
|
|
if (gStrategicStatus.ubBadReputation > bRepTolerance)
|
|
{
|
|
// too high - sorry
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
// within tolerance
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
// only meaningful for already hired mercs
|
|
BOOLEAN MercThinksHisMoraleIsTooLow( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bRepTolerance;
|
|
INT8 bMoraleTolerance;
|
|
|
|
bRepTolerance = gMercProfiles[ pSoldier->ubProfile ].bReputationTolerance;
|
|
|
|
// if he couldn't care less what it is
|
|
if (bRepTolerance == 101)
|
|
{
|
|
// that obviously it CAN'T be too low...
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
// morale tolerance is based directly upon reputation tolerance
|
|
// above 50, morale is GOOD, never below tolerance then
|
|
bMoraleTolerance = (100 - bRepTolerance) / 2;
|
|
|
|
if (pSoldier->aiData.bMorale < bMoraleTolerance)
|
|
{
|
|
// too low - sorry
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
// within tolerance
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOLEAN MercIsOwedTooMuchMoney( UINT8 ubProfileID )
|
|
{
|
|
if(gMercProfiles[ ubProfileID ].iBalance >= gMercProfiles[ ubProfileID ].sSalary * gMoraleSettings.bValues[MORALE_OWED_MONEY_DAYS])
|
|
{
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOLEAN MercThinksPlayerIsInactiveTooLong( UINT8 ubProfileID )
|
|
{
|
|
UINT8 ubTolerance;
|
|
ubTolerance = gMoraleSettings.bValues[MORALE_PLAYER_INACTIVE_DAYS];
|
|
if( gMercProfiles[ubProfileID].bCharacterTrait == CHAR_TRAIT_PACIFIST )
|
|
{
|
|
ubTolerance = max(0, ubTolerance + gMoraleSettings.bValues[MORALE_PLAYER_INACTIVE_DAYS_PACIFIST_BONUS]);
|
|
}
|
|
else if( gMercProfiles[ubProfileID].bCharacterTrait == CHAR_TRAIT_AGGRESSIVE )
|
|
{
|
|
ubTolerance = max(0, ubTolerance + gMoraleSettings.bValues[MORALE_PLAYER_INACTIVE_DAYS_AGGRESSIVE_BONUS]);
|
|
}
|
|
if( ubTolerance == 0)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
if( gStrategicStatus.ubNumberOfDaysOfInactivity >= ubTolerance )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
void UpdateLastDayOfPlayerActivity( UINT16 usDay )
|
|
{
|
|
if ( usDay > gStrategicStatus.usLastDayOfPlayerActivity )
|
|
{
|
|
gStrategicStatus.usLastDayOfPlayerActivity = usDay;
|
|
gStrategicStatus.ubNumberOfDaysOfInactivity = 0;
|
|
}
|
|
}
|
|
|
|
UINT8 LackOfProgressTolerance( void )
|
|
{
|
|
if ( gGameOptions.ubDifficultyLevel >= DIF_LEVEL_HARD )
|
|
{
|
|
// give an EXTRA day over normal
|
|
return( 7 - DIF_LEVEL_MEDIUM + gStrategicStatus.ubHighestProgress / 42 );
|
|
}
|
|
else
|
|
{
|
|
return( 6 - gGameOptions.ubDifficultyLevel + gStrategicStatus.ubHighestProgress / 42 );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// called once per day in the morning, decides whether Enrico should send any new E-mails to the player
|
|
void HandleEnricoEmail(void)
|
|
{
|
|
UINT8 ubCurrentProgress = CurrentPlayerProgressPercentage();
|
|
UINT8 ubHighestProgress = HighestPlayerProgressPercentage();
|
|
|
|
#ifdef JA2UB
|
|
//JA25 UB
|
|
#else
|
|
// if creatures have attacked a mine (doesn't care if they're still there or not at the moment)
|
|
if (HasAnyMineBeenAttackedByMonsters() && !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_CREATURES))
|
|
{
|
|
AddEmail(ENRICO_CREATURES, ENRICO_CREATURES_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_CREATURES;
|
|
return; // avoid any other E-mail at the same time
|
|
}
|
|
|
|
|
|
if ((ubCurrentProgress >= SOME_PROGRESS_THRESHOLD) && !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_SOME_PROGRESS))
|
|
{
|
|
AddEmail(ENRICO_PROG_20, ENRICO_PROG_20_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_SOME_PROGRESS;
|
|
return; // avoid any setback E-mail at the same time
|
|
}
|
|
|
|
if ((ubCurrentProgress >= ABOUT_HALFWAY_THRESHOLD) && !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_ABOUT_HALFWAY))
|
|
{
|
|
AddEmail(ENRICO_PROG_55, ENRICO_PROG_55_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_ABOUT_HALFWAY;
|
|
return; // avoid any setback E-mail at the same time
|
|
}
|
|
|
|
if ((ubCurrentProgress >= NEARLY_DONE_THRESHOLD) && !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_NEARLY_DONE))
|
|
{
|
|
AddEmail(ENRICO_PROG_80, ENRICO_PROG_80_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_NEARLY_DONE;
|
|
return; // avoid any setback E-mail at the same time
|
|
}
|
|
|
|
// test for a major setback OR a second minor setback
|
|
if ((((ubHighestProgress - ubCurrentProgress) >= MAJOR_SETBACK_THRESHOLD) ||
|
|
(((ubHighestProgress - ubCurrentProgress) >= MINOR_SETBACK_THRESHOLD) && (gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_FLAG_SETBACK_OVER))) &&
|
|
!(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_MAJOR_SETBACK))
|
|
{
|
|
AddEmail(ENRICO_SETBACK, ENRICO_SETBACK_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_MAJOR_SETBACK;
|
|
}
|
|
else
|
|
// test for a first minor setback
|
|
if (((ubHighestProgress - ubCurrentProgress) >= MINOR_SETBACK_THRESHOLD) &&
|
|
!(gStrategicStatus.usEnricoEmailFlags & (ENRICO_EMAIL_SENT_MINOR_SETBACK | ENRICO_EMAIL_SENT_MAJOR_SETBACK)))
|
|
{
|
|
AddEmail(ENRICO_SETBACK_2, ENRICO_SETBACK_2_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_MINOR_SETBACK;
|
|
}
|
|
else
|
|
// if player is back at his maximum progress after having suffered a minor setback
|
|
if ((ubHighestProgress == ubCurrentProgress) && (gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_MINOR_SETBACK))
|
|
{
|
|
// remember that the original setback has been overcome, so another one can generate another E-mail
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_FLAG_SETBACK_OVER;
|
|
}
|
|
else if ( GetWorldDay() > (UINT32) (gStrategicStatus.usLastDayOfPlayerActivity) )
|
|
{
|
|
INT8 bComplaint = 0;
|
|
UINT8 ubTolerance;
|
|
|
|
gStrategicStatus.ubNumberOfDaysOfInactivity++;
|
|
ubTolerance = LackOfProgressTolerance();
|
|
|
|
if ( gStrategicStatus.ubNumberOfDaysOfInactivity >= ubTolerance )
|
|
{
|
|
if ( gStrategicStatus.ubNumberOfDaysOfInactivity == ubTolerance )
|
|
{
|
|
// send email
|
|
if ( !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS1 ) )
|
|
{
|
|
bComplaint = 1;
|
|
}
|
|
else if ( !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS2 ) )
|
|
{
|
|
bComplaint = 2;
|
|
}
|
|
else if ( !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS3 ) )
|
|
{
|
|
bComplaint = 3;
|
|
}
|
|
}
|
|
else if ( gStrategicStatus.ubNumberOfDaysOfInactivity == (UINT32) ubTolerance * 2 )
|
|
{
|
|
// six days? send 2nd or 3rd message possibly
|
|
if ( !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS2 ) )
|
|
{
|
|
bComplaint = 2;
|
|
}
|
|
else if ( !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS3 ) )
|
|
{
|
|
bComplaint = 3;
|
|
}
|
|
|
|
}
|
|
else if ( gStrategicStatus.ubNumberOfDaysOfInactivity == ubTolerance * 3 )
|
|
{
|
|
// nine days??? send 3rd message possibly
|
|
if ( !(gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS3 ) )
|
|
{
|
|
bComplaint = 3;
|
|
}
|
|
}
|
|
|
|
if ( bComplaint != 0 )
|
|
{
|
|
switch( bComplaint )
|
|
{
|
|
case 3:
|
|
AddEmail(LACK_PLAYER_PROGRESS_3, LACK_PLAYER_PROGRESS_3_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_LACK_PROGRESS3;
|
|
break;
|
|
case 2:
|
|
AddEmail(LACK_PLAYER_PROGRESS_2, LACK_PLAYER_PROGRESS_2_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_LACK_PROGRESS2;
|
|
break;
|
|
default:
|
|
AddEmail(LACK_PLAYER_PROGRESS_1, LACK_PLAYER_PROGRESS_1_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1, -1, TYPE_EMAIL_EMAIL_EDT);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_LACK_PROGRESS1;
|
|
break;
|
|
|
|
}
|
|
|
|
AddHistoryToPlayersLog( HISTORY_ENRICO_COMPLAINED, 0, GetWorldTotalMin(), -1, -1 );
|
|
}
|
|
|
|
// penalize loyalty!
|
|
if ( gStrategicStatus.usEnricoEmailFlags & ENRICO_EMAIL_SENT_LACK_PROGRESS2 )
|
|
{
|
|
DecrementTownLoyaltyEverywhere( LOYALTY_PENALTY_INACTIVE * (gStrategicStatus.ubNumberOfDaysOfInactivity - LackOfProgressTolerance() + 1) );
|
|
}
|
|
else
|
|
{
|
|
// on first complaint, give a day's grace...
|
|
DecrementTownLoyaltyEverywhere( LOYALTY_PENALTY_INACTIVE * (gStrategicStatus.ubNumberOfDaysOfInactivity - LackOfProgressTolerance() ) );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// reset # of new sectors visited 'today'
|
|
// grant some leeway for the next day, could have started moving
|
|
// at night...
|
|
gStrategicStatus.ubNumNewSectorsVisitedToday = __min( gStrategicStatus.ubNumNewSectorsVisitedToday, NEW_SECTORS_EQUAL_TO_ACTIVITY ) / 3;
|
|
}
|
|
|
|
|
|
void TrackEnemiesKilled( UINT8 ubKilledHow, UINT8 ubSoldierClass )
|
|
{
|
|
INT8 bRankIndex;
|
|
|
|
bRankIndex = SoldierClassToRankIndex( ubSoldierClass );
|
|
|
|
// if it's not a standard enemy-class soldier
|
|
if ( bRankIndex == -1 )
|
|
{
|
|
// don't count him as an enemy
|
|
return;
|
|
}
|
|
|
|
gStrategicStatus.usEnemiesKilled[ ubKilledHow ][ bRankIndex ]++;
|
|
|
|
if ( ubKilledHow != ENEMY_KILLED_TOTAL )
|
|
{
|
|
gStrategicStatus.usEnemiesKilled[ ENEMY_KILLED_TOTAL ][ bRankIndex ]++;
|
|
}
|
|
}
|
|
|
|
|
|
INT8 SoldierClassToRankIndex( UINT8 ubSoldierClass )
|
|
{
|
|
INT8 bRankIndex = -1;
|
|
|
|
// the soldier class defines are not in natural ascending order, elite comes before army!
|
|
switch ( ubSoldierClass )
|
|
{
|
|
case SOLDIER_CLASS_ADMINISTRATOR: bRankIndex = 0; break;
|
|
case SOLDIER_CLASS_ELITE: bRankIndex = 2; break;
|
|
case SOLDIER_CLASS_ARMY: bRankIndex = 1; break;
|
|
|
|
default:
|
|
// this happens when an NPC joins the enemy team (e.g. Conrad, Iggy, Mike)
|
|
break;
|
|
}
|
|
|
|
return( bRankIndex );
|
|
}
|
|
|
|
|
|
|
|
UINT8 RankIndexToSoldierClass( UINT8 ubRankIndex )
|
|
{
|
|
UINT8 ubSoldierClass = 0;
|
|
|
|
Assert( ubRankIndex < NUM_ENEMY_RANKS );
|
|
|
|
switch ( ubRankIndex )
|
|
{
|
|
case 0: ubSoldierClass = SOLDIER_CLASS_ADMINISTRATOR; break;
|
|
case 1: ubSoldierClass = SOLDIER_CLASS_ARMY; break;
|
|
case 2: ubSoldierClass = SOLDIER_CLASS_ELITE; break;
|
|
}
|
|
|
|
return( ubSoldierClass );
|
|
}
|