Files
source/Tactical/Morale.cpp
T
Flugente 596b08ca70 - renamed AC_SUPPRESSOR to AC_MUZZLE
- mercs with average refinement get a morale penalty if they care extremely about refinement and the other merc is a slob or snob
- added code for soldier-specific interrupt modifier
- added belgian and portoguese nationalities
- Fix: race, sexism etc. xml data didn't correctly overwrite PROF.DAT data

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6519 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2013-10-21 17:57:39 +00:00

1453 lines
49 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include <stdlib.h>
#include "Morale.h"
#include "Overhead.h"
#include "Soldier Profile.h"
#include "dialogue control.h"
#include "Map Screen Interface.h"
#include "message.h"
#include "assignments.h"
#include "Strategic Movement.h"
#include "Strategic Status.h"
#include "SkillCheck.h"
#include "drugs and alcohol.h"
#include "StrategicMap.h"
#include "Debug.h"
#include "Squads.h"
#include "ai.h"
#include "Campaign.h"
#include "mapscreen.h"
#include "Soldier macros.h"
#include "Event Pump.h"
// HEADROCK HAM 3.5: Added for facility effect on morale
#include "Facilities.h"
// addedd by SANDRO
#include "GameSettings.h"
#include "Isometric Utils.h"
#include "Food.h"
#endif
#include "connect.h"
#include "fresh_header.h"
#define MORALE_MOD_MAX 50 // morale *mod* range is -50 to 50, if you change this, check the decay formulas!
#define DRUG_EFFECT_MORALE_MOD 150
#define ALCOHOL_EFFECT_MORALE_MOD 160
#define HOURS_BETWEEN_STRATEGIC_DECAY 3
#define PHOBIC_LIMIT -20
// macros
#define SOLDIER_IN_SECTOR( pSoldier, sX, sY, bZ ) ( !pSoldier->flags.fBetweenSectors && ( pSoldier->sSectorX == sX ) && ( pSoldier->sSectorY == sY ) && ( pSoldier->bSectorZ == bZ ) )
MoraleEvent gbMoraleEvent[NUM_MORALE_EVENTS] =
{
// TACTICAL = Short Term Effect, STRATEGIC = Long Term Effect
{ TACTICAL_MORALE_EVENT, +4}, // MORALE_KILLED_ENEMY
{ TACTICAL_MORALE_EVENT, -5}, // MORALE_SQUADMATE_DIED, // in same sector (not really squad)... IN ADDITION to strategic loss of morale
{ TACTICAL_MORALE_EVENT, -1}, // MORALE_SUPPRESSED, // up to 4 times per turn
{ TACTICAL_MORALE_EVENT, -2}, // MORALE_AIRSTRIKE,
{ TACTICAL_MORALE_EVENT, +2}, // MORALE_DID_LOTS_OF_DAMAGE,
{ TACTICAL_MORALE_EVENT, -3}, // MORALE_TOOK_LOTS_OF_DAMAGE,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_KILLED_CIVILIAN,
{ STRATEGIC_MORALE_EVENT, +4}, // MORALE_BATTLE_WON,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_RAN_AWAY,
{ STRATEGIC_MORALE_EVENT, +2}, // MORALE_HEARD_BATTLE_WON,
{ STRATEGIC_MORALE_EVENT, -2}, // MORALE_HEARD_BATTLE_LOST,
{ STRATEGIC_MORALE_EVENT, +5}, // MORALE_TOWN_LIBERATED,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_TOWN_LOST,
{ STRATEGIC_MORALE_EVENT, +8}, // MORALE_MINE_LIBERATED,
{ STRATEGIC_MORALE_EVENT, -8}, // MORALE_MINE_LOST,
{ STRATEGIC_MORALE_EVENT, +3}, // MORALE_SAM_SITE_LIBERATED,
{ STRATEGIC_MORALE_EVENT, -3}, // MORALE_SAM_SITE_LOST,
{ STRATEGIC_MORALE_EVENT, -15}, // MORALE_BUDDY_DIED,
{ STRATEGIC_MORALE_EVENT, +5}, // MORALE_HATED_DIED,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_TEAMMATE_DIED, // not in same sector
{ STRATEGIC_MORALE_EVENT, +5}, // MORALE_LOW_DEATHRATE,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_HIGH_DEATHRATE,
{ STRATEGIC_MORALE_EVENT, +2}, // MORALE_GREAT_MORALE,
{ STRATEGIC_MORALE_EVENT, -2}, // MORALE_POOR_MORALE,
{ TACTICAL_MORALE_EVENT, -5}, // MORALE_DRUGS_CRASH // Flugente: lowered as drugs can have several components that have a backlash, causing the the morale to plummet drastical
{ TACTICAL_MORALE_EVENT, -10}, // MORALE_ALCOHOL_CRASH
{ STRATEGIC_MORALE_EVENT, +15}, // MORALE_MONSTER_QUEEN_KILLED
{ STRATEGIC_MORALE_EVENT, +25}, // MORALE_DEIDRANNA_KILLED
{ TACTICAL_MORALE_EVENT, -1}, // MORALE_CLAUSTROPHOBE_UNDERGROUND,
{ TACTICAL_MORALE_EVENT, -5}, // MORALE_INSECT_PHOBIC_SEES_CREATURE,
{ TACTICAL_MORALE_EVENT, -1}, // MORALE_NERVOUS_ALONE,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_MERC_CAPTURED,
{ STRATEGIC_MORALE_EVENT, -5}, // MORALE_MERC_MARRIED,
{ STRATEGIC_MORALE_EVENT, +8}, // MORALE_QUEEN_BATTLE_WON,
{ STRATEGIC_MORALE_EVENT, +5}, // MORALE_SEX,
// added by SANDRO
{ STRATEGIC_MORALE_EVENT, -1}, // MORALE_HEAT_INTOLERANT_IN_DESERT,
{ TACTICAL_MORALE_EVENT, -1}, // MORALE_PSYCHO_UNABLE_TO_PSYCHO,
{ STRATEGIC_MORALE_EVENT, +1}, // MORALE_PACIFIST_GAIN_NONCOMBAT,
{ TACTICAL_MORALE_EVENT, +1}, // MORALE_MALICIOUS_HIT,
// added by Flugente
{ TACTICAL_MORALE_EVENT, 1}, //MORALE_FOOD,
{ TACTICAL_MORALE_EVENT, 5}, //MORALE_GOOD_FOOD,
{ TACTICAL_MORALE_EVENT, -1}, //MORALE_BAD_FOOD,
{ TACTICAL_MORALE_EVENT, -5}, //MORALE_LOATHSOME_FOOD,
};
BOOLEAN gfSomeoneSaidMoraleQuote = FALSE;
BOOLEAN IsShowOffNearBy( SOLDIERTYPE * pSoldier ); // Added by SANDRO
INT8 GetMoraleModifier( SOLDIERTYPE * pSoldier )
{
if (pSoldier->flags.uiStatusFlags & SOLDIER_PC)
{
if (pSoldier->aiData.bMorale > 50)
{
// give +1 at 55, +3 at 65, up to +5 at 95 and above
return( (pSoldier->aiData.bMorale - 45) / 10 );
}
else
{
// give penalties down to -20 at 0 (-2 at 45, -4 by 40...)
return( (pSoldier->aiData.bMorale - 50) * 2 / 5 );
}
}
else
{
// use AI morale
switch( pSoldier->aiData.bAIMorale )
{
case MORALE_HOPELESS:
return( -15 );
case MORALE_WORRIED:
return( -7 );
case MORALE_CONFIDENT:
return( 2 );
case MORALE_FEARLESS:
return( 5 );
default:
return( 0 );
}
}
}
void DecayTacticalMorale( SOLDIERTYPE * pSoldier )
{
// decay the tactical morale modifier
if (pSoldier->aiData.bTacticalMoraleMod != 0)
{
// decay the modifier!
if (pSoldier->aiData.bTacticalMoraleMod > 0)
{
pSoldier->aiData.bTacticalMoraleMod = __max( 0, pSoldier->aiData.bTacticalMoraleMod - (8 - pSoldier->aiData.bTacticalMoraleMod / 10) );
}
else
{
pSoldier->aiData.bTacticalMoraleMod = __min( 0, pSoldier->aiData.bTacticalMoraleMod + (6 + pSoldier->aiData.bTacticalMoraleMod / 10) );
}
}
}
void DecayStrategicMorale( SOLDIERTYPE * pSoldier )
{
// HEADROCK HAM 3.5: Strategic Morale Mod no longer normalizes to 0 by default. In fact, a local facility can
// cause normalization to another value (positive or negative!), based on the activity that the character is
// currently performing. It can literally reduce morale to ridiculously low amounts if you don't watch your
// assignments in some sectors.
// decay the modifier!
if (pSoldier->aiData.bStrategicMoraleMod > 0)
{
pSoldier->aiData.bStrategicMoraleMod = __max( 0, pSoldier->aiData.bStrategicMoraleMod - (8 - (pSoldier->aiData.bStrategicMoraleMod / 10)) );
}
else
{
pSoldier->aiData.bStrategicMoraleMod = __min( 0, pSoldier->aiData.bStrategicMoraleMod + (6 + (pSoldier->aiData.bStrategicMoraleMod / 10)) );
}
}
void DecayTacticalMoraleModifiers( void )
{
SOLDIERTYPE * pSoldier;
UINT8 ubLoop, ubLoop2;
BOOLEAN fHandleNervous;
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pSoldier++ )
{
//if the merc is active, in Arulco
// CJC: decay modifiers while asleep! or POW!
if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE &&
!(pSoldier->bAssignment == IN_TRANSIT ||
pSoldier->bAssignment == ASSIGNMENT_DEAD ) )
{
// only let morale mod decay if it is positive while merc is a POW
if ( pSoldier->bAssignment == ASSIGNMENT_POW && pSoldier->aiData.bTacticalMoraleMod < 0 )
{
continue;
}
// Flugente: drugs can temporarily cause a merc get a new disability
// therefore we change this routine
BOOLEAN isClaustrophobic = gMercProfiles[ pSoldier->ubProfile ].bDisability == CLAUSTROPHOBIC ? TRUE : FALSE;
BOOLEAN isNervous = gMercProfiles[ pSoldier->ubProfile ].bDisability == NERVOUS ? TRUE : FALSE;
if ( MercUnderTheInfluence(pSoldier, DRUG_TYPE_CLAUSTROPHOBIC) )
isClaustrophobic = TRUE;
if ( MercUnderTheInfluence(pSoldier, DRUG_TYPE_NERVOUS) )
isNervous = TRUE;
if ( isClaustrophobic )
{
if ( pSoldier->bSectorZ > 0 )
{
// underground, no recovery... in fact, if tact morale is high, decay
if ( pSoldier->aiData.bTacticalMoraleMod > PHOBIC_LIMIT )
{
HandleMoraleEvent( pSoldier, MORALE_CLAUSTROPHOBE_UNDERGROUND, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
}
}
if ( isNervous )
{
if ( pSoldier->aiData.bMorale < 50 )
{
if (pSoldier->ubGroupID != 0 && PlayerIDGroupInMotion( pSoldier->ubGroupID ))
{
if ( NumberOfPeopleInSquad( pSoldier->bAssignment ) == 1 )
{
fHandleNervous = TRUE;
}
else
{
fHandleNervous = FALSE;
}
}
else if ( pSoldier->bActive && pSoldier->bInSector )
{
if ( DistanceToClosestFriend( pSoldier ) > NERVOUS_RADIUS )
{
fHandleNervous = TRUE;
}
else
{
fHandleNervous = FALSE;
}
}
else
{
// look for anyone else in same sector
fHandleNervous = TRUE;
for ( ubLoop2 = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; ubLoop2 <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop2++ )
{
if ( MercPtrs[ ubLoop2 ] != pSoldier && MercPtrs[ ubLoop2 ]->bActive && MercPtrs[ ubLoop2 ]->sSectorX == pSoldier->sSectorX && MercPtrs[ ubLoop2 ]->sSectorY == pSoldier->sSectorY && MercPtrs[ ubLoop2 ]->bSectorZ == pSoldier->bSectorZ )
{
// found someone!
fHandleNervous = FALSE;
break;
}
}
}
if ( fHandleNervous )
{
if ( pSoldier->aiData.bTacticalMoraleMod == PHOBIC_LIMIT )
{
// don't change morale
//continue;
}
else
{
// alone, no recovery... in fact, if tact morale is high, decay
if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
{
TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
}
HandleMoraleEvent( pSoldier, MORALE_NERVOUS_ALONE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
}
}
}
DecayTacticalMorale( pSoldier );
RefreshSoldierMorale( pSoldier );
}
}
}
void DecayStrategicMoraleModifiers( void )
{
SOLDIERTYPE * pSoldier;
UINT8 ubLoop;
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pSoldier++ )
{
//if the merc is active, in Arulco
// CJC: decay modifiers while asleep! or POW!
if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE &&
!(pSoldier->bAssignment == IN_TRANSIT ||
pSoldier->bAssignment == ASSIGNMENT_DEAD ) )
{
// only let morale mod decay if it is positive while merc is a POW
if ( pSoldier->bAssignment == ASSIGNMENT_POW && pSoldier->aiData.bStrategicMoraleMod < 0 )
{
continue;
}
DecayStrategicMorale( pSoldier );
RefreshSoldierMorale( pSoldier );
}
}
}
void RefreshSoldierMorale( SOLDIERTYPE * pSoldier )
{
INT32 iActualMorale;
if ( pSoldier->flags.fMercAsleep )
{
// delay this till later!
return;
}
// CJC, April 19, 1999: added up to 20% morale boost according to progress
iActualMorale = DEFAULT_MORALE + (INT32) pSoldier->aiData.bTeamMoraleMod + (INT32) pSoldier->aiData.bTacticalMoraleMod + (INT32) pSoldier->aiData.bStrategicMoraleMod + (INT32) (CurrentPlayerProgressPercentage() / 5);
// ATE: Modify morale based on drugs....
iActualMorale += ( ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_ADRENALINE ] * DRUG_EFFECT_MORALE_MOD ) / 100 );
iActualMorale += ( ( pSoldier->drugs.bDrugEffect[ DRUG_TYPE_ALCOHOL ] * ALCOHOL_EFFECT_MORALE_MOD ) / 100 );
iActualMorale = __min( 100, iActualMorale );
iActualMorale = __max( 0, iActualMorale );
UINT8 ubMaxMorale = 100;
// HEADROCK HAM 3.5: Local facilities may decrease the total morale allowed.
for (UINT16 cnt = 0; cnt < NUM_FACILITY_TYPES; cnt++)
{
if (gFacilityLocations[SECTOR(pSoldier->sSectorX, pSoldier->sSectorY)][cnt].fFacilityHere)
{
if (cnt == (UINT16)pSoldier->sFacilityTypeOperated && // Soldier is operating this facility
GetSoldierFacilityAssignmentIndex( pSoldier ) != -1)
{
UINT8 ubFacilityType = (UINT8)cnt;
UINT8 ubAssignmentType = (UINT8)GetSoldierFacilityAssignmentIndex( pSoldier );
// Check this facility both for an assignment-specific AND ambient value. Use it if it's the lowest
// encountered yet.
ubMaxMorale = __min(ubMaxMorale, (UINT8)GetFacilityModifier(FACILITY_MAX_MORALE, ubFacilityType, ubAssignmentType ));
}
else // Soldier is not operating this facility
{
// Check this facility for an AMBIENT Maximum Morale limit. Use it if it's the lowest encountered yet.
ubMaxMorale = __min(ubMaxMorale, (UINT8)GetFacilityModifier(FACILITY_MAX_MORALE, (UINT8)cnt, FAC_AMBIENT));
}
}
}
// Flugente: ubMaxMorale can now be influenced by our food situation
if ( gGameOptions.fFoodSystem )
FoodMaxMoraleModifiy(pSoldier, &ubMaxMorale);
// Flugente: max morale can be lowered
iActualMorale = min(iActualMorale, pSoldier->GetMoraleThreshold() );
if (ubMaxMorale > 0 && iActualMorale > ubMaxMorale)
{
// Normalize to Max Morale
iActualMorale = (iActualMorale + ubMaxMorale) / 2;
}
pSoldier->aiData.bMorale = (INT8) iActualMorale;
// update mapscreen as needed
fCharacterInfoPanelDirty = TRUE;
}
// SANDRO - changed this a bit
void UpdateSoldierMorale( SOLDIERTYPE * pSoldier, INT8 bMoraleEvent )
{
MERCPROFILESTRUCT * pProfile;
INT32 iMoraleModTotal;
UINT8 ubType;
INT8 bMoraleMod = 0;
if ( !pSoldier->bActive || ( pSoldier->stats.bLife < CONSCIOUSNESS ) ||
( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) || AM_AN_EPC( pSoldier ) )
{
return;
}
if ( ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) ||
( pSoldier->bAssignment == ASSIGNMENT_POW ) ||
( pSoldier->bAssignment == IN_TRANSIT ) )
{
return;
}
if ( pSoldier->ubProfile == NO_PROFILE )
{
return;
}
if(cDisableMorale && is_networked)
{
return;
}//hayden
ubType = gbMoraleEvent[bMoraleEvent].ubType;
bMoraleMod = gbMoraleEvent[bMoraleEvent].bChange;
pProfile = &(gMercProfiles[ pSoldier->ubProfile ]);
if (bMoraleMod > 0)
{
if ( gGameOptions.fNewTraitSystem )
{
// SANDRO - STOMP traits
// Squadleader's bonus to morale
if ( IS_MERC_BODY_TYPE( pSoldier ) && (pSoldier->bTeam == ENEMY_TEAM || pSoldier->bTeam == MILITIA_TEAM || pSoldier->bTeam == gbPlayerNum) )
{
bMoraleMod += (gSkillTraitValues.ubSLMoraleGainBonus * GetSquadleadersCountInVicinity( pSoldier, FALSE, FALSE ));
}
// Check for character traits
INT8 bNumMercs = 0;
switch( pProfile->bCharacterTrait )
{
case CHAR_TRAIT_SOCIABLE:
bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_SOCIABLE );
if ( bNumMercs == 0 )
bMoraleMod -= 5;
else if ( bNumMercs == 1 )
bMoraleMod -= 2;
break;
case CHAR_TRAIT_LONER:
bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_LONER );
if ( bNumMercs > 1 )
bMoraleMod -= 5;
else if ( bNumMercs == 1 )
bMoraleMod -= 2;
break;
case CHAR_TRAIT_OPTIMIST:
bMoraleMod += 1;
break;
case CHAR_TRAIT_AGGRESSIVE:
switch ( bMoraleEvent )
{
case MORALE_KILLED_ENEMY:
case MORALE_DID_LOTS_OF_DAMAGE:
case MORALE_MONSTER_QUEEN_KILLED:
case MORALE_DEIDRANNA_KILLED:
bMoraleMod += 1;
break;
}
break;
case CHAR_TRAIT_PACIFIST:
switch ( bMoraleEvent )
{
case MORALE_KILLED_ENEMY:
case MORALE_DID_LOTS_OF_DAMAGE:
case MORALE_BATTLE_WON:
case MORALE_HEARD_BATTLE_WON:
case MORALE_HATED_DIED:
case MORALE_MONSTER_QUEEN_KILLED:
case MORALE_DEIDRANNA_KILLED:
case MORALE_QUEEN_BATTLE_WON:
bMoraleMod -= 5;
break;
}
break;
}
if ( IsShowOffNearBy( pSoldier ) )
{
bMoraleMod -= 2;
}
}
else if ( bMoraleMod != MORALE_PSYCHO_UNABLE_TO_PSYCHO && bMoraleMod != MORALE_MALICIOUS_HIT)
{
switch( pProfile->bAttitude )
{
case ATT_OPTIMIST:
case ATT_AGGRESSIVE:
bMoraleMod += 1;
break;
case ATT_PESSIMIST:
bMoraleMod -= 1;
break;
default:
break;
}
}
if (bMoraleMod < 0)
{
// can't change a positive event into a negative one!
bMoraleMod = 0;
}
}
else
{
if ( gGameOptions.fNewTraitSystem )
{
// SANDRO - STOMP traits - squadleader's bonus to morale
if ( IS_MERC_BODY_TYPE( pSoldier ) && (pSoldier->bTeam == ENEMY_TEAM || pSoldier->bTeam == MILITIA_TEAM || pSoldier->bTeam == gbPlayerNum) )
{
switch ( bMoraleEvent ) // certain thing are not supported by our squadleader
{
case MORALE_HATED_DIED:
case MORALE_MERC_MARRIED:
case MORALE_SEX:
case MORALE_PSYCHO_UNABLE_TO_PSYCHO:
case MORALE_MALICIOUS_HIT:
break;
default:
bMoraleMod += (gSkillTraitValues.ubSLMoraleLossReduction * GetSquadleadersCountInVicinity( pSoldier, FALSE, FALSE ));
break;
}
}
// Check for character traits
if ( pProfile->bCharacterTrait == CHAR_TRAIT_OPTIMIST )
{
bMoraleMod += 1;
}
// Fearless character does not suffer morale loss for these so much
else if( pProfile->bCharacterTrait == CHAR_TRAIT_DAUNTLESS )
{
switch ( bMoraleEvent )
{
case MORALE_SQUADMATE_DIED:
case MORALE_SUPPRESSED:
case MORALE_TOOK_LOTS_OF_DAMAGE:
bMoraleMod += 3;
break;
case MORALE_TEAMMATE_DIED:
bMoraleMod += 2;
break;
}
}
}
else if ( bMoraleMod != MORALE_PSYCHO_UNABLE_TO_PSYCHO )
{
switch( pProfile->bAttitude )
{
case ATT_OPTIMIST:
bMoraleMod += 1;
break;
case ATT_PESSIMIST:
bMoraleMod -= 1;
break;
case ATT_COWARD:
bMoraleMod -= 2;
default:
break;
}
}
// Flugente: what the heck? why is morale lowered or increased depending on our height? This doesn't make any sense. AT ALL.
/*if (pSoldier->pathing.bLevel == 1)
{
bMoraleMod--;
}
else if (pSoldier->pathing.bLevel > 5)
{
bMoraleMod++;
}*/
if (bMoraleMod > 0)
{
// can't change a negative event into a positive one!
bMoraleMod = 0;
}
}
// apply change!
if (ubType == TACTICAL_MORALE_EVENT)
{
iMoraleModTotal = (INT32) pSoldier->aiData.bTacticalMoraleMod + (INT32) bMoraleMod;
iMoraleModTotal = __min( iMoraleModTotal, MORALE_MOD_MAX );
iMoraleModTotal = __max( iMoraleModTotal, -MORALE_MOD_MAX );
pSoldier->aiData.bTacticalMoraleMod = (INT8) iMoraleModTotal;
}
else if ( gTacticalStatus.fEnemyInSector && !pSoldier->bInSector ) // delayed strategic
{
iMoraleModTotal = (INT32) pSoldier->bDelayedStrategicMoraleMod + (INT32) bMoraleMod;
iMoraleModTotal = __min( iMoraleModTotal, MORALE_MOD_MAX );
iMoraleModTotal = __max( iMoraleModTotal, -MORALE_MOD_MAX );
pSoldier->bDelayedStrategicMoraleMod = (INT8) iMoraleModTotal;
}
else // strategic
{
iMoraleModTotal = (INT32) pSoldier->aiData.bStrategicMoraleMod + (INT32) bMoraleMod;
iMoraleModTotal = __min( iMoraleModTotal, MORALE_MOD_MAX );
iMoraleModTotal = __max( iMoraleModTotal, -MORALE_MOD_MAX );
pSoldier->aiData.bStrategicMoraleMod = (INT8) iMoraleModTotal;
}
RefreshSoldierMorale( pSoldier );
if ( !pSoldier->flags.fMercAsleep )
{
if ( !gfSomeoneSaidMoraleQuote )
{
// Check if we're below a certain value and warn
if ( pSoldier->aiData.bMorale < 35 )
{
// Have we said this quote yet?
if ( !( pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_LOW_MORAL ) )
{
gfSomeoneSaidMoraleQuote = TRUE;
// ATE: Amde it a DELAYED QUOTE - will be delayed by the dialogue Q until it's our turn...
DelayedTacticalCharacterDialogue( pSoldier, QUOTE_STARTING_TO_WHINE );
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_LOW_MORAL;
}
}
}
}
// Reset flag!
if ( pSoldier->aiData.bMorale > 65 )
{
pSoldier->usQuoteSaidFlags &= (~SOLDIER_QUOTE_SAID_LOW_MORAL );
}
}
void HandleMoraleEventForSoldier( SOLDIERTYPE * pSoldier, INT8 bMoraleEvent )
{
// SANDRO - changed this to send the event forward
UpdateSoldierMorale( pSoldier, bMoraleEvent ); //, gbMoraleEvent[bMoraleEvent].ubType, gbMoraleEvent[bMoraleEvent].bChange );
}
void HandleMoraleEvent( SOLDIERTYPE *pSoldier, INT8 bMoraleEvent, INT16 sMapX, INT16 sMapY, INT8 bMapZ )
{
UINT8 ubLoop;
SOLDIERTYPE * pTeamSoldier;
MERCPROFILESTRUCT * pProfile;
gfSomeoneSaidMoraleQuote = FALSE;
// NOTE: Many morale events are NOT attached to a specific player soldier at all!
// Those that do need it have Asserts on a case by case basis below
if (pSoldier == NULL)
{
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Handling morale event %d at X=%d, Y=%d,Z=%d", bMoraleEvent, sMapX, sMapY, bMapZ ) );
}
else
{
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Handling morale event %d for %S at X=%d, Y=%d, Z=%d", bMoraleEvent, pSoldier->GetName(), sMapX, sMapY, bMapZ ) );
}
switch( bMoraleEvent )
{
case MORALE_KILLED_ENEMY:
case MORALE_DID_LOTS_OF_DAMAGE:
case MORALE_DRUGS_CRASH:
case MORALE_ALCOHOL_CRASH:
case MORALE_SUPPRESSED:
case MORALE_TOOK_LOTS_OF_DAMAGE:
case MORALE_HIGH_DEATHRATE:
case MORALE_SEX:
// needs specific soldier!
Assert( pSoldier );
// affects the soldier only
HandleMoraleEventForSoldier( pSoldier, bMoraleEvent );
break;
case MORALE_CLAUSTROPHOBE_UNDERGROUND:
case MORALE_INSECT_PHOBIC_SEES_CREATURE:
case MORALE_NERVOUS_ALONE:
case MORALE_HEAT_INTOLERANT_IN_DESERT: // SANDRO added this one
// needs specific soldier!
Assert( pSoldier );
// affects the soldier only, should be ignored if tactical morale mod is -20 or less
// SANDRO - really?? I think it also shouldn't go lower if strategic morale mod is less than -20
if ( pSoldier->aiData.bTacticalMoraleMod > PHOBIC_LIMIT && pSoldier->aiData.bStrategicMoraleMod > PHOBIC_LIMIT )
{
HandleMoraleEventForSoldier( pSoldier, bMoraleEvent );
}
break;
case MORALE_BATTLE_WON:
// affects everyone to varying degrees
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive )
{
if ( SOLDIER_IN_SECTOR( pTeamSoldier, sMapX, sMapY, bMapZ ) )
{
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_BATTLE_WON );
}
// SANDRO - Assertive people don't care about actions of others
else if ( !gGameOptions.fNewTraitSystem || gMercProfiles[pTeamSoldier->ubProfile].bCharacterTrait != CHAR_TRAIT_ASSERTIVE )
{
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_HEARD_BATTLE_WON );
}
}
}
break;
case MORALE_RAN_AWAY:
// affects everyone to varying degrees
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive )
{
// CJC: adding to SOLDIER_IN_SECTOR check special stuff because the old sector values might
// be appropriate (because in transit going out of that sector!)
if ( SOLDIER_IN_SECTOR( pTeamSoldier, sMapX, sMapY, bMapZ ) || ( pTeamSoldier->flags.fBetweenSectors && ((pTeamSoldier->ubPrevSectorID % 16) + 1) == sMapX && ((pTeamSoldier->ubPrevSectorID / 16) + 1) == sMapY && ( pTeamSoldier->bSectorZ == bMapZ ) ) )
{
if ( gGameOptions.fNewTraitSystem )
{
// Flugente: if we have the covert trait and are covert, we might simply be returning from a reconnaissance mission in enemy territory. No need for a morale drop in this case
if ( !HAS_SKILL_TRAIT( pTeamSoldier, COVERT_NT ) || ( (pTeamSoldier->bSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER)) == 0) )
{
// SANDRO - no penalty for pacifists to run away
if ( gMercProfiles[pTeamSoldier->ubProfile].bCharacterTrait != CHAR_TRAIT_PACIFIST )
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_RAN_AWAY );
// Double morale drop for aggressive people
if ( gMercProfiles[pTeamSoldier->ubProfile].bCharacterTrait == CHAR_TRAIT_AGGRESSIVE )
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_RAN_AWAY );
}
}
else
{
switch ( gMercProfiles[ pTeamSoldier->ubProfile ].bAttitude )
{
case ATT_AGGRESSIVE:
// double the penalty - these guys REALLY hate running away
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_RAN_AWAY );
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_RAN_AWAY );
break;
case ATT_COWARD:
// no penalty - cowards are perfectly happy to avoid fights!
break;
default:
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_RAN_AWAY );
break;
}
}
}
// SANDRO - Assertive people don't care about actions of others
else if ( !gGameOptions.fNewTraitSystem || gMercProfiles[pTeamSoldier->ubProfile].bCharacterTrait != CHAR_TRAIT_ASSERTIVE )
{
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_HEARD_BATTLE_LOST );
}
}
}
break;
case MORALE_TOWN_LIBERATED:
case MORALE_TOWN_LOST:
case MORALE_MINE_LIBERATED:
case MORALE_MINE_LOST:
case MORALE_SAM_SITE_LIBERATED:
case MORALE_SAM_SITE_LOST:
case MORALE_KILLED_CIVILIAN:
case MORALE_LOW_DEATHRATE:
case MORALE_HEARD_BATTLE_WON:
case MORALE_HEARD_BATTLE_LOST:
case MORALE_MONSTER_QUEEN_KILLED:
case MORALE_DEIDRANNA_KILLED:
// affects everyone, everywhere
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive )
{
if ( gGameOptions.fNewTraitSystem && bMoraleEvent != MORALE_DEIDRANNA_KILLED)
{
if ( !SOLDIER_IN_SECTOR( pTeamSoldier, sMapX, sMapY, bMapZ ) && ( gbMoraleEvent[bMoraleEvent].bChange > 0 ) &&
gMercProfiles[pTeamSoldier->ubProfile].bCharacterTrait == CHAR_TRAIT_ASSERTIVE )
{
// No morale gain for assertive people from actions of others
}
else
{
HandleMoraleEventForSoldier( pTeamSoldier, bMoraleEvent );
}
}
else
{
HandleMoraleEventForSoldier( pTeamSoldier, bMoraleEvent );
}
}
}
break;
case MORALE_POOR_MORALE:
case MORALE_GREAT_MORALE:
case MORALE_AIRSTRIKE:
// affects every in sector
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive && SOLDIER_IN_SECTOR( pTeamSoldier, sMapX, sMapY, bMapZ ) )
{
HandleMoraleEventForSoldier( pTeamSoldier, bMoraleEvent );
}
}
break;
case MORALE_MERC_CAPTURED:
// needs specific soldier! (for reputation, not here)
Assert( pSoldier );
// affects everyone
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive )
{
HandleMoraleEventForSoldier( pTeamSoldier, bMoraleEvent );
}
}
break;
case MORALE_TEAMMATE_DIED:
// needs specific soldier!
Assert( pSoldier );
// affects everyone, in sector differently than not, extra bonuses if it's a buddy or hated merc
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive && pTeamSoldier->ubProfile != NO_PROFILE)
{
pProfile = &(gMercProfiles[ pTeamSoldier->ubProfile ]);
if (HATED_MERC( pProfile, pSoldier->ubProfile ))
{
// yesss!
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_HATED_DIED );
}
else
{
if ( SOLDIER_IN_SECTOR( pTeamSoldier, sMapX, sMapY, bMapZ ) )
{
// mate died in my sector! tactical morale mod
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_SQUADMATE_DIED );
// SANDRO - Squadleaders death cost a lot of morale
if( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, SQUADLEADER_NT ))
{
for ( UINT8 i = 0; i < (gSkillTraitValues.ubSLDeathMoralelossMultiplier * NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT )); i++ )
{
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_SQUADMATE_DIED );
}
}
}
// this is handled for everyone even if in sector, as it's a strategic morale mod
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_TEAMMATE_DIED );
// SANDRO - Squadleaders death cost a lot of morale
if( gGameOptions.fNewTraitSystem && (NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT ) > 1))
{
for ( UINT8 i = 0; i < gSkillTraitValues.ubSLDeathMoralelossMultiplier; i++ )
{
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_SQUADMATE_DIED );
}
}
if (BUDDY_MERC( pProfile, pSoldier->ubProfile ))
{
// oh no! buddy died!
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_BUDDY_DIED );
}
}
}
}
break;
case MORALE_MERC_MARRIED:
// female mercs get unhappy based on how sexist they are (=hate men)
// gentlemen males get unhappy too
ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ ubLoop ]; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++, pTeamSoldier++ )
{
if ( pTeamSoldier->bActive && pTeamSoldier->ubProfile != NO_PROFILE )
{
if ( WhichHated( pTeamSoldier->ubProfile, pSoldier->ubProfile ) != -1 )
{
// we hate 'em anyways
continue;
}
if ( gMercProfiles[ pTeamSoldier->ubProfile ].bSex == FEMALE )
{
switch( gMercProfiles[ pTeamSoldier->ubProfile ].bSexist )
{
case SOMEWHAT_SEXIST:
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_MERC_MARRIED );
break;
case VERY_SEXIST:
// handle TWICE!
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_MERC_MARRIED );
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_MERC_MARRIED );
break;
default:
break;
}
}
else
{
switch( gMercProfiles[ pTeamSoldier->ubProfile ].bSexist )
{
case GENTLEMAN:
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_MERC_MARRIED );
break;
default:
break;
}
}
}
}
break;
// added by SANDRO
case MORALE_PSYCHO_UNABLE_TO_PSYCHO:
case MORALE_PACIFIST_GAIN_NONCOMBAT: // added by SANDRO
case MORALE_MALICIOUS_HIT: // added by SANDRO
Assert( pSoldier );
HandleMoraleEventForSoldier( pSoldier, bMoraleEvent );
break;
// added by Flugente
case MORALE_FOOD:
case MORALE_GOOD_FOOD:
case MORALE_BAD_FOOD:
case MORALE_LOATHSOME_FOOD:
Assert( pSoldier );
HandleMoraleEventForSoldier( pSoldier, bMoraleEvent );
break;
default:
// debug message
ScreenMsg( MSG_FONT_RED, MSG_BETAVERSION, L"Invalid morale event type = %d. AM/CC-1", bMoraleEvent );
break;
}
// some morale events also impact the player's reputation with the mercs back home
switch( bMoraleEvent )
{
case MORALE_HIGH_DEATHRATE:
ModifyPlayerReputation(REPUTATION_HIGH_DEATHRATE);
break;
case MORALE_LOW_DEATHRATE:
ModifyPlayerReputation(REPUTATION_LOW_DEATHRATE);
break;
case MORALE_POOR_MORALE:
ModifyPlayerReputation(REPUTATION_POOR_MORALE);
break;
case MORALE_GREAT_MORALE:
ModifyPlayerReputation(REPUTATION_GREAT_MORALE);
break;
case MORALE_BATTLE_WON:
ModifyPlayerReputation(REPUTATION_BATTLE_WON);
break;
case MORALE_RAN_AWAY:
case MORALE_HEARD_BATTLE_LOST:
ModifyPlayerReputation(REPUTATION_BATTLE_LOST);
break;
case MORALE_TOWN_LIBERATED:
ModifyPlayerReputation(REPUTATION_TOWN_WON);
break;
case MORALE_TOWN_LOST:
ModifyPlayerReputation(REPUTATION_TOWN_LOST);
break;
case MORALE_TEAMMATE_DIED:
// impact depends on that dude's level of experience
ModifyPlayerReputation((UINT8) (pSoldier->stats.bExpLevel * REPUTATION_SOLDIER_DIED));
break;
case MORALE_MERC_CAPTURED:
// impact depends on that dude's level of experience
ModifyPlayerReputation((UINT8) (pSoldier->stats.bExpLevel * REPUTATION_SOLDIER_CAPTURED));
break;
case MORALE_KILLED_CIVILIAN:
ModifyPlayerReputation(REPUTATION_KILLED_CIVILIAN);
break;
case MORALE_MONSTER_QUEEN_KILLED:
ModifyPlayerReputation(REPUTATION_KILLED_MONSTER_QUEEN);
break;
case MORALE_DEIDRANNA_KILLED:
ModifyPlayerReputation(REPUTATION_KILLED_DEIDRANNA);
break;
default:
// no reputation impact
break;
}
}
void HourlyMoraleUpdate( void )
{
INT8 bMercID, bOtherID;
INT8 bActualTeamOpinion;
INT8 bTeamMoraleModChange, bTeamMoraleModDiff;
INT8 bOpinion=-1;
INT32 iTotalOpinions;
INT8 bNumTeamMembers;
INT8 bHighestTeamLeadership = 0;
INT8 bLastTeamID;
SOLDIERTYPE * pSoldier;
SOLDIERTYPE * pOtherSoldier;
MERCPROFILESTRUCT * pProfile;
BOOLEAN fSameGroupOnly;
static INT8 bStrategicMoraleUpdateCounter = 0;
BOOLEAN fFoundHated = FALSE;
INT8 bHated;
bMercID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID;
// loop through all mercs to calculate their morale
for ( pSoldier = MercPtrs[ bMercID ]; bMercID <= bLastTeamID; bMercID++,pSoldier++)
{
//if the merc is active, in Arulco, and conscious, not POW
if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE &&
!(pSoldier->bAssignment == IN_TRANSIT ||
pSoldier->flags.fMercAsleep == TRUE ||
pSoldier->bAssignment == ASSIGNMENT_DEAD ||
pSoldier->bAssignment == ASSIGNMENT_POW) )
{
// calculate the guy's opinion of the people he is with
pProfile = &(gMercProfiles[ pSoldier->ubProfile ]);
// if we're moving
if (pSoldier->ubGroupID != 0 && PlayerIDGroupInMotion( pSoldier->ubGroupID ))
{
// we only check our opinions of people in our squad
fSameGroupOnly = TRUE;
}
else
{
fSameGroupOnly = FALSE;
}
fFoundHated = FALSE;
// reset counts to calculate average opinion
iTotalOpinions = 0;
bNumTeamMembers = 0;
// let people with high leadership affect their own morale
bHighestTeamLeadership = EffectiveLeadership( pSoldier );
// loop through all other mercs
bOtherID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pOtherSoldier = MercPtrs[ bOtherID ]; bOtherID <= bLastTeamID; bOtherID++,pOtherSoldier++)
{
// skip past ourselves and all inactive mercs
if (bOtherID != bMercID && pOtherSoldier->bActive && pOtherSoldier->ubProfile != NO_PROFILE &&
!(pOtherSoldier->bAssignment == IN_TRANSIT ||
pOtherSoldier->flags.fMercAsleep == TRUE ||
pOtherSoldier->bAssignment == ASSIGNMENT_DEAD ||
pOtherSoldier->bAssignment == ASSIGNMENT_POW))
{
if (fSameGroupOnly)
{
// all we have to check is the group ID
if (pSoldier->ubGroupID != pOtherSoldier->ubGroupID)
{
continue;
}
}
else
{
// check to see if the location is the same
if (pOtherSoldier->sSectorX != pSoldier->sSectorX ||
pOtherSoldier->sSectorY != pSoldier->sSectorY ||
pOtherSoldier->bSectorZ != pSoldier->bSectorZ)
{
continue;
}
// if the OTHER soldier is in motion then we don't do anything!
if (pOtherSoldier->ubGroupID != 0 && PlayerIDGroupInMotion( pOtherSoldier->ubGroupID ))
{
continue;
}
}
// WANNE: Fix by Headrock: Morale and the ice-cream truck.
if (OKToCheckOpinion(pOtherSoldier->ubProfile))
{
bOpinion = pProfile->bMercOpinion[ pOtherSoldier->ubProfile ];
}
else
{
bOpinion = 0;
}
// Flugente: evaluate appearance/refinement/hated nationalities
// some people loathe ugly people and like beautiful people. It's a mean world.
// we also handle sexism here
switch ( gMercProfiles[ pOtherSoldier->ubProfile ].bAppearance )
{
case APPEARANCE_UGLY:
{
if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME )
bOpinion -= gGameExternalOptions.sMoraleModAppearance * 2;
else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModAppearance * 4;
// some people are sexists
if ( pProfile->bSexist && gMercProfiles[ pOtherSoldier->ubProfile ].bSex != pProfile->bSex )
{
if ( pProfile->bSexist == SOMEWHAT_SEXIST )
bOpinion -= gGameExternalOptions.sMoraleModSexism * 2;
else if ( pProfile->bSexist == VERY_SEXIST )
bOpinion -= gGameExternalOptions.sMoraleModSexism * 4;
}
}
break;
case APPEARANCE_HOMELY:
{
if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME )
bOpinion -= gGameExternalOptions.sMoraleModAppearance;
else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModAppearance * 2;
// some people are sexists
if ( pProfile->bSexist && gMercProfiles[ pOtherSoldier->ubProfile ].bSex != pProfile->bSex )
{
if ( pProfile->bSexist == SOMEWHAT_SEXIST )
bOpinion -= gGameExternalOptions.sMoraleModSexism;
else if ( pProfile->bSexist == VERY_SEXIST )
bOpinion -= gGameExternalOptions.sMoraleModSexism * 2;
}
}
break;
case APPEARANCE_ATTRACTIVE:
{
if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME )
bOpinion += gGameExternalOptions.sMoraleModAppearance;
else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME )
bOpinion += gGameExternalOptions.sMoraleModAppearance * 2;
// some people are sexists
if ( pProfile->bSexist && gMercProfiles[ pOtherSoldier->ubProfile ].bSex != pProfile->bSex )
{
if ( pProfile->bSexist == SOMEWHAT_SEXIST )
bOpinion += gGameExternalOptions.sMoraleModSexism;
else if ( pProfile->bSexist == VERY_SEXIST )
bOpinion += gGameExternalOptions.sMoraleModSexism * 2;
}
}
break;
case APPEARANCE_BABE:
{
if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME )
bOpinion += gGameExternalOptions.sMoraleModAppearance * 2;
else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME )
bOpinion += gGameExternalOptions.sMoraleModAppearance * 4;
// some people are sexists
if ( pProfile->bSexist && gMercProfiles[ pOtherSoldier->ubProfile ].bSex != pProfile->bSex )
{
if ( pProfile->bSexist == SOMEWHAT_SEXIST )
bOpinion += gGameExternalOptions.sMoraleModSexism * 2;
else if ( pProfile->bSexist == VERY_SEXIST )
bOpinion += gGameExternalOptions.sMoraleModSexism * 4;
}
}
break;
}
// some people care about how distuingished other people are. Malus if on different ends of the spectrum, a small bonus if on the same and its really important to the person
// also give a malus if the other person is a slob or snob and we are average but care extremely (we don't like people who behave differently)
switch ( gMercProfiles[ pOtherSoldier->ubProfile ].bRefinement )
{
case REFINEMENT_SLOB:
{
if ( pProfile->bRefinement == REFINEMENT_AVERAGE )
{
if ( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModRefinement;
}
if ( pProfile->bRefinement == REFINEMENT_SLOB )
{
if ( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME )
bOpinion += gGameExternalOptions.sMoraleModRefinement;
}
else if ( pProfile->bRefinement == REFINEMENT_SNOB )
{
if ( pProfile->bRefinementCareLevel == CARELEVEL_SOME )
bOpinion -= gGameExternalOptions.sMoraleModRefinement;
else if ( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModRefinement * 2;
}
}
break;
case REFINEMENT_SNOB:
{
if ( pProfile->bRefinement == REFINEMENT_AVERAGE )
{
if ( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModRefinement;
}
if ( pProfile->bRefinement == REFINEMENT_SNOB )
{
if ( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME )
bOpinion += gGameExternalOptions.sMoraleModRefinement;
}
else if ( pProfile->bRefinement == REFINEMENT_SLOB )
{
if ( pProfile->bRefinementCareLevel == CARELEVEL_SOME )
bOpinion -= gGameExternalOptions.sMoraleModRefinement;
else if ( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModRefinement * 2;
}
}
break;
}
// some people hate other nationalities (do not mix up with racism, which uses bRace)
if ( pProfile->bHatedNationality > -1 && gMercProfiles[ pOtherSoldier->ubProfile ].bNationality == pProfile->bHatedNationality )
{
if ( pProfile->bHatedNationalityCareLevel == CARELEVEL_SOME )
bOpinion -= gGameExternalOptions.sMoraleModHatedNationality;
else if ( pProfile->bHatedNationalityCareLevel == CARELEVEL_EXTREME )
bOpinion -= gGameExternalOptions.sMoraleModHatedNationality * 2;
}
// some people are racists
if ( pProfile->bRacist && gMercProfiles[ pOtherSoldier->ubProfile ].bRace != pProfile->bRace )
{
if ( pProfile->bRacist == RACIST_SOME )
bOpinion -= gGameExternalOptions.sMoraleModRacism;
else if ( pProfile->bRacist == RACIST_VERY )
bOpinion -= gGameExternalOptions.sMoraleModRacism * 2;
}
// Flugente: backgrounds
if ( pSoldier->HasBackgroundFlag( BACKGROUND_XENOPHOBIC ) && pOtherSoldier->ubProfile != NO_PROFILE && gMercProfiles[pSoldier->ubProfile].usBackground != gMercProfiles[pOtherSoldier->ubProfile].usBackground )
bOpinion -= gGameExternalOptions.sMoraleModXenophobicBackGround;
if (bOpinion == HATED_OPINION)
{
bHated = WhichHated( pSoldier->ubProfile, pOtherSoldier->ubProfile );
if ( bHated >= 2 )
{
// learn to hate which has become full-blown hatred, full strength
fFoundHated = TRUE;
break;
}
else
{
// scale according to how close to we are to snapping
//KM : Divide by 0 error found. Wrapped into an if statement.
if( pProfile->bHatedTime[ bHated ] )
{
bOpinion = ((INT32) bOpinion) * (pProfile->bHatedTime[ bHated ] - pProfile->bHatedCount[ bHated ]) / pProfile->bHatedTime[ bHated ];
}
if ( pProfile->bHatedCount[ bHated ] <= pProfile->bHatedTime[ bHated ] / 2 )
{
// Augh, we're teamed with someone we hate! We HATE this!! Ignore everyone else!
fFoundHated = TRUE;
break;
}
// otherwise just mix this opinion in with everyone else...
}
}
iTotalOpinions += bOpinion;
bNumTeamMembers++;
if ( EffectiveLeadership( pOtherSoldier ) > bHighestTeamLeadership)
{
bHighestTeamLeadership = EffectiveLeadership( pOtherSoldier );
}
}
}
if (fFoundHated)
{
// If teamed with someone we hated, team opinion is automatically minimum
bActualTeamOpinion = HATED_OPINION;
}
else if (bNumTeamMembers > 0)
{
bActualTeamOpinion = (INT8) (iTotalOpinions / bNumTeamMembers);
// give bonus/penalty for highest leadership value on team
bActualTeamOpinion += (bHighestTeamLeadership - 50) / 10;
}
else // alone
{
bActualTeamOpinion = 0;
}
// reduce to a range of HATED through BUDDY
if (bActualTeamOpinion > BUDDY_OPINION)
{
bActualTeamOpinion = BUDDY_OPINION;
}
else if (bActualTeamOpinion < HATED_OPINION)
{
bActualTeamOpinion = HATED_OPINION;
}
// shift morale from team by ~10%
// this should range between -75 and +75
bTeamMoraleModDiff = bActualTeamOpinion - pSoldier->aiData.bTeamMoraleMod;
if (bTeamMoraleModDiff > 0)
{
bTeamMoraleModChange = 1 + bTeamMoraleModDiff / 10;
}
else if (bTeamMoraleModDiff < 0)
{
bTeamMoraleModChange = -1 + bTeamMoraleModDiff / 10;
}
else
{
bTeamMoraleModChange = 0;
}
// SANDRO - morale is going down faster if not fighting for malicious characters
if ( gGameOptions.fNewTraitSystem && pProfile->bCharacterTrait == CHAR_TRAIT_MALICIOUS )
{
bTeamMoraleModChange -= 1;
}
pSoldier->aiData.bTeamMoraleMod += bTeamMoraleModChange;
pSoldier->aiData.bTeamMoraleMod = __min( pSoldier->aiData.bTeamMoraleMod, MORALE_MOD_MAX );
pSoldier->aiData.bTeamMoraleMod = __max( pSoldier->aiData.bTeamMoraleMod, -MORALE_MOD_MAX );
// New, December 3rd, 1998, by CJC --
// If delayed strategic modifier exists then incorporate it in strategic mod
if ( pSoldier->bDelayedStrategicMoraleMod )
{
pSoldier->aiData.bStrategicMoraleMod += pSoldier->bDelayedStrategicMoraleMod;
pSoldier->bDelayedStrategicMoraleMod = 0;
pSoldier->aiData.bStrategicMoraleMod = __min( pSoldier->aiData.bStrategicMoraleMod, MORALE_MOD_MAX );
pSoldier->aiData.bStrategicMoraleMod = __max( pSoldier->aiData.bStrategicMoraleMod, -MORALE_MOD_MAX );
}
// refresh the morale value for the soldier based on the recalculated team modifier
RefreshSoldierMorale( pSoldier );
}
}
bStrategicMoraleUpdateCounter++;
if ( bStrategicMoraleUpdateCounter == HOURS_BETWEEN_STRATEGIC_DECAY )
{
DecayStrategicMoraleModifiers();
bStrategicMoraleUpdateCounter = 0;
}
}
void DailyMoraleUpdate(SOLDIERTYPE *pSoldier)
{
if ( pSoldier->ubProfile == NO_PROFILE )
{
return;
}
// CJC: made per hour now
/*
// decay the merc's strategic morale modifier
if (pSoldier->aiData.bStrategicMoraleMod != 0)
{
// decay the modifier!
DecayStrategicMorale( pSoldier );
// refresh the morale value for the soldier based on the recalculated modifier
RefreshSoldierMorale( pSoldier );
}
*/
// check death rate vs. merc's tolerance once/day (ignores buddies!)
if ( MercThinksDeathRateTooHigh( pSoldier->ubProfile ) )
{
// too high, morale takes a hit
HandleMoraleEvent( pSoldier, MORALE_HIGH_DEATHRATE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
// check his morale vs. his morale tolerance once/day (ignores buddies!)
if ( MercThinksHisMoraleIsTooLow( pSoldier ) )
{
// too low, morale sinks further (merc's in a funk and things aren't getting better)
HandleMoraleEvent( pSoldier, MORALE_POOR_MORALE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
else if ( pSoldier->aiData.bMorale >= 75 )
{
// very high morale, merc is cheerleading others
HandleMoraleEvent( pSoldier, MORALE_GREAT_MORALE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
}
// Added by SANDRO
BOOLEAN IsShowOffNearBy( SOLDIERTYPE * pSoldier )
{
UINT32 uiLoop;
SOLDIERTYPE * pTeammate;
BOOLEAN fOneException = FALSE;
BOOLEAN fYesHeIs = FALSE;
if (!pSoldier)
{
return( FALSE );
}
if( !(pSoldier->bActive) || !(pSoldier->bInSector) )
{
return( FALSE );
}
for ( uiLoop = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID; uiLoop < gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; uiLoop++)
{
pTeammate = MercPtrs[ uiLoop ];
if ( pTeammate == NULL )
{
continue;
}
if ( pTeammate == pSoldier )
{
continue;
}
// Are we actually here?
if ( !(pTeammate->bActive) || !(pTeammate->bInSector) || ( pTeammate->flags.uiStatusFlags & SOLDIER_VEHICLE ) || (pTeammate->bAssignment == VEHICLE ) )
{
// is nowhere around!
continue;
}
// Are we from our team an dalive?
if ( pTeammate->bTeam == pSoldier->bTeam && pTeammate->stats.bLife >= OKLIFE &&
gMercProfiles[ pTeammate->ubProfile ].bCharacterTrait == CHAR_TRAIT_SHOWOFF && PythSpacesAway( pSoldier->sGridNo, pTeammate->sGridNo ) <= 15)
{
if ( (pSoldier->ubBodyType <= STOCKYMALE && pTeammate->ubBodyType <= STOCKYMALE) ||
(pSoldier->ubBodyType == REGFEMALE && pTeammate->ubBodyType == REGFEMALE) )
{
// phlegmatic character can ignore one
if ( gMercProfiles[ pSoldier->ubProfile ].bCharacterTrait == CHAR_TRAIT_PHLEGMATIC && !fOneException)
{
fOneException = TRUE;
}
else
{
fYesHeIs = TRUE;
}
}
}
}
return( fYesHeIs );
}