Files
source/Tactical/Morale.cpp
T
Overhaul 7a27e7105d New attack busy system
Added missing #includes

Added LUA scripting and console

Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain

Fixed signedness of many grid variables used in GetMouseMapPos calls

Fixed enemy weapon choosing:  Sometimes a mortar is chosen but later rejected, but the grenade class was not reset.  Caused assertion failure

Added checks so enemies don't try to chuck RPG grenades with their hands

Now possible to shoot a someone in the head if he's in water

Fixed InitSightArrays to also clear soldier interrupt duel points.  This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called.

Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have

Removed early setting of muzzle flash.  This would allow enemies to get an interrupt before you even fired.

Fixed item dropping by AI.  If AI tried to drop something while standing it would cause deadlock

Change to greatly speed up closing the sector inventory window in an unloaded sector

Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting


Check builddefines.h for the conditional flags.  Of greatest interest might be LUA_CONSOLE.  This will cause the game to bring up a command console when run.  However this console is severely lacking in many areas.  If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated.  The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus.

LUA scripting is very limited, basically just proof of concept.  There is one global variable, the Soldiers array.  An array index gives you the soldier in that slot in the currently loaded sector.  The soldier has a few things that can be accessed:  name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid)


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2007-06-09 09:01:04 +00:00

971 lines
30 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"
#endif
#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->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, -10}, // MORALE_DRUGS_CRASH
{ 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,
};
BOOLEAN gfSomeoneSaidMoraleQuote = FALSE;
INT8 GetMoraleModifier( SOLDIERTYPE * pSoldier )
{
if (pSoldier->uiStatusFlags & SOLDIER_PC)
{
if (pSoldier->bMorale > 50)
{
// give +1 at 55, +3 at 65, up to +5 at 95 and above
return( (pSoldier->bMorale - 45) / 10 );
}
else
{
// give penalties down to -20 at 0 (-2 at 45, -4 by 40...)
return( (pSoldier->bMorale - 50) * 2 / 5 );
}
}
else
{
// use AI morale
switch( pSoldier->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->bTacticalMoraleMod != 0)
{
// decay the modifier!
if (pSoldier->bTacticalMoraleMod > 0)
{
pSoldier->bTacticalMoraleMod = __max( 0, pSoldier->bTacticalMoraleMod - (8 - pSoldier->bTacticalMoraleMod / 10) );
}
else
{
pSoldier->bTacticalMoraleMod = __min( 0, pSoldier->bTacticalMoraleMod + (6 + pSoldier->bTacticalMoraleMod / 10) );
}
}
}
void DecayStrategicMorale( SOLDIERTYPE * pSoldier )
{
// decay the modifier!
if (pSoldier->bStrategicMoraleMod > 0)
{
pSoldier->bStrategicMoraleMod = __max( 0, pSoldier->bStrategicMoraleMod - (8 - pSoldier->bStrategicMoraleMod / 10) );
}
else
{
pSoldier->bStrategicMoraleMod = __min( 0, pSoldier->bStrategicMoraleMod + (6 + pSoldier->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->bTacticalMoraleMod < 0 )
{
continue;
}
switch( gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait )
{
case CLAUSTROPHOBIC:
if ( pSoldier->bSectorZ > 0 )
{
// underground, no recovery... in fact, if tact morale is high, decay
if ( pSoldier->bTacticalMoraleMod > PHOBIC_LIMIT )
{
HandleMoraleEvent( pSoldier, MORALE_CLAUSTROPHOBE_UNDERGROUND, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
continue;
}
break;
case NERVOUS:
if ( pSoldier->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->bTacticalMoraleMod == PHOBIC_LIMIT )
{
// don't change morale
continue;
}
// 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 );
continue;
}
}
}
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->bStrategicMoraleMod < 0 )
{
continue;
}
DecayStrategicMorale( pSoldier );
RefreshSoldierMorale( pSoldier );
}
}
}
void RefreshSoldierMorale( SOLDIERTYPE * pSoldier )
{
INT32 iActualMorale;
if ( pSoldier->fMercAsleep )
{
// delay this till later!
return;
}
// CJC, April 19, 1999: added up to 20% morale boost according to progress
iActualMorale = DEFAULT_MORALE + (INT32) pSoldier->bTeamMoraleMod + (INT32) pSoldier->bTacticalMoraleMod + (INT32) pSoldier->bStrategicMoraleMod + (INT32) (CurrentPlayerProgressPercentage() / 5);
// ATE: Modify morale based on drugs....
iActualMorale += ( ( pSoldier->bDrugEffect[ DRUG_TYPE_ADRENALINE ] * DRUG_EFFECT_MORALE_MOD ) / 100 );
iActualMorale += ( ( pSoldier->bDrugEffect[ DRUG_TYPE_ALCOHOL ] * ALCOHOL_EFFECT_MORALE_MOD ) / 100 );
iActualMorale = __min( 100, iActualMorale );
iActualMorale = __max( 0, iActualMorale );
pSoldier->bMorale = (INT8) iActualMorale;
// update mapscreen as needed
fCharacterInfoPanelDirty = TRUE;
}
void UpdateSoldierMorale( SOLDIERTYPE * pSoldier, UINT8 ubType, INT8 bMoraleMod )
{
MERCPROFILESTRUCT * pProfile;
INT32 iMoraleModTotal;
if ( !pSoldier->bActive || ( pSoldier->bLife < CONSCIOUSNESS ) ||
( pSoldier->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;
}
pProfile = &(gMercProfiles[ pSoldier->ubProfile ]);
if (bMoraleMod > 0)
{
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
{
switch( pProfile->bAttitude )
{
case ATT_OPTIMIST:
bMoraleMod += 1;
break;
case ATT_PESSIMIST:
bMoraleMod -= 1;
break;
case ATT_COWARD:
bMoraleMod -= 2;
default:
break;
}
if (pSoldier->bLevel == 1)
{
bMoraleMod--;
}
else if (pSoldier->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->bTacticalMoraleMod + (INT32) bMoraleMod;
iMoraleModTotal = __min( iMoraleModTotal, MORALE_MOD_MAX );
iMoraleModTotal = __max( iMoraleModTotal, -MORALE_MOD_MAX );
pSoldier->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->bStrategicMoraleMod + (INT32) bMoraleMod;
iMoraleModTotal = __min( iMoraleModTotal, MORALE_MOD_MAX );
iMoraleModTotal = __max( iMoraleModTotal, -MORALE_MOD_MAX );
pSoldier->bStrategicMoraleMod = (INT8) iMoraleModTotal;
}
RefreshSoldierMorale( pSoldier );
if ( !pSoldier->fMercAsleep )
{
if ( !gfSomeoneSaidMoraleQuote )
{
// Check if we're below a certain value and warn
if ( pSoldier->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->bMorale > 65 )
{
pSoldier->usQuoteSaidFlags &= (~SOLDIER_QUOTE_SAID_LOW_MORAL );
}
}
void HandleMoraleEventForSoldier( SOLDIERTYPE * pSoldier, INT8 bMoraleEvent )
{
UpdateSoldierMorale( pSoldier, 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->name, 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:
// needs specific soldier!
Assert( pSoldier );
// affects the soldier only, should be ignored if tactical morale mod is -20 or less
if ( pSoldier->bTacticalMoraleMod > 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 );
}
else
{
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->fBetweenSectors && ((pTeamSoldier->ubPrevSectorID % 16) + 1) == sMapX && ((pTeamSoldier->ubPrevSectorID / 16) + 1) == sMapY && ( pTeamSoldier->bSectorZ == bMapZ ) ) )
{
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;
}
}
else
{
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 )
{
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 );
}
// this is handled for everyone even if in sector, as it's a strategic morale mod
HandleMoraleEventForSoldier( pTeamSoldier, MORALE_TEAMMATE_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;
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->bExpLevel * REPUTATION_SOLDIER_DIED));
break;
case MORALE_MERC_CAPTURED:
// impact depends on that dude's level of experience
ModifyPlayerReputation((UINT8) (pSoldier->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->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->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;
}
}
bOpinion = pProfile->bMercOpinion[ pOtherSoldier->ubProfile ];
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->bTeamMoraleMod;
if (bTeamMoraleModDiff > 0)
{
bTeamMoraleModChange = 1 + bTeamMoraleModDiff / 10;
}
else if (bTeamMoraleModDiff < 0)
{
bTeamMoraleModChange = -1 + bTeamMoraleModDiff / 10;
}
else
{
bTeamMoraleModChange = 0;
}
pSoldier->bTeamMoraleMod += bTeamMoraleModChange;
pSoldier->bTeamMoraleMod = __min( pSoldier->bTeamMoraleMod, MORALE_MOD_MAX );
pSoldier->bTeamMoraleMod = __max( pSoldier->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->bStrategicMoraleMod += pSoldier->bDelayedStrategicMoraleMod;
pSoldier->bDelayedStrategicMoraleMod = 0;
pSoldier->bStrategicMoraleMod = __min( pSoldier->bStrategicMoraleMod, MORALE_MOD_MAX );
pSoldier->bStrategicMoraleMod = __max( pSoldier->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->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->bMorale >= 75 )
{
// very high morale, merc is cheerleading others
HandleMoraleEvent( pSoldier, MORALE_GREAT_MORALE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}
}