mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
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
411 lines
12 KiB
C++
411 lines
12 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Strategic All.h"
|
|
#else
|
|
#include <memory.h>
|
|
#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 "TopicIDs.h"
|
|
#include "TopicOps.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;
|
|
|
|
// subtract, so that a negative reputation change results in an increase in bad reputation
|
|
iNewBadRep = (INT32) gStrategicStatus.ubBadReputation - bRepChange;
|
|
|
|
// 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->bMorale < bMoraleTolerance)
|
|
{
|
|
// too low - sorry
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
// within tolerance
|
|
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();
|
|
|
|
// 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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_LACK_PROGRESS3;
|
|
break;
|
|
case 2:
|
|
AddEmail(LACK_PLAYER_PROGRESS_2, LACK_PLAYER_PROGRESS_2_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1);
|
|
gStrategicStatus.usEnricoEmailFlags |= ENRICO_EMAIL_SENT_LACK_PROGRESS2;
|
|
break;
|
|
default:
|
|
AddEmail(LACK_PLAYER_PROGRESS_1, LACK_PLAYER_PROGRESS_1_LENGTH, MAIL_ENRICO, GetWorldTotalMin(), -1);
|
|
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() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 );
|
|
} |