mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
** Merged Source Code from Development trunk, for the Spring 2011 Release ** **************************************************************************** o Development Trunk (Revision: 4444): https://81.169.133.124/source/ja2/branches/Wanne/JA2%201.13%20MP o This trunk (the official 1.13 source trunk) will only be used for fixing bugs. No new features will be added here. New Features are only included in the Development trunk o !!! After we have fixed a bug here in this trunk, we should MANUALLY merge the bugfix in the development trunk !!! git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@4446 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2323 lines
72 KiB
C++
2323 lines
72 KiB
C++
#include "builddefines.h"
|
|
|
|
#ifdef PRECOMPILEDHEADERS
|
|
#include "Strategic All.h"
|
|
#include "GameSettings.h"
|
|
#else
|
|
#include "Town Militia.h"
|
|
#include "Militia Control.h"
|
|
#include "Campaign Types.h"
|
|
#include "strategic.h"
|
|
#include "strategicmap.h"
|
|
#include "Overhead.h"
|
|
#include "Strategic Town Loyalty.h"
|
|
#include "Utilities.h"
|
|
#include "random.h"
|
|
#include "text.h"
|
|
#include "Map Screen Interface.h"
|
|
#include "Interface.h"
|
|
#include "Laptopsave.h"
|
|
#include "finances.h"
|
|
#include "Game Clock.h"
|
|
#include "Assignments.h"
|
|
#include "squads.h"
|
|
#include "Soldier Create.h"
|
|
#include "Dialogue Control.h"
|
|
#include "GameSettings.h"
|
|
#include "Queen Command.h"
|
|
#include "PreBattle Interface.h"
|
|
#include "Map Screen Interface Border.h"
|
|
#include "interface control.h"
|
|
#include "Map Screen Interface Map.h"
|
|
#endif
|
|
|
|
// HEADROCK HAM 3: include these files so that a militia trainer's Effective Leadership can be determined. Used
|
|
// to determine the number of militia trained by this merc per session. In the future may also determine QUALITY
|
|
// of trained troops.
|
|
#include "SkillCheck.h"
|
|
#include "Soldier Control.h"
|
|
#include "soldier profile type.h"
|
|
|
|
#include "MilitiaSquads.h"
|
|
#define SIZE_OF_MILITIA_COMPLETED_TRAINING_LIST 50
|
|
|
|
// temporary local global variables
|
|
UINT8 gubTownSectorServerTownId = BLANK_SECTOR;
|
|
INT16 gsTownSectorServerSkipX = -1;
|
|
INT16 gsTownSectorServerSkipY = -1;
|
|
UINT8 gubTownSectorServerIndex = 0;
|
|
BOOLEAN gfYesNoPromptIsForContinue = FALSE; // this flag remembers whether we're starting new training, or continuing
|
|
INT32 giTotalCostOfTraining = 0;
|
|
BOOLEAN gfAreWeTrainingMobile = FALSE;
|
|
BOOLEAN gfAreWePromotingGreen = FALSE;
|
|
BOOLEAN gfAreWePromotingRegular = FALSE;
|
|
|
|
|
|
//the completed list of sector soldiers for training militia
|
|
INT32 giListOfMercsInSectorsCompletedMilitiaTraining[ SIZE_OF_MILITIA_COMPLETED_TRAINING_LIST ];
|
|
SOLDIERTYPE *pMilitiaTrainerSoldier = NULL;
|
|
|
|
// note that these sector values are STRATEGIC INDEXES, not 0-255!
|
|
INT16 gsUnpaidStrategicSector[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ];
|
|
|
|
// the selected list of mercs
|
|
extern BOOLEAN fSelectedListOfMercsForMapScreen[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ];
|
|
|
|
// towns with militia training allowed
|
|
BOOLEAN gfMilitiaAllowedInTown[MAX_TOWNS] =
|
|
{
|
|
0, // blank sector
|
|
0, // omerta
|
|
1, // drassen
|
|
1, // alma
|
|
1, // grumm
|
|
0, // tixa
|
|
1, // cambria
|
|
0, // san mona
|
|
0, // estoni
|
|
0, // orta
|
|
1, // balime
|
|
1, // meduna
|
|
1, // chitzena
|
|
};
|
|
|
|
// private prototypes
|
|
void PayMilitiaTrainingYesNoBoxCallback( UINT8 bExitValue );
|
|
// HEADROCK HAM 3.6: Duplicate function for handling callback on Mobile Militia training
|
|
void PayMobileMilitiaTrainingYesNoBoxCallback( UINT8 bExitValue );
|
|
void CantTrainMilitiaOkBoxCallback( UINT8 bExitValue );
|
|
void CantTrainMobileMilitiaOkBoxCallback( UINT8 bExitValue );
|
|
|
|
void PayForTrainingInSector( UINT8 ubSector );
|
|
|
|
void InitFriendlyTownSectorServer(UINT8 ubTownId, INT16 sSkipSectorX, INT16 sSkipSectorY);
|
|
BOOLEAN ServeNextFriendlySectorInTown( INT16 *sNeighbourX, INT16 *sNeighbourY );
|
|
|
|
// HEADROCK HAM 3.6: Instead of adding more functions, I've added an argument that determines whether we're checking
|
|
// Garrison Militia payment versus Mobile Militie payment.
|
|
void BuildListOfUnpaidTrainableSectors( UINT8 ubMilitiaType );
|
|
INT32 GetNumberOfUnpaidTrainableSectors( UINT8 ubMilitiaType );
|
|
void ContinueTrainingInThisSector( UINT8 ubMilitiaType );
|
|
void StartTrainingInAllUnpaidTrainableSectors( UINT8 ubMilitiaType );
|
|
void ResetDoneFlagForAllMilitiaTrainersInSector( UINT8 ubSector, UINT8 ubMilitiaType );
|
|
void MilitiaTrainingRejected( UINT8 ubMilitiaType );
|
|
|
|
// HEADROCK HAM 3.6: Total upkeep costs for keeping militia.
|
|
UINT32 guiTotalUpkeepForMilitia = 0;
|
|
|
|
// HEADROCK HAM 3.6: Qsort for sectors with militia in them.
|
|
int MilitiaListQsortCompare(const void *pArg1, const void *pArg2);
|
|
|
|
#ifdef JA2BETAVERSION
|
|
void VerifyTownTrainingIsPaidFor( void );
|
|
#endif
|
|
|
|
void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMapY )
|
|
{
|
|
UINT8 ubMilitiaTrained = 0;
|
|
BOOLEAN fFoundOne;
|
|
INT16 sNeighbourX, sNeighbourY;
|
|
UINT8 ubTownId;
|
|
|
|
INT32 iMaxMilitiaPerSector = gGameExternalOptions.iMaxMilitiaPerSector;
|
|
|
|
// HEADROCK HAM 3.6: Leadership may affect the resulting squad size.
|
|
UINT8 ubTrainerEffectiveLeadership = FindBestMilitiaTrainingLeadershipInSector ( sMapX, sMapY, pTrainer->bSectorZ, TOWN_MILITIA );
|
|
UINT8 iTrainingSquadSize = __min(iMaxMilitiaPerSector, CalcNumMilitiaTrained(ubTrainerEffectiveLeadership, FALSE));
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia1");
|
|
// get town index
|
|
ubTownId = StrategicMap[ sMapX + sMapY * MAP_WORLD_X ].bNameId;
|
|
|
|
if( ubTownId == BLANK_SECTOR )
|
|
{
|
|
Assert( IsThisSectorASAMSector( sMapX, sMapY, 0 ) );
|
|
}
|
|
|
|
|
|
// force tactical to update militia status
|
|
gfStrategicMilitiaChangesMade = FALSE;
|
|
|
|
// ok, so what do we do with all this training? Well, in order of decreasing priority:
|
|
// 1) If there's room in training sector, create new GREEN militia guys there
|
|
// 2) If not enough room there, create new GREEN militia guys in friendly sectors of the same town
|
|
// 3) If not enough room anywhere in town, promote a number of GREENs in this sector into regulars
|
|
// 4) If not enough GREENS there to promote, promote GREENs in other sectors.
|
|
// 5) If all friendly sectors of this town are completely filled with REGULAR militia, then training effect is wasted
|
|
|
|
|
|
// Kaiden: Roaming Militia Training:
|
|
// If we're not training roaming militia,
|
|
// then we will handle everything as normal.
|
|
|
|
// HEADROCK HAM 3.6: This is now VOLUNTARY.
|
|
if( pTrainer->bAssignment == TRAIN_MOBILE )
|
|
{
|
|
CreateMilitiaSquads(sMapX, sMapY );
|
|
|
|
// the trainer announces to player that he's finished his assignment. Make his sector flash!
|
|
AssignmentDone( pTrainer, TRUE, FALSE );
|
|
|
|
// handle completion of town by training group
|
|
HandleCompletionOfTownTrainingByGroupWithTrainer( pTrainer, MOBILE_MILITIA );
|
|
}
|
|
else
|
|
{
|
|
while (ubMilitiaTrained < iTrainingSquadSize)
|
|
{
|
|
// is there room for another militia in the training sector itself?
|
|
if (CountAllMilitiaInSector(sMapX, sMapY) < iMaxMilitiaPerSector)
|
|
{
|
|
// great! Create a new GREEN militia guy in the training sector
|
|
StrategicAddMilitiaToSector(sMapX, sMapY, GREEN_MILITIA, 1);
|
|
if (sMapX == gWorldSectorX && sMapY == gWorldSectorY)
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fFoundOne = FALSE;
|
|
|
|
if( ubTownId != BLANK_SECTOR )
|
|
{
|
|
InitFriendlyTownSectorServer(ubTownId, sMapX, sMapY);
|
|
|
|
// check other eligible sectors in this town for room for another militia
|
|
while( ServeNextFriendlySectorInTown( &sNeighbourX, &sNeighbourY ) )
|
|
{
|
|
// is there room for another militia in this neighbouring sector ?
|
|
if (CountAllMilitiaInSector(sNeighbourX, sNeighbourY) < iMaxMilitiaPerSector)
|
|
{
|
|
// great! Create a new GREEN militia guy in the neighbouring sector
|
|
StrategicAddMilitiaToSector(sNeighbourX, sNeighbourY, GREEN_MILITIA, 1);
|
|
|
|
if (sNeighbourX == gWorldSectorX && sNeighbourY == gWorldSectorY)
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
fFoundOne = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we still haven't been able to train anyone
|
|
if (!fFoundOne)
|
|
{
|
|
// alrighty, then. We'll have to *promote* guys instead.
|
|
|
|
// are there any GREEN militia men in the training sector itself?
|
|
if (MilitiaInSectorOfRank(sMapX, sMapY, GREEN_MILITIA) > 0)
|
|
{
|
|
// great! Promote a GREEN militia guy in the training sector to a REGULAR
|
|
StrategicPromoteMilitiaInSector(sMapX, sMapY, GREEN_MILITIA, 1);
|
|
fFoundOne = TRUE;
|
|
if (sMapX == gWorldSectorX && sMapY == gWorldSectorY)
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ubTownId != BLANK_SECTOR )
|
|
{
|
|
// dammit! Last chance - try to find other eligible sectors in the same town with a Green guy to be promoted
|
|
InitFriendlyTownSectorServer(ubTownId, sMapX, sMapY);
|
|
|
|
// check other eligible sectors in this town for room for another militia
|
|
while( ServeNextFriendlySectorInTown( &sNeighbourX, &sNeighbourY ) )
|
|
{
|
|
// are there any GREEN militia men in the neighbouring sector ?
|
|
if (MilitiaInSectorOfRank(sNeighbourX, sNeighbourY, GREEN_MILITIA) > 0)
|
|
{
|
|
// great! Promote a GREEN militia guy in the neighbouring sector to a REGULAR
|
|
StrategicPromoteMilitiaInSector(sNeighbourX, sNeighbourY, GREEN_MILITIA, 1);
|
|
|
|
if (sNeighbourX == gWorldSectorX && sNeighbourY == gWorldSectorY)
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
fFoundOne = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kaiden: Veteran militia training
|
|
// This is essentially copy/pasted from above
|
|
// But the names have been changed to protect the innocent
|
|
if ((!fFoundOne) && (gGameExternalOptions.gfTrainVeteranMilitia)
|
|
&& (GetWorldDay( ) >= gGameExternalOptions.guiTrainVeteranMilitiaDelay))
|
|
{
|
|
// are there any REGULAR militia men in the training sector itself?
|
|
if (MilitiaInSectorOfRank(sMapX, sMapY, REGULAR_MILITIA) > 0)
|
|
{
|
|
// great! Promote a REGULAR militia guy in the training sector to a VETERAN
|
|
StrategicPromoteMilitiaInSector(sMapX, sMapY, REGULAR_MILITIA, 1);
|
|
if (sMapX == gWorldSectorX && sMapY == gWorldSectorY)
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
fFoundOne = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if( ubTownId != BLANK_SECTOR )
|
|
{
|
|
// dammit! Last chance - try to find other eligible sectors in the same town with a Regular guy to be promoted
|
|
InitFriendlyTownSectorServer(ubTownId, sMapX, sMapY);
|
|
|
|
// check other eligible sectors in this town for room for another militia
|
|
while( ServeNextFriendlySectorInTown( &sNeighbourX, &sNeighbourY ) )
|
|
{
|
|
// are there any REGULAR militia men in the neighbouring sector ?
|
|
if (MilitiaInSectorOfRank(sNeighbourX, sNeighbourY, REGULAR_MILITIA) > 0)
|
|
{
|
|
// great! Promote a Regular militia guy in the neighbouring sector to a Veteran
|
|
StrategicPromoteMilitiaInSector(sNeighbourX, sNeighbourY, REGULAR_MILITIA, 1);
|
|
|
|
if (sNeighbourX == gWorldSectorX && sNeighbourY == gWorldSectorY)
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
fFoundOne = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we still haven't been able to train anyone
|
|
if (!fFoundOne)
|
|
{
|
|
// Well, that's it. All eligible sectors of this town are full of REGULARs or ELITEs.
|
|
// The training goes to waste in this situation.
|
|
break; // the main while loop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// next, please!
|
|
ubMilitiaTrained++;
|
|
}
|
|
|
|
if (gfStrategicMilitiaChangesMade)
|
|
{
|
|
ResetMilitia();
|
|
}
|
|
|
|
// if anyone actually got trained
|
|
if (ubMilitiaTrained > 0)
|
|
{
|
|
// update the screen display
|
|
fMapPanelDirty = TRUE;
|
|
|
|
if( ubTownId != BLANK_SECTOR )
|
|
{
|
|
// HEADROCK HAM 3.6: Adjust loyalty based on how many militia were trained, compared to
|
|
// how many can be trained (max).
|
|
UINT32 uiTownLoyaltyBonus = (LOYALTY_BONUS_FOR_TOWN_TRAINING * ubMilitiaTrained) / gGameExternalOptions.iTrainingSquadSize;
|
|
// loyalty in this town increases a bit because we obviously care about them...
|
|
IncrementTownLoyalty( ubTownId, uiTownLoyaltyBonus );
|
|
}
|
|
|
|
// SANDRO - merc records (num militia trained)
|
|
RecordNumMilitiaTrainedForMercs( sMapX, sMapY, pTrainer->bSectorZ, ubMilitiaTrained, FALSE );
|
|
}
|
|
|
|
// the trainer announces to player that he's finished his assignment. Make his sector flash!
|
|
AssignmentDone( pTrainer, TRUE, FALSE );
|
|
|
|
// handle completion of town by training group
|
|
HandleCompletionOfTownTrainingByGroupWithTrainer( pTrainer, TOWN_MILITIA );
|
|
}
|
|
}
|
|
|
|
|
|
// feed this a SOLDIER_CLASS_, it will return you a _MITILIA rank, or -1 if the guy's not militia
|
|
INT8 SoldierClassToMilitiaRank(UINT8 ubSoldierClass)
|
|
{
|
|
INT8 bRank = -1;
|
|
|
|
switch( ubSoldierClass )
|
|
{
|
|
case SOLDIER_CLASS_GREEN_MILITIA:
|
|
bRank = GREEN_MILITIA;
|
|
break;
|
|
case SOLDIER_CLASS_REG_MILITIA:
|
|
bRank = REGULAR_MILITIA;
|
|
break;
|
|
case SOLDIER_CLASS_ELITE_MILITIA:
|
|
bRank = ELITE_MILITIA;
|
|
break;
|
|
}
|
|
|
|
return(bRank);
|
|
}
|
|
|
|
|
|
// feed this a _MITILIA rank, it will return you a SOLDIER_CLASS_, or -1 if the guy's not militia
|
|
INT8 MilitiaRankToSoldierClass(UINT8 ubRank)
|
|
{
|
|
INT8 bSoldierClass = -1;
|
|
|
|
switch( ubRank )
|
|
{
|
|
case GREEN_MILITIA:
|
|
bSoldierClass = SOLDIER_CLASS_GREEN_MILITIA;
|
|
break;
|
|
case REGULAR_MILITIA:
|
|
bSoldierClass = SOLDIER_CLASS_REG_MILITIA;
|
|
break;
|
|
case ELITE_MILITIA:
|
|
bSoldierClass = SOLDIER_CLASS_ELITE_MILITIA;
|
|
break;
|
|
}
|
|
|
|
return(bSoldierClass);
|
|
}
|
|
|
|
|
|
void StrategicAddMilitiaToSector(INT16 sMapX, INT16 sMapY, UINT8 ubRank, UINT8 ubHowMany)
|
|
{
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] );
|
|
|
|
pSectorInfo->ubNumberOfCivsAtLevel[ ubRank ] += ubHowMany;
|
|
|
|
if (ubHowMany && sMapX == gWorldSectorX && sMapY == gWorldSectorY )
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
// update the screen display
|
|
fMapPanelDirty = TRUE;
|
|
}
|
|
|
|
|
|
void StrategicPromoteMilitiaInSector(INT16 sMapX, INT16 sMapY, UINT8 ubCurrentRank, UINT8 ubHowMany)
|
|
{
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] );
|
|
|
|
// damn well better have that many around to promote!
|
|
//Assert(pSectorInfo->ubNumberOfCivsAtLevel[ ubCurrentRank ] >= ubHowMany);
|
|
|
|
//KM : July 21, 1999 patch fix
|
|
if( pSectorInfo->ubNumberOfCivsAtLevel[ ubCurrentRank ] < ubHowMany )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pSectorInfo->ubNumberOfCivsAtLevel[ ubCurrentRank ] -= ubHowMany;
|
|
pSectorInfo->ubNumberOfCivsAtLevel[ ubCurrentRank + 1 ] += ubHowMany;
|
|
|
|
if (ubHowMany && sMapX == gWorldSectorX && sMapY == gWorldSectorY )
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
// update the screen display
|
|
fMapPanelDirty = TRUE;
|
|
}
|
|
|
|
|
|
void StrategicRemoveMilitiaFromSector(INT16 sMapX, INT16 sMapY, UINT8 ubRank, UINT8 ubHowMany)
|
|
{
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] );
|
|
|
|
// WANNE: BUGZILLA #357: Fixed assertion when miltia dies.
|
|
// damn well better have that many around to remove!
|
|
//Assert(pSectorInfo->ubNumberOfCivsAtLevel[ ubRank ] >= ubHowMany);
|
|
|
|
//KM : July 21, 1999 patch fix
|
|
if( pSectorInfo->ubNumberOfCivsAtLevel[ ubRank ] < ubHowMany )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pSectorInfo->ubNumberOfCivsAtLevel[ ubRank ] -= ubHowMany;
|
|
|
|
if (ubHowMany && sMapX == gWorldSectorX && sMapY == gWorldSectorY )
|
|
{
|
|
gfStrategicMilitiaChangesMade = TRUE;
|
|
}
|
|
|
|
// update the screen display
|
|
fMapPanelDirty = TRUE;
|
|
}
|
|
|
|
|
|
// kill pts are (2 * kills) + assists
|
|
UINT8 CheckOneMilitiaForPromotion(INT16 sMapX, INT16 sMapY, UINT8 ubCurrentRank, UINT8 ubRecentKillPts)
|
|
{
|
|
UINT32 uiChanceToLevel = 0;
|
|
|
|
switch( ubCurrentRank )
|
|
{
|
|
case GREEN_MILITIA:
|
|
// 2 kill pts minimum
|
|
if (ubRecentKillPts >= 2)
|
|
{
|
|
// 25% chance per kill pt
|
|
uiChanceToLevel = 25 * ubRecentKillPts;
|
|
}
|
|
break;
|
|
case REGULAR_MILITIA:
|
|
// 5 kill pts minimum
|
|
if (ubRecentKillPts >= 5)
|
|
{
|
|
// 10% chance per kill pt.
|
|
uiChanceToLevel = 10 * ubRecentKillPts;
|
|
}
|
|
break;
|
|
case ELITE_MILITIA:
|
|
return 0;
|
|
break;
|
|
|
|
}
|
|
// roll the bones, and see if he makes it
|
|
if (Random(100) < uiChanceToLevel)
|
|
{
|
|
StrategicPromoteMilitiaInSector(sMapX, sMapY, ubCurrentRank, 1);
|
|
if( ubCurrentRank == GREEN_MILITIA )
|
|
{ //Attempt yet another level up if sufficient points
|
|
if( ubRecentKillPts > 2 )
|
|
{
|
|
if( CheckOneMilitiaForPromotion( sMapX, sMapY, REGULAR_MILITIA, (UINT8)(ubRecentKillPts - 2) ) )
|
|
{ //success, this militia was promoted twice
|
|
return 2;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// call this if the player attacks his own militia
|
|
void HandleMilitiaDefections(INT16 sMapX, INT16 sMapY)
|
|
{
|
|
UINT8 ubRank;
|
|
UINT8 ubMilitiaCnt;
|
|
UINT8 ubCount;
|
|
UINT32 uiChanceToDefect;
|
|
|
|
for( ubRank = 0; ubRank < MAX_MILITIA_LEVELS; ubRank++ )
|
|
{
|
|
ubMilitiaCnt = MilitiaInSectorOfRank(sMapX, sMapY, ubRank);
|
|
|
|
// check each guy at each rank to see if he defects
|
|
for (ubCount = 0; ubCount < ubMilitiaCnt; ubCount++)
|
|
{
|
|
switch( ubRank )
|
|
{
|
|
case GREEN_MILITIA:
|
|
uiChanceToDefect = 50;
|
|
break;
|
|
case REGULAR_MILITIA:
|
|
uiChanceToDefect = 75;
|
|
break;
|
|
case ELITE_MILITIA:
|
|
uiChanceToDefect = 90;
|
|
break;
|
|
default:
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
|
|
// roll the bones; should I stay or should I go now? (for you music fans out there)
|
|
if (Random(100) < uiChanceToDefect)
|
|
{
|
|
//B'bye! (for you SNL fans out there)
|
|
StrategicRemoveMilitiaFromSector(sMapX, sMapY, ubRank, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UINT8 CountAllMilitiaInSector(INT16 sMapX, INT16 sMapY)
|
|
{
|
|
UINT8 ubMilitiaTotal = 0;
|
|
UINT8 ubRank;
|
|
|
|
// find out if there are any town militia in this SECTOR (don't care about other sectors in same town)
|
|
for( ubRank = 0; ubRank < MAX_MILITIA_LEVELS; ubRank++ )
|
|
{
|
|
ubMilitiaTotal += MilitiaInSectorOfRank(sMapX, sMapY, ubRank);
|
|
}
|
|
|
|
return(ubMilitiaTotal);
|
|
}
|
|
|
|
|
|
UINT8 MilitiaInSectorOfRank(INT16 sMapX, INT16 sMapY, UINT8 ubRank)
|
|
{
|
|
unsigned count = SectorInfo[ SECTOR( sMapX, sMapY ) ].ubNumberOfCivsAtLevel[ ubRank ];
|
|
return count;
|
|
}
|
|
|
|
|
|
BOOLEAN SectorOursAndPeaceful( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
|
|
{
|
|
// if this sector is currently loaded
|
|
if ( ( sMapX == gWorldSectorX ) && ( sMapY == gWorldSectorY ) && ( bMapZ == gbWorldSectorZ ) )
|
|
{
|
|
// and either there are enemies prowling this sector, or combat is in progress
|
|
if ( gTacticalStatus.fEnemyInSector || ( gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// if sector is controlled by enemies, it's not ours (duh!)
|
|
if( !bMapZ && StrategicMap[ sMapX + sMapY * MAP_WORLD_X ].fEnemyControlled == TRUE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if( NumHostilesInSector( sMapX, sMapY, bMapZ ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// safe & secure, s'far as we can tell
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
void InitFriendlyTownSectorServer(UINT8 ubTownId, INT16 sSkipSectorX, INT16 sSkipSectorY)
|
|
{
|
|
// reset globals
|
|
gubTownSectorServerTownId = ubTownId;
|
|
gsTownSectorServerSkipX = sSkipSectorX;
|
|
gsTownSectorServerSkipY = sSkipSectorY;
|
|
|
|
gubTownSectorServerIndex = 0;
|
|
}
|
|
|
|
|
|
// this feeds the X,Y of the next town sector on the town list for the town specified at initialization
|
|
// it will skip an entry that matches the skip X/Y value if one was specified at initialization
|
|
// MUST CALL InitFriendlyTownSectorServer() before using!!!
|
|
BOOLEAN ServeNextFriendlySectorInTown( INT16 *sNeighbourX, INT16 *sNeighbourY )
|
|
{
|
|
INT32 iTownSector;
|
|
INT16 sMapX, sMapY;
|
|
BOOLEAN fStopLooking = FALSE;
|
|
|
|
do
|
|
{
|
|
// have we reached the end of the town list?
|
|
if (pTownNamesList[ gubTownSectorServerIndex ] == BLANK_SECTOR)
|
|
{
|
|
// end of list reached
|
|
return(FALSE);
|
|
}
|
|
|
|
iTownSector = pTownLocationsList[ gubTownSectorServerIndex ];
|
|
|
|
// if this sector is in the town we're looking for
|
|
if( StrategicMap[ iTownSector ].bNameId == gubTownSectorServerTownId )
|
|
{
|
|
// A sector in the specified town. Calculate its X & Y sector compotents
|
|
sMapX = iTownSector % MAP_WORLD_X;
|
|
sMapY = iTownSector / MAP_WORLD_X;
|
|
|
|
// Make sure we're not supposed to skip it
|
|
if ( ( sMapX != gsTownSectorServerSkipX ) || ( sMapY != gsTownSectorServerSkipY ) )
|
|
{
|
|
// check if it's "friendly" - not enemy controlled, no enemies in it, no combat in progress
|
|
if (SectorOursAndPeaceful( sMapX, sMapY, 0 ))
|
|
{
|
|
// then that's it!
|
|
*sNeighbourX = sMapX;
|
|
*sNeighbourY = sMapY;
|
|
|
|
fStopLooking = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// advance to next entry in town list
|
|
gubTownSectorServerIndex++;
|
|
|
|
} while ( !fStopLooking );
|
|
|
|
|
|
// found & returning a valid sector
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
void HandleInterfaceMessageForCostOfTrainingMilitia( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT32 iMilitiaTrainingCost = gGameExternalOptions.iMilitiaTrainingCost;
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia2");
|
|
|
|
CHAR16 sString[ 128 ];
|
|
SGPRect pCenteringRect= {0, 0, 640, INV_INTERFACE_START_Y };
|
|
INT32 iNumberOfSectors = 0;
|
|
|
|
pMilitiaTrainerSoldier = pSoldier;
|
|
|
|
// HEADROCK HAM 3.6: Which kind of militia are we training?
|
|
UINT8 ubMilitiaType = 0;
|
|
if (pSoldier->bAssignment == TRAIN_TOWN)
|
|
ubMilitiaType = TOWN_MILITIA;
|
|
else if (pSoldier->bAssignment == TRAIN_MOBILE)
|
|
ubMilitiaType = MOBILE_MILITIA;
|
|
else
|
|
return;
|
|
// grab total number of sectors
|
|
iNumberOfSectors = GetNumberOfUnpaidTrainableSectors( ubMilitiaType );
|
|
Assert( iNumberOfSectors > 0 );
|
|
|
|
// Kaiden: Roaming Militia Training:
|
|
// We want to charge more for Roaming
|
|
|
|
// get total cost
|
|
// HEADROCK HAM 3.6: Mobile and Garrison Militia now separate
|
|
if (ubMilitiaType == TOWN_MILITIA) // Garrison
|
|
{
|
|
// If Regulars are maxed, and Elites are allowed, calculate cost to upgrade Regular->Elite
|
|
if (IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier, REGULAR_MILITIA )
|
|
&& (gGameExternalOptions.gfTrainVeteranMilitia)
|
|
&& (GetWorldDay( ) >= gGameExternalOptions.guiTrainVeteranMilitiaDelay))
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost*gGameExternalOptions.iVeteranCostModifier) * iNumberOfSectors;
|
|
Assert( giTotalCostOfTraining > 0 );
|
|
gfAreWePromotingRegular = TRUE;
|
|
}
|
|
else if (IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier, GREEN_MILITIA ))
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost*gGameExternalOptions.iRegularCostModifier) * iNumberOfSectors;
|
|
Assert( giTotalCostOfTraining > 0 );
|
|
gfAreWePromotingGreen = TRUE;
|
|
}
|
|
// Normal training.
|
|
else
|
|
{
|
|
giTotalCostOfTraining = iMilitiaTrainingCost * iNumberOfSectors;
|
|
Assert( giTotalCostOfTraining > 0 );
|
|
}
|
|
}
|
|
|
|
// Mobile
|
|
else
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost*gGameExternalOptions.iMilitiaCostModifier) * iNumberOfSectors;
|
|
Assert( giTotalCostOfTraining > 0 );
|
|
gfAreWeTrainingMobile = TRUE;
|
|
}
|
|
|
|
gfYesNoPromptIsForContinue = FALSE;
|
|
|
|
if( LaptopSaveInfo.iCurrentBalance < giTotalCostOfTraining )
|
|
{
|
|
// Different message for Mobiles
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
swprintf( sString, pMilitiaConfirmStrings[ 8 ], giTotalCostOfTraining );
|
|
DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, CantTrainMilitiaOkBoxCallback );
|
|
}
|
|
else
|
|
{
|
|
swprintf( sString, pMilitiaConfirmStrings[ 11 ], giTotalCostOfTraining );
|
|
DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, CantTrainMobileMilitiaOkBoxCallback );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ok to start training, ask player
|
|
|
|
|
|
if( iNumberOfSectors > 1 )
|
|
{
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
swprintf( sString, pMilitiaConfirmStrings[ 7 ], iNumberOfSectors, giTotalCostOfTraining, pMilitiaConfirmStrings[ 1 ] );
|
|
}
|
|
else
|
|
{
|
|
swprintf( sString, pMilitiaConfirmStrings[ 13 ], iNumberOfSectors, giTotalCostOfTraining, pMilitiaConfirmStrings[ 1 ] );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
swprintf( sString, L"%s%d. %s", pMilitiaConfirmStrings[ 0 ], giTotalCostOfTraining, pMilitiaConfirmStrings[ 1 ] );
|
|
}
|
|
else
|
|
{
|
|
swprintf( sString, L"%s%d. %s", pMilitiaConfirmStrings[ 14 ], giTotalCostOfTraining, pMilitiaConfirmStrings[ 1 ] );
|
|
}
|
|
}
|
|
|
|
// if we are in mapscreen, make a pop up
|
|
if( guiCurrentScreen == MAP_SCREEN )
|
|
{
|
|
// HEADROCK HAM 3.6: Separate callbacks for Mobile and Garrison militia
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_YESNO, PayMilitiaTrainingYesNoBoxCallback );
|
|
}
|
|
else
|
|
{
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_YESNO, PayMobileMilitiaTrainingYesNoBoxCallback );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
DoMessageBox( MSG_BOX_BASIC_STYLE, sString, GAME_SCREEN, MSG_BOX_FLAG_YESNO, PayMilitiaTrainingYesNoBoxCallback, &pCenteringRect );
|
|
}
|
|
else
|
|
{
|
|
DoMessageBox( MSG_BOX_BASIC_STYLE, sString, GAME_SCREEN, MSG_BOX_FLAG_YESNO, PayMobileMilitiaTrainingYesNoBoxCallback, &pCenteringRect );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void DoContinueMilitiaTrainingMessageBox( INT16 sSectorX, INT16 sSectorY, const STR16 str, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback )
|
|
{
|
|
if( sSectorX <= 10 && sSectorY >= 6 && sSectorY <= 11 )
|
|
{
|
|
DoLowerScreenIndependantMessageBox( str, usFlags, ReturnCallback );
|
|
}
|
|
else
|
|
{
|
|
DoScreenIndependantMessageBox( str, usFlags, ReturnCallback );
|
|
}
|
|
}
|
|
|
|
void HandleInterfaceMessageForContinuingTrainingMilitia( SOLDIERTYPE *pSoldier )
|
|
{
|
|
CHAR16 sString[ 128 ];
|
|
INT16 sSectorX = 0, sSectorY = 0;
|
|
CHAR16 sStringB[ 128 ];
|
|
INT8 bTownId;
|
|
BOOLEAN fIsFull = FALSE;
|
|
INT32 iCounter = 0;
|
|
|
|
INT32 iMinLoyaltyToTrain = gGameExternalOptions.iMinLoyaltyToTrain;
|
|
INT32 iMilitiaTrainingCost = gGameExternalOptions.iMilitiaTrainingCost;
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia3");
|
|
|
|
sSectorX = pSoldier->sSectorX;
|
|
sSectorY = pSoldier->sSectorY;
|
|
|
|
bTownId = GetTownIdForSector( sSectorX, sSectorY );
|
|
|
|
UINT8 ubMilitiaType = 0;
|
|
if (pSoldier->bAssignment == TRAIN_TOWN)
|
|
{
|
|
ubMilitiaType = TOWN_MILITIA;
|
|
}
|
|
else if (pSoldier->bAssignment == TRAIN_MOBILE )
|
|
{
|
|
ubMilitiaType = MOBILE_MILITIA;
|
|
}
|
|
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
Assert( SectorInfo[ SECTOR( sSectorX, sSectorY ) ].fMilitiaTrainingPaid == FALSE );
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
Assert( SectorInfo[ SECTOR( sSectorX, sSectorY ) ].fMobileMilitiaTrainingPaid == FALSE );
|
|
}
|
|
|
|
pMilitiaTrainerSoldier = pSoldier;
|
|
|
|
gfYesNoPromptIsForContinue = TRUE;
|
|
|
|
// is there enough loyalty to continue training
|
|
if( DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( pSoldier ) == FALSE )
|
|
{
|
|
// loyalty too low to continue training
|
|
swprintf( sString, pMilitiaConfirmStrings[ 9 ], pTownNames[ GetTownIdForSector( sSectorX, sSectorY )], iMinLoyaltyToTrain );
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMilitiaOkBoxCallback );
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMobileMilitiaOkBoxCallback );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Training town militia and town is full of Elites (or Regulars, if Elites are disabled)
|
|
if (ubMilitiaType == TOWN_MILITIA && IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier, ELITE_MILITIA ))
|
|
{
|
|
// we're full!!! go home!
|
|
if ( bTownId == BLANK_SECTOR )
|
|
{
|
|
// wilderness SAM site
|
|
GetSectorIDString( sSectorX, sSectorY, 0, sStringB, TRUE );
|
|
void (*tempFptr)(INT16 , INT16 , INT8 , CHAR16 *, BOOLEAN) = GetSectorIDString;
|
|
swprintf( sString, pMilitiaConfirmStrings[ 10 ], sStringB, tempFptr, iMinLoyaltyToTrain );
|
|
}
|
|
else
|
|
{
|
|
// town
|
|
swprintf( sString, pMilitiaConfirmStrings[ 10 ], pTownNames[ bTownId ], iMinLoyaltyToTrain );
|
|
}
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMilitiaOkBoxCallback );
|
|
return;
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
// Test to see if not enough Town Militia
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR(pSoldier->sSectorX, pSoldier->sSectorY) ] );
|
|
BOOLEAN fUnfullSectorFound = FALSE;
|
|
|
|
// Test to see if not enough Town Militia
|
|
// HEADROCK HAM 4: No need for this check anymore. Militia can now be trained separately thanks to being
|
|
// able to keep them out of cities.
|
|
/*
|
|
|
|
if ( CountMilitia( pSectorInfo ) < gGameExternalOptions.iMaxMilitiaPerSector )
|
|
{
|
|
fUnfullSectorFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
iCounter = 0;
|
|
while( pTownNamesList[ iCounter ] != 0 )
|
|
{
|
|
// Are we in this city?
|
|
if( pTownNamesList[ iCounter] == bTownId )
|
|
{
|
|
INT16 sCurrentX = GET_X_FROM_STRATEGIC_INDEX( pTownLocationsList[ iCounter ] );
|
|
INT16 sCurrentY = GET_Y_FROM_STRATEGIC_INDEX( pTownLocationsList[ iCounter ] );
|
|
|
|
pSectorInfo = &( SectorInfo[ SECTOR(sCurrentX, sCurrentY) ] );
|
|
// if sector has enemies or hasn't already been taken at least once, then
|
|
if ( !SectorInfo[ SECTOR(sCurrentX, sCurrentY) ].fSurfaceWasEverPlayerControlled ||
|
|
NumEnemiesInSector( sCurrentX, sCurrentY ) > 0 )
|
|
{
|
|
// skip the rest. This sector cannot generate militia anyway.
|
|
iCounter++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (CountMilitia(pSectorInfo) < gGameExternalOptions.iMaxMilitiaPerSector )
|
|
{
|
|
// Found a controlled city sector that does not yet have a full garrison
|
|
fUnfullSectorFound;
|
|
}
|
|
}
|
|
}
|
|
iCounter++;
|
|
}
|
|
}
|
|
if (fUnfullSectorFound)
|
|
{
|
|
// At least one city sector is controlled but not full of garrison militia. Can't train mobiles!
|
|
swprintf(sString, New113HAMMessage[9], gGameExternalOptions.iMaxMilitiaPerSector, pTownNames[bTownId]);
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMobileMilitiaOkBoxCallback );
|
|
return;
|
|
}
|
|
*/
|
|
|
|
//////////////////////////////////////////
|
|
// Capacity check in nearby sectors
|
|
UINT16 pMoveDir[4][3];
|
|
UINT8 uiDirNumber = 0;
|
|
BOOLEAN fFoundValidSector = FALSE;
|
|
|
|
|
|
INT8 bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY );
|
|
GenerateDirectionInfosForTraining( pSoldier->sSectorX, pSoldier->sSectorY, &uiDirNumber, pMoveDir );
|
|
// Found at least one suitable place to put Mobiles?
|
|
if (uiDirNumber)
|
|
{
|
|
fFoundValidSector = TRUE;
|
|
}
|
|
// Try entire city.
|
|
else
|
|
{
|
|
INT32 iCounter = 0;
|
|
|
|
// Go through each city in the game
|
|
while( pTownNamesList[ iCounter ] != 0 )
|
|
{
|
|
// Are we in this city?
|
|
if( pTownNamesList[ iCounter] == bTownId )
|
|
{
|
|
INT16 sCurrentX = GET_X_FROM_STRATEGIC_INDEX( pTownLocationsList[ iCounter ] );
|
|
INT16 sCurrentY = GET_Y_FROM_STRATEGIC_INDEX( pTownLocationsList[ iCounter ] );
|
|
|
|
// if sector has enemies or hasn't already been taken at least once, then
|
|
if ( !SectorInfo[ SECTOR(sCurrentX, sCurrentY) ].fSurfaceWasEverPlayerControlled ||
|
|
NumEnemiesInSector( sCurrentX, sCurrentY ) > 0 )
|
|
{
|
|
// skip the rest. This sector cannot generate militia anyway.
|
|
iCounter++;
|
|
continue;
|
|
}
|
|
|
|
// Find out if any adjacent sectors have room in them.
|
|
GenerateDirectionInfosForTraining( sCurrentX, sCurrentY, &uiDirNumber, pMoveDir );
|
|
|
|
if(uiDirNumber)
|
|
{
|
|
fFoundValidSector = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
iCounter++;
|
|
}
|
|
}
|
|
|
|
// Couldn't find at least one sector to place Mobiles. Report "No room!"
|
|
if (!fFoundValidSector)
|
|
{
|
|
swprintf( sString, New113HAMMessage[ 8 ], pTownNames[ bTownId ], iMinLoyaltyToTrain );
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMobileMilitiaOkBoxCallback );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Kaiden: Roaming Militia Training:
|
|
// Charging more to train Roaming Militia
|
|
// Also Charging more for promotions over Training
|
|
|
|
if( ubMilitiaType == MOBILE_MILITIA )
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost*gGameExternalOptions.iMilitiaCostModifier);
|
|
gfAreWeTrainingMobile = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier, REGULAR_MILITIA )
|
|
&& (gGameExternalOptions.gfTrainVeteranMilitia)
|
|
&& (GetWorldDay( ) >= gGameExternalOptions.guiTrainVeteranMilitiaDelay))
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost*gGameExternalOptions.iVeteranCostModifier);
|
|
gfAreWePromotingRegular = TRUE;
|
|
}
|
|
else if (IsMilitiaTrainableFromSoldiersSectorMaxed( pSoldier, GREEN_MILITIA ))
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost*gGameExternalOptions.iRegularCostModifier);
|
|
gfAreWePromotingGreen = TRUE;
|
|
}
|
|
else
|
|
{
|
|
giTotalCostOfTraining = (iMilitiaTrainingCost);
|
|
}
|
|
}
|
|
|
|
// can player afford to continue training?
|
|
if( LaptopSaveInfo.iCurrentBalance < giTotalCostOfTraining )
|
|
{
|
|
// can't afford to continue training
|
|
swprintf( sString, pMilitiaConfirmStrings[ 8 ], giTotalCostOfTraining );
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMilitiaOkBoxCallback );
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_OK, CantTrainMobileMilitiaOkBoxCallback );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ok to continue, ask player
|
|
|
|
GetSectorIDString( sSectorX, sSectorY, 0, sStringB, TRUE );
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
swprintf( sString, pMilitiaConfirmStrings[ 3 ], sStringB, pMilitiaConfirmStrings[ 4 ], giTotalCostOfTraining );
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
swprintf( sString, pMilitiaConfirmStrings[ 12 ], sStringB, pMilitiaConfirmStrings[ 4 ], giTotalCostOfTraining );
|
|
}
|
|
|
|
// ask player whether he'd like to continue training
|
|
//DoContinueMilitiaTrainingMessageBox( sSectorX, sSectorY, sString, MSG_BOX_FLAG_YESNO, PayMilitiaTrainingYesNoBoxCallback );
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_YESNO, PayMilitiaTrainingYesNoBoxCallback );
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_YESNO, PayMobileMilitiaTrainingYesNoBoxCallback );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// IMPORTANT: This same callback is used both for initial training and for continue training prompt
|
|
// use 'gfYesNoPromptIsForContinue' flag to tell them apart
|
|
void PayMilitiaTrainingYesNoBoxCallback( UINT8 bExitValue )
|
|
{
|
|
CHAR16 sString[ 128 ];
|
|
|
|
Assert( giTotalCostOfTraining > 0 );
|
|
|
|
// yes
|
|
if( bExitValue == MSG_BOX_RETURN_YES )
|
|
{
|
|
// does the player have enough
|
|
if( LaptopSaveInfo.iCurrentBalance >= giTotalCostOfTraining )
|
|
{
|
|
if( gfYesNoPromptIsForContinue )
|
|
{
|
|
ContinueTrainingInThisSector( TOWN_MILITIA );
|
|
}
|
|
else
|
|
{
|
|
StartTrainingInAllUnpaidTrainableSectors( TOWN_MILITIA );
|
|
}
|
|
|
|
#ifdef JA2BETAVERSION
|
|
// put this BEFORE training gets handled to avoid detecting an error everytime a sector completes training
|
|
VerifyTownTrainingIsPaidFor();
|
|
#endif
|
|
|
|
// this completes the training prompt sequence
|
|
pMilitiaTrainerSoldier = NULL;
|
|
}
|
|
else // can't afford it
|
|
{
|
|
StopTimeCompression();
|
|
|
|
swprintf( sString, L"%s", pMilitiaConfirmStrings[ 2 ] );
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, CantTrainMilitiaOkBoxCallback );
|
|
}
|
|
}
|
|
else if( bExitValue == MSG_BOX_RETURN_NO )
|
|
{
|
|
StopTimeCompression();
|
|
|
|
MilitiaTrainingRejected( TOWN_MILITIA );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void CantTrainMilitiaOkBoxCallback( UINT8 bExitValue )
|
|
{
|
|
MilitiaTrainingRejected( TOWN_MILITIA );
|
|
return;
|
|
}
|
|
|
|
// HEADROCK HAM 3.6: Duplicate of the above, for Mobile Militia purposes.
|
|
void CantTrainMobileMilitiaOkBoxCallback( UINT8 bExitValue )
|
|
{
|
|
MilitiaTrainingRejected( MOBILE_MILITIA );
|
|
return;
|
|
}
|
|
|
|
// IMPORTANT: This same callback is used both for initial training and for continue training prompt
|
|
// use 'gfYesNoPromptIsForContinue' flag to tell them apart
|
|
void MilitiaTrainingRejected( UINT8 ubMilitiaType )
|
|
{
|
|
if( gfYesNoPromptIsForContinue )
|
|
{
|
|
// take all mercs in that sector off militia training
|
|
ResetAssignmentOfMercsThatWereTrainingMilitiaInThisSector( pMilitiaTrainerSoldier->sSectorX, pMilitiaTrainerSoldier->sSectorY, ubMilitiaType );
|
|
}
|
|
else
|
|
{
|
|
// take all mercs in unpaid sectors EVERYWHERE off militia training
|
|
ResetAssignmentsForMercsTrainingUnpaidSectorsInSelectedList( ubMilitiaType );
|
|
}
|
|
|
|
#ifdef JA2BETAVERSION
|
|
// put this BEFORE training gets handled to avoid detecting an error everytime a sector completes training
|
|
// HEADROCK HAM 3.6: Checks Mobiles and Garrisons
|
|
VerifyTownTrainingIsPaidFor();
|
|
#endif
|
|
|
|
// this completes the training prompt sequence
|
|
pMilitiaTrainerSoldier = NULL;
|
|
}
|
|
|
|
|
|
|
|
void HandleMilitiaStatusInCurrentMapBeforeLoadingNewMap( void )
|
|
{
|
|
if ( gTacticalStatus.Team[ MILITIA_TEAM ].bSide != 0 )
|
|
{
|
|
// handle militia defections and reset team to friendly
|
|
HandleMilitiaDefections( gWorldSectorX, gWorldSectorY );
|
|
gTacticalStatus.Team[ MILITIA_TEAM ].bSide = 0;
|
|
}
|
|
else if( !gfAutomaticallyStartAutoResolve )
|
|
{ //Don't promote militia if we are going directly to autoresolve to finish the current battle.
|
|
HandleMilitiaPromotions();
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN CanSomeoneNearbyScoutThisSector( INT16 sSectorX, INT16 sSectorY, BOOLEAN fScoutTraitCheck ) // added argument - SANDRO
|
|
{
|
|
INT16 sSectorValue = 0, sSector = 0;
|
|
INT16 sCounterA = 0, sCounterB = 0;
|
|
UINT8 ubScoutingRange = 1;
|
|
|
|
|
|
// get the sector value
|
|
sSector = sSectorX + sSectorY * MAP_WORLD_X;
|
|
|
|
for( sCounterA = sSectorX - ubScoutingRange; sCounterA <= sSectorX + ubScoutingRange; sCounterA++ )
|
|
{
|
|
for( sCounterB = sSectorY - ubScoutingRange; sCounterB <= sSectorY + ubScoutingRange; sCounterB++ )
|
|
{
|
|
// skip out of bounds sectors
|
|
if ( ( sCounterA < 1 ) || ( sCounterA > 16 ) || ( sCounterB < 1 ) || ( sCounterB > 16 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sSectorValue = SECTOR( sCounterA, sCounterB );
|
|
|
|
// SANDRO - STOMP traits - Scouting check
|
|
if (fScoutTraitCheck && gGameOptions.fNewTraitSystem && ScoutIsPresentInSquad( sCounterA, sCounterB ))
|
|
{
|
|
// if diagonal sector and not allowed
|
|
if (!gSkillTraitValues.fSCDetectionInDiagonalSectors &&
|
|
((sCounterA - 1 == sSectorX && sCounterB + 1 == sSectorY) ||
|
|
(sCounterA - 1 == sSectorX && sCounterB - 1 == sSectorY) ||
|
|
(sCounterA + 1 == sSectorX && sCounterB + 1 == sSectorY) ||
|
|
(sCounterA + 1 == sSectorX && sCounterB - 1 == sSectorY)))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
else
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
// check if any sort of militia here
|
|
if( SectorInfo[ sSectorValue ].ubNumberOfCivsAtLevel[ GREEN_MILITIA ] )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else if( SectorInfo[ sSectorValue ].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else if( SectorInfo[ sSectorValue ].ubNumberOfCivsAtLevel[ ELITE_MILITIA ] )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN IsTownFullMilitia( INT8 bTownId, INT8 iMilitiaType )
|
|
{
|
|
INT32 iMaxMilitiaPerSector = gGameExternalOptions.iMaxMilitiaPerSector;
|
|
|
|
INT32 iCounter = 0;
|
|
INT16 sSectorX = 0, sSectorY = 0;
|
|
INT32 iNumberOfMilitia = 0;
|
|
INT32 iMaxNumber = 0;
|
|
BOOLEAN fIncreaseCost = FALSE;
|
|
|
|
while( pTownNamesList[ iCounter ] != 0 )
|
|
{
|
|
if( pTownNamesList[ iCounter ] == bTownId )
|
|
{
|
|
// get the sector x and y
|
|
sSectorX = pTownLocationsList[ iCounter ] % MAP_WORLD_X;
|
|
sSectorY = pTownLocationsList[ iCounter ] / MAP_WORLD_X;
|
|
|
|
// if sector is ours get number of militia here
|
|
if( SectorOursAndPeaceful( sSectorX, sSectorY, 0 ) )
|
|
{
|
|
|
|
//Kaiden: Checking for Price Hikes.
|
|
if (iMilitiaType == GREEN_MILITIA)
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, GREEN_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
iMaxNumber += iMaxMilitiaPerSector;
|
|
|
|
if (MilitiaInSectorOfRank( sSectorX, sSectorY, GREEN_MILITIA ) > 0)
|
|
fIncreaseCost = TRUE;
|
|
}
|
|
else if (iMilitiaType == REGULAR_MILITIA)
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
iMaxNumber += iMaxMilitiaPerSector;
|
|
|
|
if (MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA ) > 0)
|
|
fIncreaseCost = TRUE;
|
|
}
|
|
|
|
//Kaiden: OK So iMilitiaType should be ELITE_MILITIA
|
|
// and we're just checking sectors for purposes
|
|
// of training mobile militia or ending training
|
|
|
|
// don't count GREEN militia, they can be trained into regulars first
|
|
// Kaiden: Veteran Militia Training:
|
|
// And we don't count regulars either if we're training veterans
|
|
if (gGameExternalOptions.gfTrainVeteranMilitia && (iMilitiaType == ELITE_MILITIA)
|
|
&& (GetWorldDay( ) >= gGameExternalOptions.guiTrainVeteranMilitiaDelay))
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
iMaxNumber += iMaxMilitiaPerSector;
|
|
}
|
|
else if (iMilitiaType == ELITE_MILITIA)
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
iMaxNumber += iMaxMilitiaPerSector;
|
|
}
|
|
}
|
|
}
|
|
|
|
iCounter++;
|
|
}
|
|
|
|
if (iMilitiaType == GREEN_MILITIA)
|
|
{
|
|
if (( iNumberOfMilitia == iMaxNumber ) && (fIncreaseCost))
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
|
|
}
|
|
else if (iMilitiaType == REGULAR_MILITIA)
|
|
{
|
|
if (( iNumberOfMilitia == iMaxNumber ) && (fIncreaseCost))
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
// now check the number of militia
|
|
if ( iMaxNumber > iNumberOfMilitia )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN IsSAMSiteFullOfMilitia( INT16 sSectorX, INT16 sSectorY, INT8 iMilitiaType )
|
|
{
|
|
BOOLEAN fSamSitePresent = FALSE;
|
|
INT32 iNumberOfMilitia = 0;
|
|
INT32 iMaxNumber = 0;
|
|
INT32 iMaxMilitiaPerSector = gGameExternalOptions.iMaxMilitiaPerSector;
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia5");
|
|
// check if SAM site is ours?
|
|
fSamSitePresent = IsThisSectorASAMSector( sSectorX, sSectorY, 0 );
|
|
|
|
if( fSamSitePresent == FALSE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if( SectorOursAndPeaceful( sSectorX, sSectorY, 0 ) )
|
|
{
|
|
|
|
//Kaiden: If we're checking for specific Militia for Price Hikes:
|
|
if (iMilitiaType == GREEN_MILITIA)
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, GREEN_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
|
|
if (( iNumberOfMilitia == iMaxMilitiaPerSector) &&
|
|
(MilitiaInSectorOfRank( sSectorX, sSectorY, GREEN_MILITIA ) > 0))
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
|
|
}
|
|
else if (iMilitiaType == REGULAR_MILITIA)
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
|
|
if (( iNumberOfMilitia == iMaxMilitiaPerSector) &&
|
|
(MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA ) > 0))
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
}
|
|
// Kaiden: Ok we're not checking for militia promotions now,
|
|
// so MilitiaType should be ELITE_MILITIA for purposes
|
|
// of training Roaming or not.
|
|
|
|
// don't count GREEN militia, they can be trained into regulars first
|
|
|
|
// Kaiden: Veteran Militia Training:
|
|
// And we don't count regulars either if we're training veterans
|
|
if (gGameExternalOptions.gfTrainVeteranMilitia && (iMilitiaType == ELITE_MILITIA)
|
|
&& (GetWorldDay( ) >= gGameExternalOptions.guiTrainVeteranMilitiaDelay))
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
iMaxNumber += iMaxMilitiaPerSector;
|
|
}
|
|
else if (iMilitiaType == ELITE_MILITIA)
|
|
{
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, REGULAR_MILITIA );
|
|
iNumberOfMilitia += MilitiaInSectorOfRank( sSectorX, sSectorY, ELITE_MILITIA );
|
|
iMaxNumber += iMaxMilitiaPerSector;
|
|
}
|
|
}
|
|
|
|
|
|
// now check the number of militia
|
|
if ( iMaxNumber > iNumberOfMilitia )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void HandleCompletionOfTownTrainingByGroupWithTrainer( SOLDIERTYPE *pTrainer, UINT8 ubMilitiaType )
|
|
{
|
|
|
|
INT16 sSectorX = 0, sSectorY = 0;
|
|
INT8 bSectorZ = 0;
|
|
SOLDIERTYPE *pSoldier = NULL;
|
|
INT32 iCounter = 0;
|
|
|
|
|
|
// get the sector values
|
|
sSectorX = pTrainer->sSectorX;
|
|
sSectorY = pTrainer->sSectorY;
|
|
bSectorZ = pTrainer->bSectorZ;
|
|
|
|
for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ )
|
|
{
|
|
// valid character?
|
|
if( gCharactersList[ iCounter ].fValid == FALSE )
|
|
{
|
|
// nope
|
|
continue;
|
|
}
|
|
|
|
pSoldier = &Menptr[ gCharactersList[ iCounter ].usSolID ];
|
|
|
|
// valid soldier?
|
|
if( pSoldier->bActive == FALSE )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// HEADROCK HAM 3.6: Separated Mobiles from Garrisons.
|
|
if ( ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
if( ( pSoldier->bAssignment == TRAIN_TOWN ) && ( pSoldier->sSectorX == sSectorX )&&( pSoldier->sSectorY == sSectorY )&&( pSoldier->bSectorZ == bSectorZ ) )
|
|
{
|
|
// done assignment
|
|
AssignmentDone( pSoldier, FALSE, FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ( pSoldier->bAssignment == TRAIN_MOBILE ) && ( pSoldier->sSectorX == sSectorX )&&( pSoldier->sSectorY == sSectorY )&&( pSoldier->bSectorZ == bSectorZ ) )
|
|
{
|
|
// done assignment
|
|
AssignmentDone( pSoldier, FALSE, FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
void AddSectorForSoldierToListOfSectorsThatCompletedMilitiaTraining( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT32 iCounter = 0;
|
|
INT16 sSector = 0, sCurrentSector = 0;
|
|
SOLDIERTYPE *pCurrentSoldier = NULL;
|
|
|
|
// get the sector value
|
|
sSector = pSoldier->sSectorX + pSoldier->sSectorY * MAP_WORLD_X;
|
|
|
|
while( giListOfMercsInSectorsCompletedMilitiaTraining[ iCounter ] != -1 )
|
|
{
|
|
// get the current soldier
|
|
pCurrentSoldier = &Menptr[ giListOfMercsInSectorsCompletedMilitiaTraining[ iCounter ] ];
|
|
|
|
// get the current sector value
|
|
sCurrentSector = pCurrentSoldier->sSectorX + pCurrentSoldier->sSectorY * MAP_WORLD_X;
|
|
|
|
// is the merc's sector already in the list?
|
|
if( sCurrentSector == sSector )
|
|
{
|
|
// already here
|
|
return;
|
|
}
|
|
|
|
iCounter++;
|
|
|
|
Assert( iCounter < SIZE_OF_MILITIA_COMPLETED_TRAINING_LIST );
|
|
}
|
|
|
|
// add merc to the list
|
|
giListOfMercsInSectorsCompletedMilitiaTraining[ iCounter ] = pSoldier->ubID;
|
|
|
|
return;
|
|
}
|
|
|
|
// clear out the list of training sectors...should be done once the list is posted
|
|
void ClearSectorListForCompletedTrainingOfMilitia( void )
|
|
{
|
|
INT32 iCounter = 0;
|
|
|
|
for( iCounter = 0; iCounter < SIZE_OF_MILITIA_COMPLETED_TRAINING_LIST; iCounter++ )
|
|
{
|
|
giListOfMercsInSectorsCompletedMilitiaTraining[ iCounter ] = -1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void HandleContinueOfTownTraining( void )
|
|
{
|
|
SOLDIERTYPE *pSoldier = NULL;
|
|
INT32 iCounter = 0;
|
|
BOOLEAN fContinueEventPosted = FALSE;
|
|
|
|
|
|
while( giListOfMercsInSectorsCompletedMilitiaTraining[ iCounter ] != -1 )
|
|
{
|
|
// get the soldier
|
|
pSoldier = &Menptr[ giListOfMercsInSectorsCompletedMilitiaTraining[ iCounter ] ];
|
|
|
|
if( pSoldier->bActive )
|
|
{
|
|
fContinueEventPosted = TRUE;
|
|
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_CONTINUE_TRAINING_MILITIA, pSoldier->ubProfile, 0, 0, 0, 0 );
|
|
|
|
// now set all of these peoples assignment done too
|
|
//HandleInterfaceMessageForContinuingTrainingMilitia( pSoldier );
|
|
}
|
|
|
|
// next entry
|
|
iCounter++;
|
|
}
|
|
|
|
// now clear the list
|
|
ClearSectorListForCompletedTrainingOfMilitia( );
|
|
|
|
if( fContinueEventPosted )
|
|
{
|
|
// ATE: If this event happens in tactical mode we will be switching at some time to mapscreen...
|
|
if ( guiCurrentScreen == GAME_SCREEN )
|
|
{
|
|
gfEnteringMapScreen = TRUE;
|
|
}
|
|
|
|
//If the militia view isn't currently active, then turn it on when prompting to continue training.
|
|
if ( !fShowMilitia )
|
|
{
|
|
ToggleShowMilitiaMode();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void BuildListOfUnpaidTrainableSectors( UINT8 ubMilitiaType )
|
|
{
|
|
INT32 iCounter = 0, iCounterB = 0;
|
|
SOLDIERTYPE *pSoldier = NULL;
|
|
|
|
// WDS - make number of mercenaries, etc. be configurable
|
|
memset( gsUnpaidStrategicSector, 0, sizeof(gsUnpaidStrategicSector) );
|
|
|
|
if( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN )
|
|
{
|
|
for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ )
|
|
{
|
|
// valid character?
|
|
if( gCharactersList[ iCounter ].fValid )
|
|
{
|
|
// selected?
|
|
if( ( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) || ( iCounter == bSelectedAssignChar ) )
|
|
{
|
|
pSoldier = &Menptr[ gCharactersList[ iCounter ].usSolID ];
|
|
|
|
// HEADROCK HAM 3.6: Two different conditions depending on the type of militia being trained.
|
|
if( ubMilitiaType == TOWN_MILITIA )
|
|
{
|
|
if (CanCharacterTrainMilitia( pSoldier ) == TRUE )
|
|
{
|
|
if (SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMilitiaTrainingPaid == FALSE)
|
|
{
|
|
gsUnpaidStrategicSector[ iCounter ] = CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CanCharacterTrainMobileMilitia( pSoldier ) == TRUE )
|
|
{
|
|
if (SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMobileMilitiaTrainingPaid == FALSE)
|
|
{
|
|
gsUnpaidStrategicSector[ iCounter ] = CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// handle for tactical
|
|
pSoldier = &Menptr[ gusUIFullTargetID ];
|
|
iCounter = 0;
|
|
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
if (CanCharacterTrainMilitia( pSoldier ) == TRUE )
|
|
{
|
|
if (SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMilitiaTrainingPaid == FALSE )
|
|
{
|
|
gsUnpaidStrategicSector[ iCounter ] = CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CanCharacterTrainMobileMilitia( pSoldier ) == TRUE )
|
|
{
|
|
if (SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMobileMilitiaTrainingPaid == FALSE )
|
|
{
|
|
gsUnpaidStrategicSector[ iCounter ] = CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now clean out repeated sectors
|
|
for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1; iCounter++ )
|
|
{
|
|
if( gsUnpaidStrategicSector[ iCounter ] > 0 )
|
|
{
|
|
for( iCounterB = iCounter + 1 ; iCounterB < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounterB++ )
|
|
{
|
|
if( gsUnpaidStrategicSector[ iCounterB ] == gsUnpaidStrategicSector[ iCounter ] )
|
|
{
|
|
gsUnpaidStrategicSector[ iCounterB ] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
INT32 GetNumberOfUnpaidTrainableSectors( UINT8 ubMilitiaType )
|
|
{
|
|
INT32 iCounter = 0;
|
|
INT32 iNumberOfSectors = 0;
|
|
|
|
BuildListOfUnpaidTrainableSectors( ubMilitiaType );
|
|
|
|
// now count up the results
|
|
for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ )
|
|
{
|
|
if( gsUnpaidStrategicSector[ iCounter ] > 0 )
|
|
{
|
|
iNumberOfSectors++;
|
|
}
|
|
}
|
|
|
|
// return the result
|
|
return( iNumberOfSectors );
|
|
|
|
}
|
|
|
|
|
|
void StartTrainingInAllUnpaidTrainableSectors( UINT8 ubMilitiaType )
|
|
{
|
|
INT32 iCounter = 0;
|
|
UINT8 ubSector;
|
|
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
SetAssignmentForList( TRAIN_TOWN, 0 );
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
SetAssignmentForList( TRAIN_MOBILE, 0 );
|
|
}
|
|
|
|
BuildListOfUnpaidTrainableSectors( ubMilitiaType );
|
|
|
|
// pay up in each sector
|
|
for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ )
|
|
{
|
|
if( gsUnpaidStrategicSector[ iCounter ] > 0 )
|
|
{
|
|
// convert strategic sector to 0-255 system
|
|
ubSector = STRATEGIC_INDEX_TO_SECTOR_INFO( gsUnpaidStrategicSector[ iCounter ] );
|
|
PayForTrainingInSector( ubSector );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ContinueTrainingInThisSector( UINT8 ubMilitiaType )
|
|
{
|
|
UINT8 ubSector;
|
|
|
|
Assert( pMilitiaTrainerSoldier );
|
|
|
|
// pay up in the sector where pMilitiaTrainerSoldier is
|
|
ubSector = SECTOR( pMilitiaTrainerSoldier->sSectorX, pMilitiaTrainerSoldier->sSectorY );
|
|
PayForTrainingInSector( ubSector );
|
|
}
|
|
|
|
|
|
void PayForTrainingInSector( UINT8 ubSector )
|
|
{
|
|
INT32 iMilitiaTrainingCost = gGameExternalOptions.iMilitiaTrainingCost;
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia6");
|
|
|
|
INT32 CostMultiplyer = 0;
|
|
UINT8 ubMilitiaType = 0;
|
|
|
|
// spend the money
|
|
// Kaiden: Roaming Militia Training:
|
|
// Charging more to train Roaming Militia
|
|
// Or for promotions.
|
|
|
|
if ( gfAreWeTrainingMobile)
|
|
{
|
|
Assert( SectorInfo[ ubSector ].fMobileMilitiaTrainingPaid == FALSE );
|
|
// Mark sector as being paid up
|
|
SectorInfo[ ubSector ].fMobileMilitiaTrainingPaid = TRUE;
|
|
CostMultiplyer = gGameExternalOptions.iMilitiaCostModifier;
|
|
gfAreWeTrainingMobile = FALSE;
|
|
ubMilitiaType = MOBILE_MILITIA;
|
|
}
|
|
else
|
|
{
|
|
Assert( SectorInfo[ ubSector ].fMilitiaTrainingPaid == FALSE );
|
|
if ( gfAreWePromotingGreen)
|
|
{
|
|
CostMultiplyer = gGameExternalOptions.iRegularCostModifier;
|
|
gfAreWePromotingGreen = FALSE;
|
|
}
|
|
else if ( gfAreWePromotingRegular)
|
|
{
|
|
CostMultiplyer = gGameExternalOptions.iVeteranCostModifier;
|
|
gfAreWePromotingRegular = FALSE;
|
|
}
|
|
else
|
|
{
|
|
CostMultiplyer = 1;
|
|
}
|
|
// Mark sector as having paid
|
|
SectorInfo[ ubSector ].fMilitiaTrainingPaid = TRUE;
|
|
ubMilitiaType = TOWN_MILITIA;
|
|
}
|
|
|
|
AddTransactionToPlayersBook( TRAIN_TOWN_MILITIA, ubSector, GetWorldTotalMin(), -( iMilitiaTrainingCost*CostMultiplyer ) );
|
|
|
|
// reset done flags
|
|
ResetDoneFlagForAllMilitiaTrainersInSector( ubSector, ubMilitiaType );
|
|
}
|
|
|
|
|
|
void ResetDoneFlagForAllMilitiaTrainersInSector( UINT8 ubSector, UINT8 ubMilitiaType )
|
|
{
|
|
INT32 iCounter = 0;
|
|
SOLDIERTYPE *pSoldier = NULL;
|
|
|
|
|
|
for( iCounter = 0; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCounter++ )
|
|
{
|
|
pSoldier = &Menptr[ iCounter ];
|
|
|
|
if( pSoldier->bActive )
|
|
{
|
|
if (ubMilitiaType == TOWN_MILITIA)
|
|
{
|
|
if( pSoldier->bAssignment == TRAIN_TOWN )
|
|
{
|
|
if( ( SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) == ubSector ) && ( pSoldier->bSectorZ == 0 ) )
|
|
{
|
|
pSoldier->flags.fDoneAssignmentAndNothingToDoFlag = FALSE;
|
|
pSoldier->usQuoteSaidExtFlags &= ~SOLDIER_QUOTE_SAID_DONE_ASSIGNMENT;
|
|
}
|
|
}
|
|
}
|
|
else if (ubMilitiaType == MOBILE_MILITIA)
|
|
{
|
|
if( pSoldier->bAssignment == TRAIN_MOBILE )
|
|
{
|
|
if( ( SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) == ubSector ) && ( pSoldier->bSectorZ == 0 ) )
|
|
{
|
|
pSoldier->flags.fDoneAssignmentAndNothingToDoFlag = FALSE;
|
|
pSoldier->usQuoteSaidExtFlags &= ~SOLDIER_QUOTE_SAID_DONE_ASSIGNMENT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN MilitiaTrainingAllowedInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
|
|
{
|
|
INT8 bTownId;
|
|
BOOLEAN fSamSitePresent = FALSE;
|
|
|
|
|
|
if( bSectorZ != 0 )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
fSamSitePresent = IsThisSectorASAMSector( sSectorX, sSectorY, bSectorZ );
|
|
|
|
if( fSamSitePresent )
|
|
{
|
|
// all SAM sites may have militia trained at them
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
bTownId = GetTownIdForSector( sSectorX, sSectorY );
|
|
|
|
|
|
return( MilitiaTrainingAllowedInTown( bTownId ) );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN MilitiaTrainingAllowedInTown( INT8 bTownId )
|
|
{
|
|
return( gfMilitiaAllowedInTown[bTownId] );
|
|
}
|
|
|
|
void BuildMilitiaPromotionsString( STR16 str )
|
|
{
|
|
CHAR16 pStr[256];
|
|
BOOLEAN fAddSpace = FALSE;
|
|
swprintf( str, L"" );
|
|
|
|
if( !gbMilitiaPromotions )
|
|
{
|
|
return;
|
|
}
|
|
if( gbGreenToElitePromotions > 1 )
|
|
{
|
|
swprintf( pStr, gzLateLocalizedString[22], gbGreenToElitePromotions );
|
|
wcscat( str, pStr );
|
|
fAddSpace = TRUE;
|
|
}
|
|
else if( gbGreenToElitePromotions == 1 )
|
|
{
|
|
wcscat( str, gzLateLocalizedString[29] );
|
|
fAddSpace = TRUE;
|
|
}
|
|
|
|
if( gbGreenToRegPromotions > 1 )
|
|
{
|
|
if( fAddSpace )
|
|
{
|
|
wcscat( str, L" " );
|
|
}
|
|
swprintf( pStr, gzLateLocalizedString[23], gbGreenToRegPromotions );
|
|
wcscat( str, pStr );
|
|
fAddSpace = TRUE;
|
|
}
|
|
else if( gbGreenToRegPromotions == 1 )
|
|
{
|
|
if( fAddSpace )
|
|
{
|
|
wcscat( str, L" " );
|
|
}
|
|
wcscat( str, gzLateLocalizedString[30] );
|
|
fAddSpace = TRUE;
|
|
}
|
|
|
|
if( gbRegToElitePromotions > 1 )
|
|
{
|
|
if( fAddSpace )
|
|
{
|
|
wcscat( str, L" " );
|
|
}
|
|
swprintf( pStr, gzLateLocalizedString[24], gbRegToElitePromotions );
|
|
wcscat( str, pStr );
|
|
}
|
|
else if( gbRegToElitePromotions == 1 )
|
|
{
|
|
if( fAddSpace )
|
|
{
|
|
wcscat( str, L" " );
|
|
}
|
|
wcscat( str, gzLateLocalizedString[31] );
|
|
fAddSpace = TRUE;
|
|
}
|
|
|
|
//Clear the fields
|
|
gbGreenToElitePromotions = 0;
|
|
gbGreenToRegPromotions = 0;
|
|
gbRegToElitePromotions = 0;
|
|
gbMilitiaPromotions = 0;
|
|
}
|
|
// HEADROCK HAM 3.3: This function finds the best Militia Trainer in the target sector. It may also take into account
|
|
// mercs' TEACHING skill, if told to. The function returns the calculated "Effective Leadership" of the selected
|
|
// trainer.
|
|
UINT8 FindBestMilitiaTrainingLeadershipInSector ( INT16 sMapX, INT16 sMapY, INT8 bMapZ, UINT8 ubMilitiaType )
|
|
{
|
|
|
|
UINT16 cnt = 0;
|
|
SOLDIERTYPE * pCheckedTrainer;
|
|
UINT16 usTrainerEffectiveLeadership = 0;
|
|
UINT8 ubBestLeadership = 0;
|
|
|
|
// HEADROCK HAM 3.3: Add search for best trainer in sector
|
|
for ( pCheckedTrainer = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pCheckedTrainer++)
|
|
{
|
|
if (pCheckedTrainer->bActive && pCheckedTrainer->stats.bLife >= OKLIFE &&
|
|
((ubMilitiaType == TOWN_MILITIA && pCheckedTrainer->bAssignment == TRAIN_TOWN) ||
|
|
(ubMilitiaType == MOBILE_MILITIA && pCheckedTrainer->bAssignment == TRAIN_MOBILE) ) )
|
|
{
|
|
if (pCheckedTrainer->sSectorX == sMapX && pCheckedTrainer->sSectorY == sMapY && pCheckedTrainer->bSectorZ == bMapZ )
|
|
{
|
|
usTrainerEffectiveLeadership = EffectiveLeadership(pCheckedTrainer);
|
|
|
|
if ( gGameOptions.fNewTraitSystem ) // SANDRO - old/new traits
|
|
{
|
|
// -10% penalty for untrained mercs
|
|
usTrainerEffectiveLeadership = (usTrainerEffectiveLeadership * (100 - gSkillTraitValues.bSpeedModifierTrainingMilitia) / 100);
|
|
|
|
if (HAS_SKILL_TRAIT( pCheckedTrainer, TEACHING_NT ))
|
|
{
|
|
// bonus from Teaching trait
|
|
usTrainerEffectiveLeadership = __min(100,(usTrainerEffectiveLeadership * (100 + gSkillTraitValues.ubTGEffectiveLDRToTrainMilitia) / 100 ));
|
|
}
|
|
}
|
|
// Effective leadership is modified by an INI-based percentage, once for every TEACHING trait level.
|
|
else if ( gGameExternalOptions.usTeacherTraitEffectOnLeadership > 0 && gGameExternalOptions.usTeacherTraitEffectOnLeadership != 100 )
|
|
{
|
|
for (UINT8 i = 0; i < NUM_SKILL_TRAITS( pCheckedTrainer, TEACHING_OT ); i++ )
|
|
{
|
|
// percentage-based.
|
|
usTrainerEffectiveLeadership = __min(100,((usTrainerEffectiveLeadership * gGameExternalOptions.usTeacherTraitEffectOnLeadership)/100));
|
|
}
|
|
}
|
|
|
|
if (usTrainerEffectiveLeadership > ubBestLeadership)
|
|
{
|
|
ubBestLeadership = __min(100,usTrainerEffectiveLeadership);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(ubBestLeadership);
|
|
}
|
|
|
|
// HEADROCK HAM 3.6: Created a new function to make things easier. This calculates the target number of militia to
|
|
// be created.
|
|
UINT8 CalcNumMilitiaTrained(UINT8 ubBestLeadership, BOOLEAN fMobile)
|
|
{
|
|
|
|
UINT8 ubReqLeadershipForFull = 1;
|
|
UINT8 ubMinLeadershipForTraining = 0;
|
|
UINT8 ubLeadershipRange = 0;
|
|
|
|
// Result value
|
|
UINT8 ubMilitiaToTrain = 0;
|
|
|
|
|
|
// Garrison Training
|
|
if (!fMobile)
|
|
{
|
|
// Default Squad Size for Garrisons, read from INI.
|
|
ubMilitiaToTrain = gGameExternalOptions.iTrainingSquadSize;
|
|
|
|
// Is affected by Leadership?
|
|
if (gGameExternalOptions.fLeadershipAffectsMilitiaQuantity &&
|
|
gGameExternalOptions.ubReqLeadershipForFullTraining > gGameExternalOptions.ubMinimumLeadershipToTrainMilitia)
|
|
{
|
|
// Set these variables to their INI values.
|
|
ubReqLeadershipForFull = gGameExternalOptions.ubReqLeadershipForFullTraining;
|
|
ubMinLeadershipForTraining = gGameExternalOptions.ubMinimumLeadershipToTrainMilitia;
|
|
|
|
// We make sure our effective leadership isn't above or below the limits. If so, reset to limits.
|
|
// If there is a minimum required to train militia, we're ASSUMING that the character has enough leadership
|
|
// otherwise they wouldn't even be here. But just in case he's skipped the check somehow, this will raise
|
|
// leadership to the minimum to avoid problems.
|
|
ubBestLeadership = __max(ubBestLeadership, ubMinLeadershipForTraining);
|
|
ubBestLeadership = __min(ubBestLeadership, ubReqLeadershipForFull);
|
|
|
|
// Find out by how much we've beaten the skill check
|
|
ubBestLeadership = ubBestLeadership - ubMinLeadershipForTraining;
|
|
|
|
// Find the range between the minimum and maximum.
|
|
ubLeadershipRange = ubReqLeadershipForFull - ubMinLeadershipForTraining;
|
|
|
|
// Squad Size is determined by the relative value of the leadership within the range. Anyone meeting or
|
|
// exceeding the range (latter shouldn't happen) will train a full squad. Others will train less. If
|
|
// leadership was the minimum required, only 1 militia is trained.
|
|
ubMilitiaToTrain = ((ubBestLeadership * ubMilitiaToTrain) / ubLeadershipRange) + 1;
|
|
ubMilitiaToTrain = __min(ubMilitiaToTrain, gGameExternalOptions.iTrainingSquadSize);
|
|
ubMilitiaToTrain = __max(1, ubMilitiaToTrain);
|
|
}
|
|
}
|
|
|
|
// Mobile Militia
|
|
else
|
|
{
|
|
// Default Squad Size for Mobiles, read from INI.
|
|
ubMilitiaToTrain = gGameExternalOptions.guiNumMobileMilitiaTrained;
|
|
|
|
// Is affected by Leadership?
|
|
if (gGameExternalOptions.fLeadershipAffectsMobileMilitiaQuantity &&
|
|
gGameExternalOptions.ubReqLeadershipForFullMobileTraining > gGameExternalOptions.ubMinimumLeadershipToTrainMobileMilitia )
|
|
{
|
|
|
|
// Set these variables to their INI values.
|
|
ubReqLeadershipForFull = gGameExternalOptions.ubReqLeadershipForFullMobileTraining;
|
|
ubMinLeadershipForTraining = gGameExternalOptions.ubMinimumLeadershipToTrainMobileMilitia;
|
|
|
|
// We make sure our effective leadership isn't above or below the limits. If so, reset to limits.
|
|
// If there is a minimum required to train militia, we're ASSUMING that the character has enough leadership
|
|
// otherwise they wouldn't even be here. But just in case he's skipped the check somehow, this will raise
|
|
// leadership to the minimum to avoid problems.
|
|
ubBestLeadership = __max(ubBestLeadership, ubMinLeadershipForTraining);
|
|
ubBestLeadership = __min(ubBestLeadership, ubReqLeadershipForFull);
|
|
|
|
// Find out by how much we've beaten the skill check
|
|
ubBestLeadership = ubBestLeadership - ubMinLeadershipForTraining;
|
|
|
|
// Find the range between the minimum and maximum.
|
|
ubLeadershipRange = ubReqLeadershipForFull - ubMinLeadershipForTraining;
|
|
|
|
// Squad Size is determined by the relative value of the leadership within the range. Anyone meeting or
|
|
// exceeding the range (latter shouldn't happen) will train a full squad. Others will train less. If
|
|
// leadership was the minimum required, only 3 militia are trained.
|
|
ubMilitiaToTrain = ((ubBestLeadership * ubMilitiaToTrain) / ubLeadershipRange) + 1;
|
|
ubMilitiaToTrain = __min(ubMilitiaToTrain, gGameExternalOptions.guiNumMobileMilitiaTrained);
|
|
ubMilitiaToTrain = __max(3, ubMilitiaToTrain);
|
|
}
|
|
}
|
|
|
|
return (ubMilitiaToTrain);
|
|
}
|
|
|
|
// HEADROCK HAM 3.6: Duplicate function for handling callback on Mobile Militia training
|
|
void PayMobileMilitiaTrainingYesNoBoxCallback( UINT8 bExitValue )
|
|
{
|
|
CHAR16 sString[ 128 ];
|
|
|
|
Assert( giTotalCostOfTraining > 0 );
|
|
|
|
// yes
|
|
if( bExitValue == MSG_BOX_RETURN_YES )
|
|
{
|
|
// does the player have enough
|
|
if( LaptopSaveInfo.iCurrentBalance >= giTotalCostOfTraining )
|
|
{
|
|
if( gfYesNoPromptIsForContinue )
|
|
{
|
|
ContinueTrainingInThisSector( MOBILE_MILITIA );
|
|
}
|
|
else
|
|
{
|
|
StartTrainingInAllUnpaidTrainableSectors( MOBILE_MILITIA );
|
|
}
|
|
|
|
#ifdef JA2BETAVERSION
|
|
// put this BEFORE training gets handled to avoid detecting an error everytime a sector completes training
|
|
// Checks Town and Mobile militia
|
|
VerifyTownTrainingIsPaidFor();
|
|
#endif
|
|
|
|
// this completes the training prompt sequence
|
|
pMilitiaTrainerSoldier = NULL;
|
|
}
|
|
else // can't afford it
|
|
{
|
|
StopTimeCompression();
|
|
|
|
swprintf( sString, L"%s", pMilitiaConfirmStrings[ 2 ] );
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, CantTrainMobileMilitiaOkBoxCallback );
|
|
}
|
|
}
|
|
else if( bExitValue == MSG_BOX_RETURN_NO )
|
|
{
|
|
StopTimeCompression();
|
|
|
|
MilitiaTrainingRejected( MOBILE_MILITIA );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// HEADROCK HAM 3.6: Daily check for upkeep of all militia
|
|
void HandleMilitiaUpkeepPayment( void )
|
|
{
|
|
UINT32 uiNewTotalUpkeep = CalcMilitiaUpkeep();
|
|
|
|
if (guiTotalUpkeepForMilitia > 0)
|
|
{
|
|
// Yesterday night we already had some militia, so we're eligible to pay tonight.
|
|
|
|
// Pay no more than what we owed yesterday. Militia added today don't get paid.
|
|
UINT32 uiPayment = __min( uiNewTotalUpkeep, guiTotalUpkeepForMilitia );
|
|
|
|
if (uiPayment > 0)
|
|
{
|
|
// Do we have enough money?
|
|
if (LaptopSaveInfo.iCurrentBalance < (INT32)uiPayment)
|
|
{
|
|
INT32 uiMoneyUnpaid = uiPayment - LaptopSaveInfo.iCurrentBalance;
|
|
|
|
// Find and remove militia across the map. This is done randomly, starting with elites.
|
|
UINT32 uiNumMilitiaDisbanded = 0;
|
|
|
|
MILITIA_LIST_TYPE MilitiaList[256];
|
|
|
|
// Go through every sector and count how many militia we've got in total.
|
|
for (UINT16 cnt = 0; cnt < 256; cnt++)
|
|
{
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ cnt ] );
|
|
UINT8 sMapX = SECTORX(cnt);
|
|
UINT8 sMapY = SECTORY(cnt);
|
|
|
|
MilitiaList[cnt].ubSectorId = (UINT8)cnt;
|
|
|
|
UINT8 ubTownId = GetTownIdForSector( sMapX, sMapY );
|
|
if ((ubTownId != BLANK_SECTOR && MilitiaTrainingAllowedInTown(ubTownId)) || // Major Town
|
|
IsThisSectorASAMSector( sMapX, sMapY, 0 ) ) // SAM Site
|
|
{
|
|
MilitiaList[cnt].ubNumTownGreens = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA];
|
|
MilitiaList[cnt].ubNumTownRegulars = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA];
|
|
MilitiaList[cnt].ubNumTownElites = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA];
|
|
MilitiaList[cnt].ubNumMobileGreens = 0;
|
|
MilitiaList[cnt].ubNumMobileRegulars = 0;
|
|
MilitiaList[cnt].ubNumMobileElites = 0;
|
|
}
|
|
else
|
|
{
|
|
MilitiaList[cnt].ubNumMobileGreens = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA];
|
|
MilitiaList[cnt].ubNumMobileRegulars = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA];
|
|
MilitiaList[cnt].ubNumMobileElites = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA];
|
|
MilitiaList[cnt].ubNumTownGreens = 0;
|
|
MilitiaList[cnt].ubNumTownRegulars = 0;
|
|
MilitiaList[cnt].ubNumTownElites = 0;
|
|
}
|
|
}
|
|
|
|
// Resort list so that sectors with more militia are at the top of the array. Higher weight
|
|
// for mobiles, and moreso for elites, then regulars, then greens.
|
|
qsort( MilitiaList, 256, sizeof(MILITIA_LIST_TYPE), MilitiaListQsortCompare);
|
|
|
|
while (uiMoneyUnpaid > 0)
|
|
{
|
|
if (MilitiaList[0].ubNumTownGreens +
|
|
MilitiaList[0].ubNumTownRegulars +
|
|
MilitiaList[0].ubNumTownElites +
|
|
MilitiaList[0].ubNumMobileGreens +
|
|
MilitiaList[0].ubNumMobileRegulars +
|
|
MilitiaList[0].ubNumMobileElites == 0)
|
|
{
|
|
//All sectors are empty? Default to 0 payment.
|
|
uiMoneyUnpaid = 0;
|
|
}
|
|
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ MilitiaList[0].ubSectorId ] );
|
|
|
|
if (MilitiaList[0].ubNumMobileElites > 0)
|
|
{
|
|
// Remove one militia from sector
|
|
pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]--;
|
|
// Adjust list entry
|
|
MilitiaList[0].ubNumMobileElites--;
|
|
// Reduce debt appropriately
|
|
uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobileElite;
|
|
// Increase tally of militia removed
|
|
uiNumMilitiaDisbanded++;
|
|
}
|
|
else if (MilitiaList[0].ubNumMobileRegulars > 0)
|
|
{
|
|
pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]--;
|
|
MilitiaList[0].ubNumMobileRegulars--;
|
|
uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobileRegular;
|
|
uiNumMilitiaDisbanded++;
|
|
}
|
|
else if (MilitiaList[0].ubNumMobileGreens > 0)
|
|
{
|
|
pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]--;
|
|
MilitiaList[0].ubNumMobileGreens--;
|
|
uiMoneyUnpaid -= gGameExternalOptions.usDailyCostMobileGreen;
|
|
uiNumMilitiaDisbanded++;
|
|
}
|
|
else if (MilitiaList[0].ubNumTownElites > 0)
|
|
{
|
|
pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]--;
|
|
MilitiaList[0].ubNumTownElites--;
|
|
uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTownElite;
|
|
uiNumMilitiaDisbanded++;
|
|
}
|
|
else if (MilitiaList[0].ubNumTownRegulars > 0)
|
|
{
|
|
pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]--;
|
|
MilitiaList[0].ubNumTownRegulars--;
|
|
uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTownRegular;
|
|
uiNumMilitiaDisbanded++;
|
|
}
|
|
else if (MilitiaList[0].ubNumTownGreens > 0)
|
|
{
|
|
pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]--;
|
|
MilitiaList[0].ubNumTownGreens--;
|
|
uiMoneyUnpaid -= gGameExternalOptions.usDailyCostTownGreen;
|
|
uiNumMilitiaDisbanded++;
|
|
}
|
|
else
|
|
{
|
|
// Somehow there are none to remove?!
|
|
Assert(1);
|
|
}
|
|
// Resort the list!
|
|
qsort( MilitiaList, 256, sizeof(MILITIA_LIST_TYPE), MilitiaListQsortCompare);
|
|
}
|
|
|
|
// Pay all we can
|
|
uiPayment = LaptopSaveInfo.iCurrentBalance;
|
|
|
|
// Notify player that X militia have been automatically disbanded due to debt.
|
|
CHAR16 sString[256];
|
|
swprintf( sString, New113HAMMessage[21], uiNumMilitiaDisbanded );
|
|
DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL );
|
|
}
|
|
AddTransactionToPlayersBook( MILITIA_UPKEEP, 0, GetWorldTotalMin(), (-1) * (INT32)uiPayment );
|
|
}
|
|
}
|
|
|
|
// Record tonight's upkeep costs
|
|
guiTotalUpkeepForMilitia = uiNewTotalUpkeep;
|
|
}
|
|
|
|
int MilitiaListQsortCompare(const void *pArg1, const void *pArg2)
|
|
{
|
|
if (((MILITIA_LIST_TYPE *)pArg1)->ubNumMobileElites > ((MILITIA_LIST_TYPE *)pArg2)->ubNumMobileElites)
|
|
{
|
|
return(-1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumMobileRegulars > ((MILITIA_LIST_TYPE *)pArg2)->ubNumMobileRegulars)
|
|
{
|
|
return(-1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumMobileGreens > ((MILITIA_LIST_TYPE *)pArg2)->ubNumMobileGreens)
|
|
{
|
|
return(-1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumTownElites > ((MILITIA_LIST_TYPE *)pArg2)->ubNumTownElites)
|
|
{
|
|
return(-1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumTownRegulars > ((MILITIA_LIST_TYPE *)pArg2)->ubNumTownRegulars)
|
|
{
|
|
return(-1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumTownGreens > ((MILITIA_LIST_TYPE *)pArg2)->ubNumTownGreens)
|
|
{
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
if (((MILITIA_LIST_TYPE *)pArg1)->ubNumTownGreens < ((MILITIA_LIST_TYPE *)pArg2)->ubNumTownGreens)
|
|
{
|
|
return(1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumTownRegulars < ((MILITIA_LIST_TYPE *)pArg2)->ubNumTownRegulars)
|
|
{
|
|
return(1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumTownElites < ((MILITIA_LIST_TYPE *)pArg2)->ubNumTownElites)
|
|
{
|
|
return(1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumMobileGreens < ((MILITIA_LIST_TYPE *)pArg2)->ubNumMobileGreens)
|
|
{
|
|
return(1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumMobileRegulars < ((MILITIA_LIST_TYPE *)pArg2)->ubNumMobileRegulars)
|
|
{
|
|
return(1);
|
|
}
|
|
else if (((MILITIA_LIST_TYPE *)pArg1)->ubNumMobileElites < ((MILITIA_LIST_TYPE *)pArg2)->ubNumMobileElites)
|
|
{
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate amount to pay for militia upkeep today.
|
|
UINT32 CalcMilitiaUpkeep( void )
|
|
{
|
|
UINT32 uiTotalPayment = 0;
|
|
|
|
for (UINT16 cnt = 0; cnt < 256; cnt++)
|
|
{
|
|
SECTORINFO *pSectorInfo = &( SectorInfo[ cnt ] );
|
|
UINT8 sMapX = SECTORX(cnt);
|
|
UINT8 sMapY = SECTORY(cnt);
|
|
|
|
UINT8 ubTownId = GetTownIdForSector( sMapX, sMapY );
|
|
if ((ubTownId != BLANK_SECTOR && MilitiaTrainingAllowedInTown(ubTownId)) || // Major Town
|
|
IsThisSectorASAMSector( sMapX, sMapY, 0 ) ) // SAM Site
|
|
{
|
|
uiTotalPayment += pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] * gGameExternalOptions.usDailyCostTownGreen;
|
|
uiTotalPayment += pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA] * gGameExternalOptions.usDailyCostTownRegular;
|
|
uiTotalPayment += pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] * gGameExternalOptions.usDailyCostTownElite;
|
|
}
|
|
else
|
|
{
|
|
uiTotalPayment += pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] * gGameExternalOptions.usDailyCostMobileGreen;
|
|
uiTotalPayment += pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA] * gGameExternalOptions.usDailyCostMobileRegular;
|
|
uiTotalPayment += pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] * gGameExternalOptions.usDailyCostMobileElite;
|
|
}
|
|
}
|
|
|
|
return (uiTotalPayment);
|
|
} |