diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index c70c3a5d..5eeb2316 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -2637,10 +2637,13 @@ void CreateAutoResolveInterface() gpAR->iButton[ RETREAT_BUTTON ] = QuickCreateButton( gpAR->iButtonImage[ RETREAT_BUTTON ], (INT16)(gpAR->sCenterStartX+51), (INT16)(iScreenHeightOffset + 274+gpAR->bVerticalOffset), BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, RetreatButtonCallback ); - if( !gpAR->ubMercs ) + + // no retreat possible if no mercs are present and militia isn't present or cannot be commanded + if ( !gpAR->ubMercs && (!gpAR->ubCivs || !CanGiveStrategicMilitiaMoveOrder( gpAR->ubSectorX, gpAR->ubSectorY )) ) { DisableButton( gpAR->iButton[ RETREAT_BUTTON ] ); } + SpecifyGeneralButtonTextAttributes( gpAR->iButton[ RETREAT_BUTTON ], gpStrategicString[STR_AR_RETREAT_BUTTON], BLOCKFONT2, 169, FONT_NEARBLACK ); gpAR->iButton[ BANDAGE_BUTTON ] = @@ -2841,11 +2844,10 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2"); } else { - UINT8 ubPromotions; // this will check for promotions and handle them for you if( fDeleteForGood && ( gpCivs[ i ].pSoldier->ubMilitiaKills > 0) && ( ubCurrentRank < ELITE_MILITIA ) ) { - ubPromotions = CheckOneMilitiaForPromotion( gpCivs[ i ].pSoldier->sSectorX, gpCivs[ i ].pSoldier->sSectorY, ubCurrentRank, gpCivs[ i ].pSoldier->ubMilitiaKills ); + UINT8 ubPromotions = CheckOneMilitiaForPromotion( gpCivs[i].pSoldier->sSectorX, gpCivs[i].pSoldier->sSectorY, ubCurrentRank, gpCivs[i].pSoldier->ubMilitiaKills ); if( ubPromotions ) { if( ubPromotions == 2 ) @@ -3025,7 +3027,7 @@ void RetreatButtonCallback( GUI_BUTTON *btn, INT32 reason ) if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { INT32 i; - for( i = 0; i < gpAR->ubMercs; i++ ) + for ( i = 0; i < gpAR->ubMercs; ++i ) { if( !(gpMercs[ i ].uiFlags & (CELL_RETREATING|CELL_RETREATED)) ) { @@ -3036,8 +3038,26 @@ void RetreatButtonCallback( GUI_BUTTON *btn, INT32 reason ) gpMercs[ i ].usAttack = 0; } } + + // Flugente: militia can be ordered to retreat + if ( gGameExternalOptions.fMilitiaStrategicCommand ) + { + for ( INT32 i = 0; i < gpAR->ubCivs; ++i ) + { + if ( !(gpCivs[i].uiFlags & (CELL_RETREATING | CELL_RETREATED)) ) + { + gpCivs[i].uiFlags |= CELL_RETREATING | CELL_DIRTY; + //Gets to retreat after a total of 2 attacks. + gpCivs[i].usNextAttack = (UINT16)((1000 + gpCivs[i].usNextAttack * 2 + PreRandom( 2000 - gpCivs[i].usAttack )) * 2); + gpAR->usPlayerAttack -= gpCivs[i].usAttack; + gpCivs[i].usAttack = 0; + } + } + } + if( gpAR->pRobotCell ) - { //if robot is retreating, set the retreat time to be the same as the robot's controller. + { + //if robot is retreating, set the retreat time to be the same as the robot's controller. UINT8 ubRobotControllerID; ubRobotControllerID = gpAR->pRobotCell->pSoldier->ubRobotRemoteHolderID; @@ -3049,10 +3069,12 @@ void RetreatButtonCallback( GUI_BUTTON *btn, INT32 reason ) gpAR->pRobotCell->usNextAttack = 0xffff; return; } - for( i = 0; i < gpAR->ubMercs; i++ ) + + for( i = 0; i < gpAR->ubMercs; ++i ) { if( ubRobotControllerID == gpMercs[ i ].pSoldier->ubID ) - { //Found the controller, make the robot's retreat time match the contollers. + { + //Found the controller, make the robot's retreat time match the contollers. gpAR->pRobotCell->usNextAttack = gpMercs[ i ].usNextAttack; return; } @@ -4560,11 +4582,14 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) usAttack = (UINT16)(pAttacker->usAttack + PreRandom((INT16)(gGameExternalOptions.iAutoResolveLuckFactor*1000.0) - pAttacker->usAttack )); else usAttack = (UINT16)(950 + PreRandom( 50 )); + if( pTarget->uiFlags & CELL_RETREATING && !(pAttacker->uiFlags & CELL_FEMALECREATURE) ) - { //Attacking a retreating merc is harder. Modify the attack value to 70% of it's value. + { + //Attacking a retreating merc is harder. Modify the attack value to 70% of it's value. //This allows retreaters to have a better chance of escaping. usAttack = usAttack * 7 / 10; } + if( pTarget->usDefence < 950 ) usDefence = (UINT16)(pTarget->usDefence + PreRandom((INT16)(gGameExternalOptions.iAutoResolveLuckFactor*1000.0) - pTarget->usDefence )); else @@ -4739,11 +4764,11 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) // SANDRO - increased mercs' offense/deffense rating if ( pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0 ) { - ubImpact += (iImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus / 150); + ubImpact += (ubImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus / 150); } - else if ( pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (iImpact > 3) ) + else if ( pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (ubImpact > 3) ) { - ubImpact = max( 3, ((iImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) / 100) ); + ubImpact = max( 3, ((ubImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) / 100) ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -4816,11 +4841,11 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) // SANDRO - increased mercs' offense/deffense rating if ( pAttacker->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveOffenseBonus != 0 ) { - iImpact += (iImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus / 150); + ubImpact += (ubImpact * gGameExternalOptions.sMercsAutoresolveOffenseBonus / 150); } - else if ( pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (iImpact > 3) ) + else if ( pTarget->uiFlags & CELL_MERC && gGameExternalOptions.sMercsAutoresolveDeffenseBonus != 0 && (ubImpact > 3) ) { - iImpact = max( 3, ((iImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) / 100) ); + ubImpact = max( 3, ((ubImpact * (100 - (gGameExternalOptions.sMercsAutoresolveDeffenseBonus / 2))) / 100) ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -4950,12 +4975,14 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) pTarget->pSoldier->DoMercBattleSound( (INT8)( BATTLE_SOUND_HIT1 + PreRandom( 2 ) ) ); } if( !(pTarget->uiFlags & CELL_CREATURE) && iNewLife < OKLIFE && pTarget->pSoldier->stats.bLife >= OKLIFE ) - { //the hit caused the merc to fall. Play the falling sound + { + //the hit caused the merc to fall. Play the falling sound PlayAutoResolveSample( (UINT8)FALL_1, RATE_11025, 50, 1, MIDDLEPAN ); pTarget->uiFlags &= ~CELL_RETREATING; } if( iNewLife <= 0 ) - { //soldier has been killed + { + //soldier has been killed // Flugente: campaign stats gCurrentIncident.AddStat( pTarget->pSoldier, CAMPAIGNHISTORY_TYPE_KILL ); @@ -5368,34 +5395,56 @@ BOOLEAN IsBattleOver() INT32 i; INT32 iNumInvolvedMercs = 0; INT32 iNumMercsRetreated = 0; + INT32 iNumInvolvedMilitia = 0; + INT32 iNumMilitiaRetreated = 0; BOOLEAN fOnlyEPCsLeft = TRUE; + if( gpAR->ubBattleStatus != BATTLE_IN_PROGRESS ) return TRUE; - for( i = 0; i < gpAR->ubMercs; i++ ) + + for( i = 0; i < gpAR->ubMercs; ++i ) { if( !(gpMercs[ i ].uiFlags & CELL_RETREATED) && gpMercs[ i ].pSoldier->stats.bLife ) { if( !(gpMercs[ i ].uiFlags & CELL_EPC) ) { fOnlyEPCsLeft = FALSE; - iNumInvolvedMercs++; + ++iNumInvolvedMercs; } } + if( gpMercs[ i ].uiFlags & CELL_RETREATED ) { - iNumMercsRetreated++; + ++iNumMercsRetreated; } } + + for ( i = 0; i < gpAR->ubCivs; ++i ) + { + if ( !(gpCivs[i].uiFlags & CELL_RETREATED) && gpCivs[i].pSoldier->stats.bLife ) + { + ++iNumInvolvedMilitia; + } + else if ( gpCivs[i].uiFlags & CELL_RETREATED ) + { + ++iNumMilitiaRetreated; + } + } + if( gpAR->pRobotCell ) - { //Do special robot checks + { + //Do special robot checks SOLDIERTYPE *pRobot; pRobot = gpAR->pRobotCell->pSoldier; if( pRobot->ubRobotRemoteHolderID == NOBODY ) - { //Robot can't fight anymore. + { + //Robot can't fight anymore. gpAR->usPlayerAttack -= gpAR->pRobotCell->usAttack; gpAR->pRobotCell->usAttack = 0; + if( iNumInvolvedMercs == 1 && !gpAR->ubAliveCivs ) - { //Robot is the only one left in battle, so instantly kill him. + { + //Robot is the only one left in battle, so instantly kill him. pRobot->DoMercBattleSound( BATTLE_SOUND_DIE1 ); pRobot->stats.bLife = 0; gpAR->ubAliveMercs--; @@ -5403,8 +5452,10 @@ BOOLEAN IsBattleOver() } } } + if( !gpAR->ubAliveCivs && !iNumInvolvedMercs && iNumMercsRetreated ) - { //RETREATED + { + //RETREATED gpAR->ubBattleStatus = BATTLE_RETREAT; // wake everyone up @@ -5412,22 +5463,36 @@ BOOLEAN IsBattleOver() RetreatAllInvolvedPlayerGroups( ); } + else if ( !iNumInvolvedMercs && !iNumInvolvedMilitia && iNumMilitiaRetreated && gGameExternalOptions.fMilitiaStrategicCommand ) + { + //RETREATED + gpAR->ubBattleStatus = BATTLE_RETREAT; + + // wake everyone up + WakeUpAllMercsInSectorUnderAttack( ); + + RetreatAllInvolvedMilitiaGroups( ); + } else if( !gpAR->ubAliveCivs && !iNumInvolvedMercs ) - { //DEFEAT + { + //DEFEAT + if( fOnlyEPCsLeft ) - { //Kill the EPCs. - for( i = 0; i < gpAR->ubMercs; i++ ) + { + //Kill the EPCs. + for( i = 0; i < gpAR->ubMercs; ++i ) { if( gpMercs[ i ].uiFlags & CELL_EPC ) { gpMercs[ i ].pSoldier->DoMercBattleSound( BATTLE_SOUND_DIE1 ); gpMercs[ i ].pSoldier->stats.bLife = 0; gpMercs[ i ].pSoldier->iHealableInjury = 0; // added by SANDRO - gpAR->ubAliveMercs--; + --gpAR->ubAliveMercs; } } } - for( i = 0; i < gpAR->ubEnemies; i++ ) + + for( i = 0; i < gpAR->ubEnemies; ++i ) { if( gpEnemies[ i ].pSoldier->stats.bLife ) { @@ -5445,14 +5510,17 @@ BOOLEAN IsBattleOver() gpAR->ubBattleStatus = BATTLE_DEFEAT; } else if( !gpAR->ubAliveEnemies ) - { //VICTORY + { + //VICTORY gpAR->ubBattleStatus = BATTLE_VICTORY; } else { return FALSE; } + SetupDoneInterface(); + return TRUE; } @@ -5953,26 +6021,108 @@ BOOLEAN ProcessLoyalty() void CheckForSoldiersWhoRetreatedIntoMilitiaHeldSectors() { - for(int sX = 1; sX < ( MAP_WORLD_X - 1 ); sX++ ) { - for(int sY = 1; sY < ( MAP_WORLD_Y - 1); sY++ ) { + for( int sX = 1; sX < ( MAP_WORLD_X - 1 ); ++sX ) + { + for( int sY = 1; sY < ( MAP_WORLD_Y - 1); ++sY ) + { // Check if there is a sector where enemies retreated to and there are also militia present if ( (NumNonPlayerTeamMembersInSector( sX, sY, ENEMY_TEAM ) > 0) && (NumNonPlayerTeamMembersInSector( sX, sY, MILITIA_TEAM ) > 0) && - (!gTacticalStatus.fEnemyInSector)) { + (!gTacticalStatus.fEnemyInSector)) + { unsigned mercCnt = 0; - for( int i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) { + for( int i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++i ) + { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE) && !AM_A_ROBOT( MercPtrs[ i ] ) ) - { //Merc is active and alive, and not a vehicle or robot - if ((MercPtrs[ i ]->sSectorX == sX) &&(MercPtrs[ i ]->sSectorY == sY) && (MercPtrs[ i ]->bSectorZ == 0)) { + { + //Merc is active and alive, and not a vehicle or robot + if ((MercPtrs[ i ]->sSectorX == sX) &&(MercPtrs[ i ]->sSectorY == sY) && (MercPtrs[ i ]->bSectorZ == 0)) + { ++mercCnt; } } } + // If there are PC mercs here the player will have to handle the battle - if (mercCnt == 0) { + if (mercCnt == 0) + { // EnterAutoResolveMode ((UINT8)sX, (UINT8)sY); } } } } } + +// Flugente: have all militia in autoresolve drop their gear and be promoted in necessary +void AutoResolveMilitiaDropAndPromote() +{ + gbGreenToElitePromotions = 0; + gbGreenToRegPromotions = 0; + gbRegToElitePromotions = 0; + gbMilitiaPromotions = 0; + for ( INT32 i = 0; i < MAX_AR_TEAM_SIZE; ++i ) + { + if ( gpCivs[i].pSoldier ) + { + UINT8 ubCurrentRank = GREEN_MILITIA; + switch ( gpCivs[i].pSoldier->ubSoldierClass ) + { + case SOLDIER_CLASS_GREEN_MILITIA: ubCurrentRank = GREEN_MILITIA; break; + case SOLDIER_CLASS_REG_MILITIA: ubCurrentRank = REGULAR_MILITIA; break; + case SOLDIER_CLASS_ELITE_MILITIA: ubCurrentRank = ELITE_MILITIA; break; + default: +#ifdef JA2BETAVERSION + ScreenMsg( FONT_RED, MSG_ERROR, L"Removing autoresolve militia with invalid ubSoldierClass %d.", gpCivs[i].pSoldier->ubSoldierClass ); +#endif + break; + } + + // Flugente: drop sector equipment + gpCivs[i].pSoldier->DropSectorEquipment( ); + + if ( gpCivs[i].pSoldier->stats.bLife < OKLIFE / 2 ) + { + StrategicRemoveMilitiaFromSector( gpCivs[i].pSoldier->sSectorX, gpCivs[i].pSoldier->sSectorY, ubCurrentRank, 1 ); + } + else if ( gpCivs[i].pSoldier->stats.bLife >= OKLIFE / 2 ) + { + // this will check for promotions and handle them for you + if ( gpCivs[i].pSoldier->ubMilitiaKills && ubCurrentRank < ELITE_MILITIA ) + { + UINT8 ubPromotions = CheckOneMilitiaForPromotion( gpCivs[i].pSoldier->sSectorX, gpCivs[i].pSoldier->sSectorY, ubCurrentRank, gpCivs[i].pSoldier->ubMilitiaKills ); + if ( ubPromotions ) + { + if ( ubPromotions == 2 ) + { + ++gbGreenToElitePromotions; + ++gbMilitiaPromotions; + + ubCurrentRank = ELITE_MILITIA; + } + else if ( gpCivs[i].pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) + { + ++gbGreenToRegPromotions; + ++gbMilitiaPromotions; + + ubCurrentRank = REGULAR_MILITIA; + } + else if ( gpCivs[i].pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) + { + ++gbRegToElitePromotions; + ++gbMilitiaPromotions; + + ubCurrentRank = ELITE_MILITIA; + } + } + } + + // in any case, we had our chance to be promoted, clear the counter + gpCivs[i].pSoldier->ubMilitiaKills = 0; + } + + // DO NOT DELETE HERE!!!! + //TacticalRemoveSoldierPointer( gpCivs[i].pSoldier, FALSE ); + //memset( &gpCivs[i], 0, sizeof(SOLDIERCELL) ); + } + } +} \ No newline at end of file diff --git a/Strategic/Auto Resolve.h b/Strategic/Auto Resolve.h index 8d8a3366..b45fa762 100644 --- a/Strategic/Auto Resolve.h +++ b/Strategic/Auto Resolve.h @@ -21,4 +21,7 @@ BOOLEAN GetCurrentBattleSectorXYZ( INT16 *psSectorX, INT16 *psSectorY, INT16 *ps void CheckForSoldiersWhoRetreatedIntoMilitiaHeldSectors(); +// Flugente: have all militia in autoresolve drop their gear and be promoted in necessary +void AutoResolveMilitiaDropAndPromote(); + #endif \ No newline at end of file diff --git a/Strategic/Map Screen Interface Map.cpp b/Strategic/Map Screen Interface Map.cpp index ce1d1b53..1525ac0c 100644 --- a/Strategic/Map Screen Interface Map.cpp +++ b/Strategic/Map Screen Interface Map.cpp @@ -960,7 +960,8 @@ UINT32 DrawMap( void ) RestoreClipRegionToFullScreen( ); - CheckForSoldiersWhoRetreatedIntoMilitiaHeldSectors(); + // Flugente: this function does not do anything... so I'm commenting its use out + //CheckForSoldiersWhoRetreatedIntoMilitiaHeldSectors(); return( TRUE ); } @@ -7688,7 +7689,7 @@ void DisplayMilitiaGroupBox() UINT16 MILITIAGROUPBOX_HEIGHT_LINE = 14; - UINT16 MILITIAGROUPBOX_WIDTH = MILITIAGROUPBOX_WIDTH_NAME + 3 * MILITIAGROUPBOX_WIDTH_TYPEBOX + MILITIAGROUPBOX_WIDTH_SECTOR + MILITIAGROUPBOX_WIDTH_ETA; + UINT16 MILITIAGROUPBOX_WIDTH = MILITIAGROUPBOX_WIDTH_NAME + 3 * MILITIAGROUPBOX_WIDTH_TYPEBOX + 2 * MILITIAGROUPBOX_WIDTH_SECTOR + MILITIAGROUPBOX_WIDTH_ETA; // we need a header line, one for the sector, and one for each group present UINT8 usMapX = GET_X_FROM_STRATEGIC_INDEX( gMilitiaPlotStartSector ); @@ -7750,6 +7751,11 @@ void DisplayMilitiaGroupBox() swprintf( sString, szMilitiaStrategicMovementText[4] ); mprintf( dispX, dispY, sString ); + dispX += MILITIAGROUPBOX_WIDTH_ETA; + + swprintf( sString, szMilitiaStrategicMovementText[7] ); + mprintf( dispX, dispY, sString ); + dispX += MILITIAGROUPBOX_WIDTH_SECTOR; dispX = gMilitiaGroupBoxX; dispY += MILITIAGROUPBOX_HEIGHT_LINE; @@ -7783,6 +7789,11 @@ void DisplayMilitiaGroupBox() swprintf( sString, L"--:--" ); mprintf( dispX, dispY, sString ); + dispX += MILITIAGROUPBOX_WIDTH_ETA; + + swprintf( sString, L"--" ); + mprintf( dispX, dispY, sString ); + dispX += MILITIAGROUPBOX_WIDTH_SECTOR; dispY += MILITIAGROUPBOX_HEIGHT_LINE; @@ -7808,6 +7819,22 @@ void DisplayMilitiaGroupBox() CHAR16 wSectorName[64]; GetShortSectorString( pGroup->ubNextX, pGroup->ubNextY, wSectorName ); + CHAR16 wFinalSectorName[64]; + swprintf( sString, L"--" ); + + // determine the final destination of this group + { + INT16 finalsector = GetLastSectorIdInMilitiaGroupPath( pGroup->ubGroupID ); + if ( finalsector > -1 ) + { + GetShortSectorString( SECTORX( finalsector ), SECTORY( finalsector ), wFinalSectorName ); + } + else + { + GetShortSectorString( pGroup->ubNextX, pGroup->ubNextY, wFinalSectorName ); + } + } + // show ETA CHAR16 timestring[64]; UINT32 uiArrivalTime = GetWorldTotalMin( ) + CalculateTravelTimeOfGroup( pGroup ); @@ -7920,6 +7947,11 @@ void DisplayMilitiaGroupBox() swprintf( sString, L"%s", timestring ); mprintf( dispX, dispY, sString ); + dispX += MILITIAGROUPBOX_WIDTH_ETA; + + swprintf( sString, L"%s", wFinalSectorName ); + mprintf( dispX, dispY, sString ); + dispX += MILITIAGROUPBOX_WIDTH_SECTOR; dispY += MILITIAGROUPBOX_HEIGHT_LINE; diff --git a/Strategic/PreBattle Interface.cpp b/Strategic/PreBattle Interface.cpp index b76b90c9..b62f2f50 100644 --- a/Strategic/PreBattle Interface.cpp +++ b/Strategic/PreBattle Interface.cpp @@ -48,6 +48,7 @@ #include "Tactical Save.h" #include "message.h" #include "CampaignStats.h" // added by Flugente + #include "militiasquads.h" // added by Flugente #endif #ifdef JA2UB @@ -2011,7 +2012,6 @@ void RetreatAllInvolvedPlayerGroups( void ) { GROUP *pGroup; - // make sure guys stop their off duty assignments, like militia training! // but don't exit vehicles - drive off in them! PutNonSquadMercsInBattleSectorOnSquads( FALSE ); @@ -2032,6 +2032,123 @@ void RetreatAllInvolvedPlayerGroups( void ) } } +void RetreatAllInvolvedMilitiaGroups() +{ + // first, determine in which sector we are + // pick the non-hostile-occupied, reachable adjacent sector with the shortest travel time + INT16 sBattleSectorX, sBattleSectorY, sBattleSectorZ; + if ( !GetCurrentBattleSectorXYZ( &sBattleSectorX, &sBattleSectorY, &sBattleSectorZ ) ) + { + // well... this sucks. We are in retreat code, but no battle is on... abort this + return; + } + + // as we move the militia to another sector, anything that modifies them has to be done now - RemoveAutoResolveInterface is too late + // as we don't want to make the location of that dependend on some state, we use a trick: + // we drop their gear here, and promote them right now. They can then be properly deleted later on. + AutoResolveMilitiaDropAndPromote(); + + // dissolve all militia groups here + INT16 newX = 0; + INT16 newY = 0; + BOOLEAN found = FALSE; + GROUP* pGroup = gpGroupList; + while ( pGroup ) + { + if ( MilitiaGroupInvolvedInThisCombat( pGroup ) ) + { + // if a group's previous sector was different, pick it + if ( pGroup->ubPrevX != 0 && pGroup->ubPrevY != 0 && ( pGroup->ubPrevX != pGroup->ubSectorX || pGroup->ubPrevY != pGroup->ubSectorY ) ) + { + newX = pGroup->ubPrevX; + newY = pGroup->ubPrevY; + found = TRUE; + } + + GROUP* pDeleteGroup = pGroup; + pGroup = pGroup->next; + + ClearMercPathsAndWaypointsForAllInGroup( pDeleteGroup ); + DissolveMilitiaGroup( pDeleteGroup->ubGroupID ); + } + else + pGroup = pGroup->next; + } + + // create new group and instantly place it in new sector + SECTORINFO *pSector = &SectorInfo[SECTOR( sBattleSectorX, sBattleSectorY )]; + + if ( !pSector ) + return; + + pGroup = CreateNewMilitiaGroupDepartingFromSector( SECTOR( sBattleSectorX, sBattleSectorY ), pSector->ubNumberOfCivsAtLevel[0], pSector->ubNumberOfCivsAtLevel[1], pSector->ubNumberOfCivsAtLevel[2] ); + + pSector->ubNumberOfCivsAtLevel[0] = 0; + pSector->ubNumberOfCivsAtLevel[1] = 0; + pSector->ubNumberOfCivsAtLevel[2] = 0; + + // if we haven't found a good direction yet, we have to pick the best adjacent sector we can find + if ( !found ) + { + INT32 besttime = 1000000; + UINT8 usDirection = NORTH_STRATEGIC_MOVE; + for ( UINT8 i = 0; i < 4; ++i ) + { + INT16 loopX = sBattleSectorX; + INT16 loopY = sBattleSectorY; + + if ( i == 0 ) + { + ++loopY; + usDirection = SOUTH_STRATEGIC_MOVE; + } + else if ( i == 1 ) + { + ++loopX; + usDirection = EAST_STRATEGIC_MOVE; + } + else if ( i == 2 ) + { + --loopY; + usDirection = NORTH_STRATEGIC_MOVE; + } + else if ( i == 3 ) + { + --loopX; + usDirection = WEST_STRATEGIC_MOVE; + } + + if ( loopX < 1 || loopX >= MAP_WORLD_X - 1 || loopY < 1 || loopY >= MAP_WORLD_Y - 1 ) + continue; + + // don't retreat into an occupied sector! + if ( NumNonPlayerTeamMembersInSector( loopX, loopY, ENEMY_TEAM ) > 0 ) + continue; + + INT32 uiTraverseTime = GetSectorMvtTimeForGroup( (UINT8)SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), usDirection, pGroup ); + if ( uiTraverseTime < besttime ) + { + besttime = uiTraverseTime; + newX = loopX; + newY = loopY; + found = TRUE; + } + } + } + + // if we found a good direction, move the group + if ( found ) + { + // while militia groups travel, they are not between sectors. As a result, when we retreat a militia group, we have to move them to the next sector immediately + // do NOT check for a new battle - when calling this function, we are likely still in autoresolve, so this would cause hiccups + PlaceGroupInSector( pGroup->ubGroupID, pGroup->ubSectorX, pGroup->ubSectorY, newX, newY, 0, FALSE ); + } + + // delete group afterwards, if it hasn't been deleted already - the militia will become static + if ( pGroup ) + DissolveMilitiaGroup( pGroup->ubGroupID ); +} + BOOLEAN PlayerMercInvolvedInThisCombat( SOLDIERTYPE *pSoldier ) { @@ -2081,6 +2198,26 @@ BOOLEAN PlayerGroupInvolvedInThisCombat( GROUP *pGroup ) return( FALSE ); } +BOOLEAN MilitiaGroupInvolvedInThisCombat( GROUP *pGroup ) +{ + Assert( pGroup ); + + // player group, non-empty, not between sectors, in the right sector, isn't a group of in transit, dead, or POW mercs, + // and either not the helicopter group, or the heli is on the ground + if ( pGroup->usGroupTeam == MILITIA_TEAM && pGroup->ubGroupSize && + !pGroup->fBetweenSectors ) + { + if ( CurrentBattleSectorIs( pGroup->ubSectorX, pGroup->ubSectorY, pGroup->ubSectorZ ) ) + { + // involved + return(TRUE); + } + } + + // not involved + return(FALSE); +} + BOOLEAN CurrentBattleSectorIs( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ) { diff --git a/Strategic/PreBattle Interface.h b/Strategic/PreBattle Interface.h index 3cca9d33..09344983 100644 --- a/Strategic/PreBattle Interface.h +++ b/Strategic/PreBattle Interface.h @@ -85,8 +85,10 @@ void WakeUpAllMercsInSectorUnderAttack( void ); void ClearMovementForAllInvolvedPlayerGroups( void ); void RetreatAllInvolvedPlayerGroups( void ); +void RetreatAllInvolvedMilitiaGroups( void ); BOOLEAN PlayerGroupInvolvedInThisCombat( GROUP *pGroup ); +BOOLEAN MilitiaGroupInvolvedInThisCombat( GROUP *pGroup ); BOOLEAN PlayerMercInvolvedInThisCombat( SOLDIERTYPE *pSoldier ); BOOLEAN CurrentBattleSectorIs( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ); diff --git a/Strategic/Strategic Movement.cpp b/Strategic/Strategic Movement.cpp index 10267070..b1b1a080 100644 --- a/Strategic/Strategic Movement.cpp +++ b/Strategic/Strategic Movement.cpp @@ -3091,16 +3091,19 @@ void SetGroupSectorValue( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ, UINT8 pGroup->ubOriginalSector = (UINT8)SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ); DeleteStrategicEvent( EVENT_GROUP_ARRIVAL, pGroup->ubGroupID ); - // set all of the mercs in the group so that they are in the new sector too. - pPlayer = pGroup->pPlayerList; - while( pPlayer ) + if ( pGroup->usGroupTeam == OUR_TEAM ) { - pPlayer->pSoldier->sSectorX = sSectorX; - pPlayer->pSoldier->sSectorY = sSectorY; - pPlayer->pSoldier->bSectorZ = (UINT8)sSectorZ; - pPlayer->pSoldier->flags.fBetweenSectors = FALSE; - pPlayer->pSoldier->flags.uiStatusFlags &= ~SOLDIER_SHOULD_BE_TACTICALLY_VALID; - pPlayer = pPlayer->next; + // set all of the mercs in the group so that they are in the new sector too. + pPlayer = pGroup->pPlayerList; + while( pPlayer ) + { + pPlayer->pSoldier->sSectorX = sSectorX; + pPlayer->pSoldier->sSectorY = sSectorY; + pPlayer->pSoldier->bSectorZ = (UINT8)sSectorZ; + pPlayer->pSoldier->flags.fBetweenSectors = FALSE; + pPlayer->pSoldier->flags.uiStatusFlags &= ~SOLDIER_SHOULD_BE_TACTICALLY_VALID; + pPlayer = pPlayer->next; + } } CheckAndHandleUnloadingOfCurrentWorld(); diff --git a/Strategic/Strategic Pathing.cpp b/Strategic/Strategic Pathing.cpp index afb5afeb..2526487c 100644 --- a/Strategic/Strategic Pathing.cpp +++ b/Strategic/Strategic Pathing.cpp @@ -1221,10 +1221,28 @@ INT16 GetLastSectorIdInVehiclePath( INT32 iId ) } return sLastSector; - - } +// get id of last sector in group'S path list +INT16 GetLastSectorIdInMilitiaGroupPath( UINT8 uGroupId ) +{ + INT16 sLastSector = -1; + + INT16 militiapathslot = GetMilitiaPathSlot( uGroupId ); + + if ( militiapathslot > -1 ) + { + PathStPtr pNode = gMilitiaPath[militiapathslot].path; + + while ( pNode ) + { + sLastSector = (INT16)(pNode->uiSectorId); + pNode = pNode->pNext; + } + } + + return sLastSector; +} PathStPtr CopyPaths( PathStPtr pSourcePath, PathStPtr pDestPath ) @@ -1974,32 +1992,44 @@ void ClearMercPathsAndWaypointsForAllInGroup( GROUP *pGroup ) PLAYERGROUP *pPlayer = NULL; SOLDIERTYPE *pSoldier = NULL; - pPlayer = pGroup->pPlayerList; - while( pPlayer ) + if ( pGroup->usGroupTeam == OUR_TEAM ) { - pSoldier = pPlayer->pSoldier; - - if ( pSoldier != NULL ) + pPlayer = pGroup->pPlayerList; + while( pPlayer ) { - ClearPathForSoldier( pSoldier ); + pSoldier = pPlayer->pSoldier; + + if ( pSoldier != NULL ) + { + ClearPathForSoldier( pSoldier ); + } + + pPlayer = pPlayer->next; } - pPlayer = pPlayer->next; + // if it's a vehicle + if ( pGroup->fVehicle ) + { + INT32 iVehicleId = -1; + VEHICLETYPE *pVehicle = NULL; + + iVehicleId = GivenMvtGroupIdFindVehicleId( pGroup->ubGroupID ); + Assert ( iVehicleId != -1 ); + + pVehicle = &( pVehicleList[ iVehicleId ] ); + + // clear the path for that vehicle + pVehicle->pMercPath = ClearStrategicPathList( pVehicle->pMercPath, pVehicle->ubMovementGroup ); + } } - - // if it's a vehicle - if ( pGroup->fVehicle ) + else if ( pGroup->usGroupTeam == MILITIA_TEAM ) { - INT32 iVehicleId = -1; - VEHICLETYPE *pVehicle = NULL; + INT16 militiapathslot = GetMilitiaPathSlot( pGroup->ubGroupID ); - iVehicleId = GivenMvtGroupIdFindVehicleId( pGroup->ubGroupID ); - Assert ( iVehicleId != -1 ); - - pVehicle = &( pVehicleList[ iVehicleId ] ); - - // clear the path for that vehicle - pVehicle->pMercPath = ClearStrategicPathList( pVehicle->pMercPath, pVehicle->ubMovementGroup ); + if ( militiapathslot > -1 ) + { + gMilitiaPath[militiapathslot].path = ClearStrategicPathList( gMilitiaPath[militiapathslot].path, pGroup->ubGroupID ); + } } // clear the waypoints for this group too - no mercpath = no waypoints! diff --git a/Strategic/Strategic Pathing.h b/Strategic/Strategic Pathing.h index 0a16785a..ff7ce833 100644 --- a/Strategic/Strategic Pathing.h +++ b/Strategic/Strategic Pathing.h @@ -73,6 +73,9 @@ INT16 GetLastSectorIdInCharactersPath( SOLDIERTYPE *pCharacter ); // get id of last sector in mercs path list INT16 GetLastSectorIdInVehiclePath( INT32 iId ); +// get id of last sector in group'S path list +INT16 GetLastSectorIdInMilitiaGroupPath( UINT8 uGroupId ); + // copy paths PathStPtr CopyPaths( PathStPtr pSourcePath, PathStPtr pDestPath ); diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 49b79854..2e1c902f 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -10041,6 +10041,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; // WANNE: Some Chinese specific strings that needs to be in unicode! diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index c46f132a..0605c019 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -10052,6 +10052,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 727b30f1..971983ce 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -10090,6 +10090,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 7f124e8d..31a1eee1 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -10038,6 +10038,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 2d3e8a3f..3656bd1d 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -9869,6 +9869,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 14fe42df..abfc83ee 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -10048,6 +10048,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 01a73fa5..08c51378 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -10063,6 +10063,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", }; #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index f7e5de2a..fd3ba96b 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -10036,6 +10036,7 @@ STR16 szMilitiaStrategicMovementText[] = L"ETA", L"Group %d (new)", L"Group %d", + L"Final", };