New feature: mines require workers for maximum income, these can be trained like militia.

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8267 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2016-07-04 22:31:37 +00:00
parent ce6e63831d
commit 951da7812d
26 changed files with 460 additions and 9 deletions
+247 -1
View File
@@ -466,6 +466,8 @@ void HandlePrison( INT16 sMapX, INT16 sMapY, INT8 bZ );
// Flugente: assigned mercs can move equipemnt in city sectors
void HandleEquipmentMove( INT16 sMapX, INT16 sMapY, INT8 bZ );
void HandleTrainWorkers( );
// Flugente: handle radio scanning assignments
void HandleRadioScanInSector( INT16 sMapX, INT16 sMapY, INT8 bZ );
@@ -2547,9 +2549,9 @@ void UpdateAssignments()
// Flugente: prisons can riot if there aren't enough guards around
if ( !bZ )
HandlePrison( sX, sY, bZ );
}
}
}
}
// Flugente: individual militia
HandleHourlyMilitiaHealing();
@@ -2568,6 +2570,8 @@ void UpdateAssignments()
// handle fortification
HandleFortification();
HandleTrainWorkers();
// check to see if anyone is done healing?
UpdatePatientsWhoAreDoneHealing( );
@@ -3251,6 +3255,50 @@ FLOAT GetBestSAMOperatorCTH_Player( INT16 sSectorX, INT16 sSectorY, INT16 sSecto
return bestsamcth;
}
INT16 GetTrainWorkerPts(SOLDIERTYPE *pSoldier)
{
// this is not an assignment. Simply being in the sector will allow us to be counted as guards
INT16 val = 0;
if ( pSoldier->flags.fMercAsleep )
return 0;
val = 3 * EffectiveExpLevel( pSoldier ) + EffectiveLeadership( pSoldier );
if (gGameOptions.fNewTraitSystem)
{
val += 25 * NUM_SKILL_TRAITS( pSoldier, TEACHING_NT );
}
else
{
val += 25 * NUM_SKILL_TRAITS( pSoldier, TEACHING_OT );
}
// -5% for Aggressive people
if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_AGGRESSIVE ) )
{
val -= 5;
}
// +5% for Phlegmatic people
else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_PHLEGMATIC ) )
{
val += 5;
}
// adjust for fatigue
if ( val > 0 )
{
UINT32 tmp = val;
ReducePointsForFatigue( pSoldier, &tmp );
val = tmp;
return val;
}
return 0;
}
// anv: handle prisoners exposing snitch as a snitch
BOOL HandleSnitchExposition(SOLDIERTYPE *pSoldier)
{
@@ -7435,6 +7483,60 @@ void HandleEquipmentMove( INT16 sMapX, INT16 sMapY, INT8 bZ )
}
}
void HandleTrainWorkers()
{
UINT32 totalworkersadded = 0;
SOLDIERTYPE *pSoldier = NULL;
UINT32 uiCnt = 0;
UINT32 firstid = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
UINT32 lastid = gTacticalStatus.Team[ OUR_TEAM ].bLastID;
for ( uiCnt = firstid, pSoldier = MercPtrs[ uiCnt ]; uiCnt <= lastid; ++uiCnt, ++pSoldier)
{
if( pSoldier->bActive && !pSoldier->bSectorZ && !pSoldier->flags.fMercAsleep )
{
if( ( pSoldier->bAssignment == TRAIN_WORKERS ) && ( EnoughTimeOnAssignment( pSoldier ) ) )
{
if ( !SectorOursAndPeaceful( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) )
continue;
UINT8 ubTownId = StrategicMap[ pSoldier->sSectorX + pSoldier->sSectorY * MAP_WORLD_X ].bNameId;
// Flugente: adjust for workforce
UINT16 maxworkforce = 0;
UINT16 workforce = GetTownWorkers( ubTownId, maxworkforce);
if ( workforce < maxworkforce && gGameExternalOptions.usWorkerTrainingPoints > 0 )
{
UINT8 sector = SECTOR( pSoldier->sSectorX, pSoldier->sSectorY );
SECTORINFO *pSectorInfo = &(SectorInfo[sector]);
if ( !pSectorInfo )
continue;
INT16 trainpts = pSectorInfo->ubWorkerTrainingHundredths;
trainpts += GetTrainWorkerPts(pSoldier);
UINT8 workersadded = trainpts / gGameExternalOptions.usWorkerTrainingPoints;
totalworkersadded += workersadded;
AddTownWorkers( ubTownId, workersadded );
pSectorInfo->ubWorkerTrainingHundredths = trainpts - gGameExternalOptions.usWorkerTrainingPoints * workersadded;
StatChange( pSoldier, LDRAMT, 2 * workersadded, TRUE );
StatChange( pSoldier, EXPERAMT, workersadded, TRUE );
}
}
}
}
if ( totalworkersadded * gGameExternalOptions.usWorkerTrainingCost > 0 )
AddTransactionToPlayersBook( WORKERS_TRAINED, 0, GetWorldTotalMin( ), -(totalworkersadded * gGameExternalOptions.usWorkerTrainingCost) );
}
// Flugente: fortification
void HandleFortification()
{
@@ -11686,6 +11788,47 @@ void TrainingMenuBtnCallback( MOUSE_REGION * pRegion, INT32 iReason )
}
gfRenderPBInterface = TRUE;
break;
case TRAIN_MENU_WORKERS:
// Check for specific errors why this merc should not be able to train,
// and display a specific error message if one is encountered.
if( !CanCharacterTrainWorkers(pSoldier) )
{
// Error found. Breaking. Note that the above function DOES display feedback if an error is
// encountered at all.
break;
}
pSoldier->bOldAssignment = pSoldier->bAssignment;
if( ( pSoldier->bAssignment != TRAIN_WORKERS ) )
{
SetTimeOfAssignmentChangeForMerc( pSoldier );
}
MakeSoldiersTacticalAnimationReflectAssignment( pSoldier );
// stop showing menu
fShowAssignmentMenu = FALSE;
giAssignHighLine = -1;
// remove from squad
if( pSoldier->bOldAssignment == VEHICLE )
{
TakeSoldierOutOfVehicle( pSoldier );
}
RemoveCharacterFromSquads( pSoldier );
ChangeSoldiersAssignment( pSoldier, TRAIN_WORKERS );
// assign to a movement group
AssignMercToAMovementGroup( pSoldier );
SetAssignmentForList( TRAIN_WORKERS, 0 );
gfRenderPBInterface = TRUE;
break;
case( TRAIN_MENU_TEAMMATES):
if( CanCharacterTrainTeammates( pSoldier ) == TRUE )
@@ -15069,6 +15212,49 @@ void SetSoldierAssignment( SOLDIERTYPE *pSoldier, INT8 bAssignment, INT32 iParam
gfRenderPBInterface = TRUE;
}
break;
case TRAIN_WORKERS:
{
// train mobile militia
pSoldier->bOldAssignment = pSoldier->bAssignment;
// set dirty flag
fTeamPanelDirty = TRUE;
fMapScreenBottomDirty = TRUE;
// remove from squad
RemoveCharacterFromSquads( pSoldier );
// remove from any vehicle
if( pSoldier->bOldAssignment == VEHICLE )
{
TakeSoldierOutOfVehicle( pSoldier );
}
if( ( pSoldier->bAssignment != TRAIN_WORKERS ) )
{
SetTimeOfAssignmentChangeForMerc( pSoldier );
}
ChangeSoldiersAssignment( pSoldier, TRAIN_WORKERS );
if( pMilitiaTrainerSoldier == NULL )
{
if( SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ].fMobileMilitiaTrainingPaid == FALSE )
{
// show a message to confirm player wants to charge cost
HandleInterfaceMessageForCostOfTrainingMilitia( pSoldier );
}
}
AssignMercToAMovementGroup( pSoldier );
// set dirty flag
fTeamPanelDirty = TRUE;
fMapScreenBottomDirty = TRUE;
gfRenderPBInterface = TRUE;
}
break;
case( TRAIN_SELF ):
if( CanCharacterTrainStat( pSoldier, ( INT8 )iParam1, TRUE, FALSE ) )
{
@@ -15840,6 +16026,17 @@ void HandleShadingOfLinesForTrainingMenu( void )
ShadeStringInBox( ghTrainingBox, TRAIN_MENU_MOBILE );
}
if( CanCharacterTrainWorkers( pSoldier ) )
{
// unshade train militia line
UnShadeStringInBox( ghTrainingBox, TRAIN_MENU_WORKERS );
UnSecondaryShadeStringInBox( ghTrainingBox, TRAIN_MENU_WORKERS );
}
else
{
UnShadeStringInBox( ghTrainingBox, TRAIN_MENU_WORKERS );
SecondaryShadeStringInBox( ghTrainingBox, TRAIN_MENU_WORKERS );
}
// can character train teammates?
if( CanCharacterTrainTeammates( pSoldier ) == FALSE )
@@ -16706,6 +16903,10 @@ void ReEvaluateEveryonesNothingToDo()
fNothingToDo = !CanCharacterTrainMobileMilitia( pSoldier );
break;
case TRAIN_WORKERS:
fNothingToDo = !CanCharacterTrainWorkers( pSoldier );
break;
case TRAIN_SELF:
fNothingToDo = !CanCharacterTrainStat( pSoldier, pSoldier->bTrainStat, TRUE, FALSE );
break;
@@ -16925,6 +17126,14 @@ void SetAssignmentForList( INT8 bAssignment, INT8 bParam )
fItWorked = TRUE;
}
break;
case TRAIN_WORKERS:
if( CanCharacterTrainWorkers( pSoldier ) )
{
pSoldier->bOldAssignment = pSoldier->bAssignment;
SetSoldierAssignment( pSoldier, TRAIN_WORKERS, 0, 0, 0 );
fItWorked = TRUE;
}
break;
case( TRAIN_TEAMMATE ):
if( CanCharacterTrainStat( pSoldier, bParam, FALSE, TRUE ) )
{
@@ -18431,6 +18640,43 @@ BOOLEAN CanCharacterTrainMobileMilitia( SOLDIERTYPE *pSoldier )
}
BOOLEAN CanCharacterTrainWorkers( SOLDIERTYPE *pSoldier )
{
AssertNotNIL(pSoldier);
if ( !gGameExternalOptions.fMineRequiresWorkers )
return FALSE;
if( NumEnemiesInAnySector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) )
{
return( FALSE );
}
// Has leadership skill?
if( pSoldier->stats.bLeadership <= 0 )
{
// no skill whatsoever
return ( FALSE );
}
// Sector Loyalty above minimum?
if( !DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( pSoldier ) )
{
// Not enough Loyalty...
return ( FALSE );
}
INT8 bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY );
UINT16 maxworkforce = 0;
UINT16 workforce = GetTownWorkers( bTownId, maxworkforce);
if ( maxworkforce > 0 && workforce < maxworkforce )
return TRUE;
return FALSE;
}
BOOLEAN CanCharacterTrainMilitiaWithErrorReport( SOLDIERTYPE *pSoldier )
{
// Temp string.
+6
View File
@@ -85,6 +85,7 @@ enum
FACILITY_PATIENT,
FACILITY_REPAIR,
FORTIFICATION, // Flugente: sectors can be fortified according to external layout plans
TRAIN_WORKERS,
NUM_ASSIGNMENTS,
};
@@ -179,6 +180,8 @@ BOOLEAN CanCharacterPatient( SOLDIERTYPE *pCharacter );
BOOLEAN CanCharacterTrainMilitia( SOLDIERTYPE *pCharacter );
BOOLEAN CanCharacterTrainMobileMilitia( SOLDIERTYPE *pSoldier );
BOOLEAN CanCharacterTrainWorkers( SOLDIERTYPE *pSoldier );
// can character train stat?..as train self or as trainer?
BOOLEAN CanCharacterTrainStat( SOLDIERTYPE *pSoldier, INT8 bStat, BOOLEAN fTrainSelf, BOOLEAN fTrainTeammate );
@@ -266,6 +269,9 @@ UINT32 CalculateSnitchInterrogationValue(SOLDIERTYPE *pSoldier, UINT16 *pusMaxPt
// Flugente: Determine the best cth with SAMs in a sector, and which merc has that cth if present
FLOAT GetBestSAMOperatorCTH_Player( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ, UINT16 *pubID );
INT16 GetTrainWorkerPts(SOLDIERTYPE *pSoldier);
// Flugente: determine max items we can move, and the sector distance
// get bonus tarining pts due to an instructor for this student
+3 -2
View File
@@ -531,8 +531,9 @@ typedef struct SECTORINFO
// Flugente: enemy jeeps
UINT8 ubNumJeeps;
UINT8 ubJeepsInBattle;
INT8 bPadding1[2];
UINT8 usWorkers;
UINT8 ubWorkerTrainingHundredths;
// Flugente: localized weather
UINT32 usWeather;
+16
View File
@@ -4568,6 +4568,22 @@ void BlitMineText( INT16 sMapX, INT16 sMapY )
AdjustXForLeftMapEdge(wString, &sScreenX, MapMineLabelsFont);
mprintf( ( sScreenX - StringPixLengthArg( MapMineLabelsFont, wcslen(wString), wString ) / 2 ) , sScreenY + ubLineCnt * GetFontHeight( MapMineLabelsFont ), wString );
++ubLineCnt;
// Flugente: workforce
if ( gGameExternalOptions.fMineRequiresWorkers )
{
INT8 bTownId = gMineStatus[ ubMineIndex ].bAssociatedTown;
UINT16 maxworkforce = 0;
UINT16 workforce = GetTownWorkers( bTownId, maxworkforce);
if ( maxworkforce > 0 )
{
swprintf( wString, L"%d/%d workers", workforce, maxworkforce );
AdjustXForLeftMapEdge(wString, &sScreenX, MapMineLabelsFont);
mprintf( ( sScreenX - StringPixLength( wString, MapMineLabelsFont ) / 2 ) , sScreenY + ubLineCnt * GetFontHeight( MapMineLabelsFont ) , wString );
++ubLineCnt;
}
}
}
SetFontDestBuffer( FRAME_BUFFER, MAP_VIEW_START_X, MAP_VIEW_START_Y, MAP_VIEW_START_X+MAP_VIEW_WIDTH+MAP_GRID_X, MAP_VIEW_START_Y+MAP_VIEW_HEIGHT+7, FALSE );
+1
View File
@@ -177,6 +177,7 @@ enum {
TRAIN_MENU_SELF,
TRAIN_MENU_TOWN,
TRAIN_MENU_MOBILE,
TRAIN_MENU_WORKERS,
TRAIN_MENU_TEAMMATES,
TRAIN_MENU_TRAIN_BY_OTHER,
TRAIN_MENU_CANCEL,
+5
View File
@@ -178,6 +178,8 @@ void AddFacilitiesToBox( INT16 sMapX, INT16 sMapY, UINT32 *uiHandle, BOOLEAN fCi
return;
}
// Flugente: external sector data
extern SECTOR_EXT_DATA SectorExternalData[256][4];
// ALL changes of control to player must be funneled through here!
BOOLEAN SetThisSectorAsPlayerControlled( INT16 sMapX, INT16 sMapY, INT8 bMapZ, BOOLEAN fContested )
@@ -401,6 +403,9 @@ BOOLEAN SetThisSectorAsPlayerControlled( INT16 sMapX, INT16 sMapY, INT8 bMapZ, B
AddVolunteers( loyalpopulation * gGameExternalOptions.dMilitiaVolunteerGainFactorLiberation );
}
// Flugente: workforce
SectorInfo[SECTOR( sMapX, sMapY )].usWorkers = SectorExternalData[SECTOR( sMapX, sMapY )][0].maxworkers * gGameExternalOptions.dInitialWorkerRate;
}
// Flugente: if we take the surface sector for the first time, script-defined actions might happen
+13
View File
@@ -535,6 +535,19 @@ INT32 GetAvailableWorkForceForMineForPlayer( INT8 bMineIndex )
iWorkForceSize *= GetTownSectorsUnderControl( bTownId );
iWorkForceSize /= GetTownSectorSize( bTownId );
// Flugente: workforce
if ( gGameExternalOptions.fMineRequiresWorkers )
{
UINT16 maxworkforce = 0;
UINT16 workforce = GetTownWorkers( bTownId, maxworkforce);
if ( maxworkforce > 0 )
{
iWorkForceSize *= workforce;
iWorkForceSize /= maxworkforce;
}
}
return ( iWorkForceSize );
}
+13 -2
View File
@@ -97,7 +97,8 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char **
strcmp(name, "rainchance" ) == 0 ||
strcmp(name, "sandstormchance") == 0 ||
strcmp(name, "snowchance" ) == 0 ||
strcmp(name, "snakechance" ) == 0 ))
strcmp(name, "snakechance" ) == 0 ||
strcmp(name, "maxworkers" ) == 0 ))
{
pData->curElement = ELEMENT_PROPERTY;
@@ -218,6 +219,11 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
SectorExternalData[ubSectorId][1].snakechance = 0;
SectorExternalData[ubSectorId][2].snakechance = 0;
SectorExternalData[ubSectorId][3].snakechance = 0;
SectorExternalData[ubSectorId][0].maxworkers = pData->sectordata.maxworkers;
SectorExternalData[ubSectorId][1].maxworkers = 0;
SectorExternalData[ubSectorId][2].maxworkers = 0;
SectorExternalData[ubSectorId][3].maxworkers = 0;
if ( !prisonroomvector.empty( ) )
{
@@ -241,6 +247,7 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
pData->sectordata.sandstormchance = 0;
pData->sectordata.snowchance = 0;
pData->sectordata.snakechance = 0;
pData->sectordata.maxworkers = 0;
}
else
{
@@ -305,7 +312,6 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
pData->curElement = ELEMENT;
pData->sectordata.usWaterType = (UINT8)atol( pData->szCharData );
}
else if(strcmp(name, "usNaturalDirt") == 0)
{
pData->curElement = ELEMENT;
@@ -355,6 +361,11 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
pData->curElement = ELEMENT;
pData->sectordata.snakechance = min( 100, (UINT8)atoi( pData->szCharData ) );
}
else if ( strcmp( name, "maxworkers" ) == 0 )
{
pData->curElement = ELEMENT;
pData->sectordata.maxworkers = (UINT8)atoi( pData->szCharData );
}
pData->maxReadDepth--;
}
+86
View File
@@ -1882,6 +1882,86 @@ UINT8 GetTownSectorsUnderControl( INT8 bTownId )
return(ubSectorsControlled);
}
// Flugente: external sector data
extern SECTOR_EXT_DATA SectorExternalData[256][4];
UINT16 GetTownWorkers(INT8 bTownId, UINT16& arusMax)
{
arusMax = 0;
UINT16 workerspresent = 0;
for ( UINT8 sX = 1; sX < MAP_WORLD_X - 1; ++sX )
{
for ( UINT8 sY = 1; sY < MAP_WORLD_X - 1; ++sY )
{
UINT16 strategicsector = CALCULATE_STRATEGIC_INDEX( sX, sY );
if ( StrategicMap[strategicsector].bNameId == bTownId )
{
// if we check for the player team, we look for sectors free of the enemy
if ( StrategicMap[strategicsector].fEnemyControlled )
continue;
UINT8 sector = SECTOR( sX, sY );
SECTORINFO *pSectorInfo = &(SectorInfo[sector]);
if ( !pSectorInfo )
continue;
workerspresent += pSectorInfo->usWorkers;
arusMax += SectorExternalData[sector][0].maxworkers;
}
}
}
return workerspresent;
}
void AddTownWorkers( INT8 bTownId, INT16 asAdd )
{
for ( UINT8 sX = 1; sX < MAP_WORLD_X - 1; ++sX )
{
for ( UINT8 sY = 1; sY < MAP_WORLD_X - 1; ++sY )
{
UINT16 strategicsector = CALCULATE_STRATEGIC_INDEX( sX, sY );
if ( StrategicMap[strategicsector].bNameId == bTownId )
{
// if we check for the player team, we look for sectors free of the enemy
if ( StrategicMap[strategicsector].fEnemyControlled )
continue;
UINT8 sector = SECTOR( sX, sY );
SECTORINFO *pSectorInfo = &(SectorInfo[sector]);
if ( !pSectorInfo )
continue;
if ( asAdd > 0 )
{
UINT16 add = min( asAdd, SectorExternalData[sector][0].maxworkers - pSectorInfo->usWorkers );
asAdd -= add;
pSectorInfo->usWorkers += add;
}
else if ( asAdd < 0 )
{
UINT16 remove = min( -asAdd, pSectorInfo->usWorkers );
asAdd += remove;
pSectorInfo->usWorkers -= remove;
}
if ( !asAdd )
return;
}
}
}
}
void InitializeSAMSites( void )
{
// move the landing zone over to Omerta
@@ -5390,6 +5470,12 @@ BOOLEAN LoadStrategicInfoFromSavedFile( HWFILE hFile )
SectorInfo[sectorID].ubNumTanks = SectorInfo[sectorID].uiInterrogationHundredsLeft[PRISONER_GENERAL];
SectorInfo[sectorID].ubTanksInBattle = SectorInfo[sectorID].uiInterrogationHundredsLeft[PRISONER_CIVILIAN];
}
if ( guiCurrentSaveGameVersion < WORKERS )
{
SectorInfo[sectorID].usWorkers = 0;
SectorInfo[sectorID].ubWorkerTrainingHundredths = 0;
}
}
// uiSize = sizeof( SECTORINFO ) * 256;
+3
View File
@@ -132,6 +132,9 @@ UINT8 GetTownSectorSize( INT8 bTownId );
// get town sector size, that is under play control
UINT8 GetTownSectorsUnderControl( INT8 bTownId );
UINT16 GetTownWorkers(INT8 bTownId, UINT16& arusMax);
void AddTownWorkers( INT8 bTownId, INT16 asAdd );
// is this sector part of this town
BOOLEAN SectorIsPartOfTown( INT8 bTownId, INT16 sSectorX, INT16 sSectorY );