diff --git a/GameVersion.h b/GameVersion.h index cd0ddd01..14f592fe 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -21,6 +21,7 @@ extern CHAR16 zTrackingNumber[16]; // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file +#define HACKABLE_SAMS 172 // Flugente: SAM sites now have a status for hacking #define LUA_MODDERDATA 171 // Flugente: modders can set and use data in an exclusive array #define WORKERS 170 // Flugente: train workers for facilities #define WEATHER_CHANGE 169 // Flugente: sector-specific weather & more weather types @@ -90,7 +91,7 @@ extern CHAR16 zTrackingNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system -#define SAVE_GAME_VERSION LUA_MODDERDATA +#define SAVE_GAME_VERSION HACKABLE_SAMS //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/Strategic/ASD.cpp b/Strategic/ASD.cpp index d804cf49..15487ac6 100644 --- a/Strategic/ASD.cpp +++ b/Strategic/ASD.cpp @@ -59,8 +59,6 @@ extern INT32 giReinforcementPool; extern BOOLEAN gfTacticalDoHeliRun; -extern UINT8 ubSAMControlledSectors[MAP_WORLD_Y][MAP_WORLD_X]; - // Arulco special division decision code // ASD savegame variables @@ -1007,69 +1005,75 @@ void EnemyHeliSAMCheck( INT16 id ) INT8 heli_y = SECTORY( heli.sector_current ); // is the airspace in our current sector player-controlled and operational? - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( heli_x, heli_y )].usAirType == AIRSPACE_PLAYER_ACTIVE ) + if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( heli_x, heli_y )].usAirType & AIRSPACE_PLAYER_ACTIVE ) { - UINT8 ubControllingSAM = ubSAMControlledSectors[heli_y][heli_x]; - - StrategicMapElement *pSAMStrategicMap = &(StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ubControllingSAM - 1] )]); - - if ( pSAMStrategicMap ) + for ( int i = 0; i < NUMBER_OF_SAMS; ++i ) { - UINT16 ubBestSAMOperatorID = NOBODY; - FLOAT samcth = GetBestSAMOperatorCTH_Player( gpSamSectorX[ubControllingSAM - 1], gpSamSectorY[ubControllingSAM - 1], 0, &ubBestSAMOperatorID ); - - // cth is reduced if SAM is damaged, even if it can still operate - samcth = (samcth * pSAMStrategicMap->bSAMCondition) / 100.0f; - - // determine distance from SAM to heli - FLOAT distance = sqrt(FLOAT(abs( heli_x - gpSamSectorX[ubControllingSAM - 1] ) * abs( heli_x - gpSamSectorX[ubControllingSAM - 1] ) + abs( heli_y - gpSamSectorY[ubControllingSAM - 1] ) * abs( heli_y - gpSamSectorY[ubControllingSAM - 1] ) )); - - // distance penalty - samcth = (samcth * (100.0f - 8 * distance)) / 100.0f; - - // if cth is too low, we don't fire - if ( samcth > 0.01 ) + BOOLEAN samworking = FALSE; + if ( DoesSamCoverSector( i, heli.sector_current, &samworking ) && samworking ) { - CHAR16 pStrSectorName[128]; - GetSectorIDString( heli_x, heli_y, 0, pStrSectorName, FALSE ); + StrategicMapElement *pSAMStrategicMap = &(StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[i] )]); - CHAR16 pStrSectorName_SAM[128]; - GetSectorIDString( gpSamSectorX[ubControllingSAM - 1], gpSamSectorY[ubControllingSAM - 1], 0, pStrSectorName_SAM, FALSE ); - - MapScreenMessage( FONT_MCOLOR_LTRED, MSG_INTERFACE, szEnemyHeliText[8], pStrSectorName_SAM, pStrSectorName ); - - // if we hit, damage the heli - BOOLEAN fHit = FALSE; - BOOLEAN fDestroyed = FALSE; - - if ( Chance( (UINT32)(samcth) ) ) + // also check whether this sector is not enemy-controlled - we can't have the AI shooting at its own helis + if ( pSAMStrategicMap && !pSAMStrategicMap->fEnemyControlled ) { - fHit = TRUE; + UINT16 ubBestSAMOperatorID = NOBODY; + FLOAT samcth = GetBestSAMOperatorCTH_Player( gpSamSectorX[i], gpSamSectorY[i], 0, &ubBestSAMOperatorID ); - UINT8 damage = gGameExternalOptions.gEnemyHeliSAMDamage_Base + Random( gGameExternalOptions.gEnemyHeliSAMDamage_Var ); + // cth is reduced if SAM is damaged, even if it can still operate + samcth = (samcth * pSAMStrategicMap->bSAMCondition) / 100.0f; - MapScreenMessage( FONT_MCOLOR_LTRED, MSG_INTERFACE, szEnemyHeliText[6], pStrSectorName ); + // determine distance from SAM to heli + FLOAT distance = sqrt( FLOAT( abs( heli_x - gpSamSectorX[i] ) * abs( heli_x - gpSamSectorX[i] ) + abs( heli_y - gpSamSectorY[i] ) * abs( heli_y - gpSamSectorY[i] ) ) ); - heli.hp = max( 0, heli.hp - damage ); + // distance penalty + samcth = (samcth * (100.0f - 8 * distance)) / 100.0f; - if ( !heli.hp ) + // if cth is too low, we don't fire + if ( samcth > 0.01 ) { - fDestroyed = TRUE; + CHAR16 pStrSectorName[128]; + GetSectorIDString( heli_x, heli_y, 0, pStrSectorName, FALSE ); - heli.flagmask |= ENEMYHELI_SHOTDOWN; + CHAR16 pStrSectorName_SAM[128]; + GetSectorIDString( gpSamSectorX[i], gpSamSectorY[i], 0, pStrSectorName_SAM, FALSE ); - heli.Destroy( ); + MapScreenMessage( FONT_MCOLOR_LTRED, MSG_INTERFACE, szEnemyHeliText[8], pStrSectorName_SAM, pStrSectorName ); + + // if we hit, damage the heli + BOOLEAN fHit = FALSE; + BOOLEAN fDestroyed = FALSE; + + if ( Chance( (UINT32)(samcth) ) ) + { + fHit = TRUE; + + UINT8 damage = gGameExternalOptions.gEnemyHeliSAMDamage_Base + Random( gGameExternalOptions.gEnemyHeliSAMDamage_Var ); + + MapScreenMessage( FONT_MCOLOR_LTRED, MSG_INTERFACE, szEnemyHeliText[6], pStrSectorName ); + + heli.hp = max( 0, heli.hp - damage ); + + if ( !heli.hp ) + { + fDestroyed = TRUE; + + heli.flagmask |= ENEMYHELI_SHOTDOWN; + + heli.Destroy( ); + } + } + + // depending on our result, award experience to the operator + if ( ubBestSAMOperatorID != NOBODY ) + { + if ( fHit ) + StatChange( MercPtrs[ubBestSAMOperatorID], EXPERAMT, fDestroyed ? 30 : 10, TRUE ); + + StatChange( MercPtrs[ubBestSAMOperatorID], MECHANAMT, 5, TRUE ); + } } } - - // depending on our result, award experience to the operator - if ( ubBestSAMOperatorID != NOBODY ) - { - if ( fHit ) - StatChange( MercPtrs[ubBestSAMOperatorID], EXPERAMT, fDestroyed ? 30 : 10, TRUE ); - - StatChange( MercPtrs[ubBestSAMOperatorID], MECHANAMT, 5, TRUE ); - } } } } @@ -1230,17 +1234,23 @@ void EnemyHeliCheckPlayerKnowledge( INT16 id ) } // is the airspace in our current sector player-controlled and operational? - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( current_x, current_y )].usAirType == AIRSPACE_PLAYER_ACTIVE ) + if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( current_x, current_y )].usAirType & AIRSPACE_PLAYER_ACTIVE ) { - UINT8 ubControllingSAM = ubSAMControlledSectors[current_y][current_x]; - - // if mercs man the SAM, they will let us know of the new SAM - if ( PlayerMercsInSector( gpSamSectorX[ubControllingSAM - 1], gpSamSectorY[ubControllingSAM - 1], 0 ) ) + for ( int i = 0; i < NUMBER_OF_SAMS; ++i ) { - // if we previously did not know of the heli, our mercs will alert us - if ( !(heli.flagmask & ENEMYHELI_KNOWNTOPLAYER) ) + if ( DoesSamCoverSector( i, heli.sector_current ) ) { - SayQuoteFromAnyBodyInThisSector( gpSamSectorX[ubControllingSAM - 1], gpSamSectorY[ubControllingSAM - 1], 0, QUOTE_AIR_RAID ); + // if mercs man the SAM, they will let us know of the new SAM + if ( PlayerMercsInSector( gpSamSectorX[i], gpSamSectorY[i], 0 ) ) + { + // if we previously did not know of the heli, our mercs will alert us + if ( !(heli.flagmask & ENEMYHELI_KNOWNTOPLAYER) ) + { + SayQuoteFromAnyBodyInThisSector( gpSamSectorX[i], gpSamSectorY[i], 0, QUOTE_AIR_RAID ); + + break; + } + } } } @@ -1265,11 +1275,6 @@ void RepairSamSite( UINT8 aSector ) } } -BOOLEAN IsSectorAirSpacePlayerControlled( UINT8 aSector ) -{ - return (StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( aSector )].usAirType == AIRSPACE_PLAYER_ACTIVE); -} - UINT8 NumPlayerAirSpaceOnHeliPath( UINT8 aStart, UINT8 aEnd ) { UINT8 samcontacts = 0; @@ -1280,7 +1285,7 @@ UINT8 NumPlayerAirSpaceOnHeliPath( UINT8 aStart, UINT8 aEnd ) { tmpsect = GetNextEnemyHeliSector( tmpsect, aEnd ); - if ( IsSectorAirSpacePlayerControlled( tmpsect ) ) + if ( StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( tmpsect )].usAirType & AIRSPACE_PLAYER_ACTIVE ) ++samcontacts; } diff --git a/Strategic/ASD.h b/Strategic/ASD.h index c4dfa1a1..20da9679 100644 --- a/Strategic/ASD.h +++ b/Strategic/ASD.h @@ -101,8 +101,6 @@ void EnemyHeliCheckPlayerKnowledge( INT16 id ); void RepairSamSite( UINT8 aSector ); -BOOLEAN IsSectorAirSpacePlayerControlled( UINT8 aSector ); - UINT8 NumPlayerAirSpaceOnHeliPath( UINT8 aStart, UINT8 aEnd ); // get the next sector in an enemy heli flight path. diff --git a/Strategic/LuaInitNPCs.cpp b/Strategic/LuaInitNPCs.cpp index de318822..6ec1fbeb 100644 --- a/Strategic/LuaInitNPCs.cpp +++ b/Strategic/LuaInitNPCs.cpp @@ -860,6 +860,10 @@ static int l_GetUsedLanguage( lua_State *L ); static int l_DoInteractiveActionDefaultResult( lua_State *L ); static int l_GiveExp( lua_State *L ); +static int l_NumNonPlayerTeamInSector( lua_State *L ); +static int l_SetSamSiteHackStatus( lua_State *L ); +static int l_GetSamSiteHackStatus( lua_State *L ); + using namespace std; UINT16 idProfil; @@ -1728,6 +1732,10 @@ void IniFunction(lua_State *L, BOOLEAN bQuests ) lua_register( L, "DoInteractiveActionDefaultResult", l_DoInteractiveActionDefaultResult ); lua_register( L, "GiveExp", l_GiveExp ); + + lua_register( L, "NumNonPlayerTeamInSector", l_NumNonPlayerTeamInSector ); + lua_register( L, "SetSamSiteHackStatus", l_SetSamSiteHackStatus ); + lua_register( L, "GetSamSiteHackStatus", l_GetSamSiteHackStatus ); } #ifdef NEWMUSIC BOOLEAN LetLuaMusicControl(UINT8 Init) @@ -7252,7 +7260,7 @@ static int l_ACTION_ITEM_LOCAL_ALARM (lua_State *L) { if ( lua_gettop(L) >= 1 ) { - UINT32 sGridNo = lua_tointeger(L, 1); + INT32 sGridNo = lua_tointeger(L, 1); // Flugente: check for valid sGridNo if ( TileIsOutOfBounds(sGridNo) ) @@ -13296,3 +13304,46 @@ static int l_GiveExp( lua_State *L ) return 0; } + +static int l_NumNonPlayerTeamInSector( lua_State *L ) +{ + if ( lua_gettop( L ) >= 3 ) + { + INT16 sSectorX = lua_tointeger( L, 1 ); + INT16 sSectorY = lua_tointeger( L, 2 ); + UINT8 ubTeam = lua_tointeger( L, 3 ); + + UINT16 numteam = NumNonPlayerTeamMembersInSector( sSectorX, sSectorY, ubTeam ); + + lua_pushinteger( L, numteam ); + } + + return 1; +} + +static int l_SetSamSiteHackStatus( lua_State *L ) +{ + if ( lua_gettop( L ) >= 3 ) + { + INT16 sSectorX = lua_tointeger( L, 1 ); + INT16 sSectorY = lua_tointeger( L, 2 ); + INT8 sStatus = lua_tointeger( L, 3 ); + + SetSamHackStatus( sSectorX, sSectorY, sStatus ); + } + + return 0; +} + +static int l_GetSamSiteHackStatus( lua_State *L ) +{ + if ( lua_gettop( L ) >= 2 ) + { + INT16 sSectorX = lua_tointeger( L, 1 ); + INT16 sSectorY = lua_tointeger( L, 2 ); + + lua_pushinteger( L, StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].sSamHackStatus ); + } + + return 1; +} diff --git a/Strategic/Map Screen Helicopter.cpp b/Strategic/Map Screen Helicopter.cpp index 676b0ae6..503dbe79 100644 --- a/Strategic/Map Screen Helicopter.cpp +++ b/Strategic/Map Screen Helicopter.cpp @@ -81,8 +81,6 @@ extern UINT8 gubCurrentTalkingID; // current temp path for dest char extern PathStPtr pTempHelicopterPath; -extern UINT8 ubSAMControlledSectors[ MAP_WORLD_Y ][ MAP_WORLD_X ]; - // the seating capacities //extern INT32 iSeatingCapacities[]; @@ -1131,17 +1129,14 @@ INT32 GetCostOfPassageForHelicopter( INT16 sX, INT16 sY ) INT32 iCost = 0; // HEADROCK HAM 3.5: Costs externalized - INT32 iBaseCostPerGreenTile = gGameExternalOptions.usHelicopterBaseCostPerGreenTile; - INT32 iBaseCostPerRedTile = gGameExternalOptions.usHelicopterBaseCostPerRedTile; - // if they don't control it - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sX, sY )].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( sX, sY )].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { - iCost = iBaseCostPerGreenTile; + iCost = gGameExternalOptions.usHelicopterBaseCostPerGreenTile; } else { - iCost = iBaseCostPerRedTile; + iCost = gGameExternalOptions.usHelicopterBaseCostPerRedTile; } // HEADROCK HAM 3.5: Apply facility-based modifiers from global integer recalculated hourly @@ -1349,7 +1344,7 @@ void HandleHeliHoverForAMinute( void ) gubHelicopterHoverTime = 0; iTotalHeliDistanceSinceRefuel++; - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sX, sY )].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( sX, sY )].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { iTotalAccumulatedCostByPlayer += gGameExternalOptions.usHelicopterHoverCostOnGreenTile; } @@ -2412,65 +2407,46 @@ BOOLEAN IsHelicopterOnGroundAtRefuelingSite( UINT8 ubRefuelingSite ) BOOLEAN WillAirRaidBeStopped( INT16 sSectorX, INT16 sSectorY ) { - UINT8 ubSamNumber = 0; - INT8 bSAMCondition; UINT8 ubChance; + if ( !StrategicMap[(AIRPORT_X + (MAP_WORLD_X * AIRPORT_Y))].fEnemyControlled && !StrategicMap[(AIRPORT2_X + (MAP_WORLD_X * AIRPORT2_Y))].fEnemyControlled ) + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "WillAirRaidBeStopped: enemy has no more airports" ) ); + return(TRUE); + } DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "WillAirRaidBeStopped: enemy air controlled = %d", StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].usAirType ) ); + // if enemy controls this SAM site, then it can't stop an air raid - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].usAirType != AIRSPACE_PLAYER_ACTIVE ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].usAirType & AIRSPACE_PLAYER_ACTIVE) ) { return( FALSE ); } - if( !StrategicMap[ ( AIRPORT_X + ( MAP_WORLD_X * AIRPORT_Y ) ) ].fEnemyControlled && !StrategicMap[ ( AIRPORT2_X + ( MAP_WORLD_X * AIRPORT2_Y ) ) ].fEnemyControlled ) - { - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: enemy has no more airports")); - return( TRUE ); - } - - - // which SAM controls this sector? - ubSamNumber = ubSAMControlledSectors[ sSectorY ][ sSectorX ]; - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: SAM number = %d",ubSamNumber)); - // if none of them - if (ubSamNumber == 0) - { - return( FALSE); - } - - // get the condition of that SAM site (NOTE: SAM #s are 1-4, but indexes are 0-3!!!) - Assert( ubSamNumber <= NUMBER_OF_SAMS ); - bSAMCondition = StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ ubSamNumber - 1 ] ) ].bSAMCondition; - - // if it's too busted to work, then it can't stop an air raid - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: SAM condition = %d",bSAMCondition)); - if( bSAMCondition < MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) - { - // no problem, SAM site not working - return( FALSE ); - } - - // Friendly airspace controlled by a working SAM site, so SAM site fires a SAM at air raid bomber - // calc chance that chopper will be shot down - ubChance = bSAMCondition; - - // there's a fair chance of a miss even if the SAM site is in perfect working order - if (ubChance > gHelicopterSettings.ubHelicopterSAMSiteAccuracy * 3) // Madd - since this is only used for enemy air raids, we'll say that good guy SAMs can have a max of 99% to hit instead of 33% + for ( int i = 0; i < NUMBER_OF_SAMS; ++i ) { - ubChance = gHelicopterSettings.ubHelicopterSAMSiteAccuracy * 3; - } + BOOLEAN samworking = FALSE; + if ( DoesSamCoverSector( i, SECTOR( sSectorX, sSectorY ), &samworking ) && samworking ) + { + // calc chance that chopper will be shot down + ubChance = StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[i] )].bSAMCondition; - if( PreRandom( 100 ) < ubChance) - { - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: return true")); - return( TRUE ); - } + // there's a fair chance of a miss even if the SAM site is in perfect working order + if ( ubChance > gHelicopterSettings.ubHelicopterSAMSiteAccuracy * 3 ) // Madd - since this is only used for enemy air raids, we'll say that good guy SAMs can have a max of 99% to hit instead of 33% + { + ubChance = gHelicopterSettings.ubHelicopterSAMSiteAccuracy * 3; + } + if ( PreRandom( 100 ) < ubChance ) + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "WillAirRaidBeStopped: return true" ) ); + return(TRUE); + } + } + } + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("WillAirRaidBeStopped: return false")); return( FALSE ); } @@ -2483,104 +2459,94 @@ void HeliCrashSoundStopCallback( void *pData ) BOOLEAN HandleSAMSiteAttackOfHelicopterInSector( INT16 sSectorX, INT16 sSectorY ) { - UINT8 ubSamNumber = 0; + UINT32 ubSamNumber = 0; INT8 bSAMCondition; UINT8 ubChance; +#ifdef JA2TESTVERSION + if ( fSAMSitesDisabledFromAttackingPlayer == TRUE ) + { + return(FALSE); + } +#endif + // if this sector is in friendly airspace, we're safe - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { // no problem, friendly airspace return( FALSE ); } - // which SAM controls this sector? - ubSamNumber = ubSAMControlledSectors[ sSectorY ][ sSectorX ]; - - // if none of them - if (ubSamNumber == 0) - { - return( FALSE); - } - - // get the condition of that SAM site (NOTE: SAM #s are 1-4, but indexes are 0-3!!!) - Assert( ubSamNumber <= NUMBER_OF_SAMS ); - bSAMCondition = StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ ubSamNumber - 1 ] ) ].bSAMCondition; - - // if the SAM site is too damaged to be a threat - if( bSAMCondition < MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) - { - // no problem, SAM site not working - return( FALSE ); - } - -#ifdef JA2TESTVERSION - if( fSAMSitesDisabledFromAttackingPlayer == TRUE ) - { - return( FALSE ); - } -#endif // Hostile airspace controlled by a working SAM site, so SAM site fires a SAM at Skyrider!!! - // calc chance that chopper will be shot down - ubChance = bSAMCondition; - - // there's a fair chance of a miss even if the SAM site is in perfect working order - if (ubChance > gHelicopterSettings.ubHelicopterSAMSiteAccuracy) + for ( int i = 0; i < NUMBER_OF_SAMS; ++i ) { - ubChance = gHelicopterSettings.ubHelicopterSAMSiteAccuracy; - } - - if( PreRandom( 100 ) < ubChance) - { - // another hit! - gubHelicopterHitsTaken++; - - // Took a hit! Pause time so player can reconsider - StopTimeCompression(); - - // first hit? - if ( gubHelicopterHitsTaken == 1 ) + BOOLEAN samworking = FALSE; + if ( DoesSamCoverSector( i, SECTOR( sSectorX, sSectorY ), &samworking ) && samworking ) { - HeliCharacterDialogue( pSkyRider, HELI_TOOK_MINOR_DAMAGE ); - if( gGameExternalOptions.fHelicopterPassengersCanGetHit == TRUE ) - HurtPassengersInHelicopter( iHelicopterVehicleId ); - } - // second hit? - else if ( gubHelicopterHitsTaken == 2 ) - { - // going back to base (no choice, dialogue says so) - HeliCharacterDialogue( pSkyRider, HELI_TOOK_MAJOR_DAMAGE ); - MakeHeliReturnToBase( HELICOPTER_RETURN_REASON_DAMAGE ); - if( gGameExternalOptions.fHelicopterPassengersCanGetHit == TRUE ) - HurtPassengersInHelicopter( iHelicopterVehicleId ); - } - // third hit! - else - { - // Important: Skyrider must still be alive when he talks, so must do this before heli is destroyed! - HeliCharacterDialogue( pSkyRider, HELI_GOING_DOWN ); + bSAMCondition = StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[i] )].bSAMCondition; - // everyone die die die - // play sound - // if ( PlayJA2StreamingSampleFromFile( "stsounds\\blah2.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN, HeliCrashSoundStopCallback ) == SOUND_ERROR ) - //{ - //// Destroy here if we cannot play streamed sound sample.... - // SkyriderDestroyed( ); - //} - //else - //{ - //// otherwise it's handled in the callback - //// remove any arrival events for the helicopter's group - //DeleteStrategicEvent( EVENT_GROUP_ARRIVAL, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); - //} - // anv - calling SkyriderDestroyed() in callback from playing sound causes sound system to crash, as SkyriderDestroyed causes new sounds (hit battlesnds) to play while blah2 is not removed yet - // + if there's a pause while falling heli sound is played, it's possible to cheat by changing passengers assignments - PlayJA2StreamingSampleFromFile( "stsounds\\blah2.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN, HeliCrashSoundStopCallback ); - SkyriderDestroyed( ); + // calc chance that chopper will be shot down + ubChance = bSAMCondition; - // special return code indicating heli was destroyed - return( TRUE ); + // there's a fair chance of a miss even if the SAM site is in perfect working order + if ( ubChance > gHelicopterSettings.ubHelicopterSAMSiteAccuracy ) + { + ubChance = gHelicopterSettings.ubHelicopterSAMSiteAccuracy; + } + + if ( PreRandom( 100 ) < ubChance ) + { + // another hit! + ++gubHelicopterHitsTaken; + + // Took a hit! Pause time so player can reconsider + StopTimeCompression( ); + + // first hit? + if ( gubHelicopterHitsTaken == 1 ) + { + HeliCharacterDialogue( pSkyRider, HELI_TOOK_MINOR_DAMAGE ); + if ( gGameExternalOptions.fHelicopterPassengersCanGetHit == TRUE ) + HurtPassengersInHelicopter( iHelicopterVehicleId ); + } + // second hit? + else if ( gubHelicopterHitsTaken == 2 ) + { + // going back to base (no choice, dialogue says so) + HeliCharacterDialogue( pSkyRider, HELI_TOOK_MAJOR_DAMAGE ); + MakeHeliReturnToBase( HELICOPTER_RETURN_REASON_DAMAGE ); + if ( gGameExternalOptions.fHelicopterPassengersCanGetHit == TRUE ) + HurtPassengersInHelicopter( iHelicopterVehicleId ); + } + // third hit! + else + { + // Important: Skyrider must still be alive when he talks, so must do this before heli is destroyed! + HeliCharacterDialogue( pSkyRider, HELI_GOING_DOWN ); + + // everyone die die die + // play sound + // if ( PlayJA2StreamingSampleFromFile( "stsounds\\blah2.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN, HeliCrashSoundStopCallback ) == SOUND_ERROR ) + //{ + //// Destroy here if we cannot play streamed sound sample.... + // SkyriderDestroyed( ); + //} + //else + //{ + //// otherwise it's handled in the callback + //// remove any arrival events for the helicopter's group + //DeleteStrategicEvent( EVENT_GROUP_ARRIVAL, pVehicleList[ iHelicopterVehicleId ].ubMovementGroup ); + //} + // anv - calling SkyriderDestroyed() in callback from playing sound causes sound system to crash, as SkyriderDestroyed causes new sounds (hit battlesnds) to play while blah2 is not removed yet + // + if there's a pause while falling heli sound is played, it's possible to cheat by changing passengers assignments + PlayJA2StreamingSampleFromFile( "stsounds\\blah2.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN, HeliCrashSoundStopCallback ); + SkyriderDestroyed( ); + + // special return code indicating heli was destroyed + return(TRUE); + } + } } } @@ -2833,7 +2799,7 @@ INT16 GetNumSafeSectorsInPath( void ) { uiLocation = pNode->uiSectorId; - if ( StrategicMap[uiLocation].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[uiLocation].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { ++uiCount; } @@ -2861,7 +2827,7 @@ INT16 GetNumSafeSectorsInPath( void ) { uiLocation = pNode->uiSectorId; - if ( StrategicMap[uiLocation].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[uiLocation].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { ++uiCount; } @@ -2915,7 +2881,7 @@ INT16 GetNumUnSafeSectorsInPath( void ) { uiLocation = pNode->uiSectorId; - if ( StrategicMap[uiLocation].usAirType == AIRSPACE_ENEMY_ACTIVE ) + if ( StrategicMap[uiLocation].usAirType & AIRSPACE_ENEMY_ACTIVE ) { ++uiCount; } @@ -2942,7 +2908,7 @@ INT16 GetNumUnSafeSectorsInPath( void ) { uiLocation = pNode->uiSectorId; - if ( StrategicMap[uiLocation].usAirType == AIRSPACE_ENEMY_ACTIVE ) + if ( StrategicMap[uiLocation].usAirType & AIRSPACE_ENEMY_ACTIVE ) { ++uiCount; } diff --git a/Strategic/Map Screen Interface Border.cpp b/Strategic/Map Screen Interface Border.cpp index c679ee06..f95bc2ef 100644 --- a/Strategic/Map Screen Interface Border.cpp +++ b/Strategic/Map Screen Interface Border.cpp @@ -775,7 +775,7 @@ void ToggleShowTeamsMode( void ) void ToggleAirspaceMode( void ) { - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // turn airspace OFF gusMapDisplayColourMode = MAP_DISPLAY_NORMAL; @@ -1006,23 +1006,26 @@ void TurnOnAirSpaceMode( void ) { // if mode already on, leave, else set and redraw - if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE ) - { + if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE && gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) gusMapDisplayColourMode = MAP_DISPLAY_AIRSPACE; + else if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + gusMapDisplayColourMode = MAP_DISPLAY_AIRSPACE_COLOURED_SAMS; + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) + { MapBorderButtonOn( MAP_BORDER_AIRSPACE_BTN ); MapBorderButtonOff( MAP_BORDER_MOBILE_BTN ); MapBorderButtonOff( MAP_BORDER_DISEASE_BTN ); MapBorderButtonOff( MAP_BORDER_WEATHER_BTN ); // Turn off towns & mines (mostly because town/mine names overlap SAM site names) - if( fShowTownFlag == TRUE ) + if( fShowTownFlag ) { fShowTownFlag = FALSE; MapBorderButtonOff( MAP_BORDER_TOWN_BTN ); } - if( fShowMineFlag == TRUE ) + if( fShowMineFlag ) { fShowMineFlag = FALSE; MapBorderButtonOff( MAP_BORDER_MINE_BTN ); @@ -1044,7 +1047,7 @@ void TurnOnAirSpaceMode( void ) */ // Turn off items - if( fShowItemsFlag == TRUE ) + if( fShowItemsFlag ) { fShowItemsFlag = FALSE; MapBorderButtonOff( MAP_BORDER_ITEM_BTN ); @@ -1390,6 +1393,7 @@ void InitializeMapBorderButtonStates( void ) switch ( gusMapDisplayColourMode ) { case MAP_DISPLAY_AIRSPACE: + case MAP_DISPLAY_AIRSPACE_COLOURED_SAMS: MapBorderButtonOn( MAP_BORDER_AIRSPACE_BTN ); MapBorderButtonOff( MAP_BORDER_MOBILE_BTN ); MapBorderButtonOff( MAP_BORDER_DISEASE_BTN ); diff --git a/Strategic/Map Screen Interface Border.h b/Strategic/Map Screen Interface Border.h index c9ac453e..50c62630 100644 --- a/Strategic/Map Screen Interface Border.h +++ b/Strategic/Map Screen Interface Border.h @@ -56,6 +56,7 @@ enum { MAP_DISPLAY_NORMAL, MAP_DISPLAY_AIRSPACE, + MAP_DISPLAY_AIRSPACE_COLOURED_SAMS, MAP_DISPLAY_MOBILEMILITIARESTRICTIONS, MAP_DISPLAY_DISEASE, MAP_DISPLAY_WEATHER, diff --git a/Strategic/Map Screen Interface Map.cpp b/Strategic/Map Screen Interface Map.cpp index e2fbc28b..f9ed56d6 100644 --- a/Strategic/Map Screen Interface Map.cpp +++ b/Strategic/Map Screen Interface Map.cpp @@ -693,7 +693,6 @@ void HandleShowingOfEnemiesWithMilitiaOn( void ) } } - UINT32 DrawMap( void ) { HVSURFACE hSrcVSurface; @@ -731,6 +730,11 @@ UINT32 DrawMap( void ) UnLockVideoSurface( guiBIGMAP ); UnLockVideoSurface( guiSAVEBUFFER ); + // draw SAM sites + //ShowSAMSitesOnStrategicMap( ); + + //UpdateAirspaceControl(); + if ( !iCurrentMapSectorZ ) { // shade map sectors (must be done after Tixa/Orta/Mine icons have been blitted, but before icons!) @@ -744,42 +748,99 @@ UINT32 DrawMap( void ) switch ( gusMapDisplayColourMode ) { case MAP_DISPLAY_AIRSPACE: - if ( GetSectorFlagStatus( cnt, cnt2, (UINT8)iCurrentMapSectorZ, SF_ALREADY_VISITED ) == FALSE ) { - if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType == AIRSPACE_PLAYER_ACTIVE ) + BOOLEAN fAlreadyVisited = GetSectorFlagStatus( cnt, cnt2, (UINT8)iCurrentMapSectorZ, SF_ALREADY_VISITED ); + + if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType & AIRSPACE_PLAYER_ACTIVE ) { - // sector not visited but air controlled - ShadeMapElem( cnt, cnt2, MAP_SHADE_DK_GREEN ); + if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType & AIRSPACE_ENEMY_ACTIVE ) + { + // sector not visited and not air controlled + ShadeMapElem( cnt, cnt2, fAlreadyVisited ? MAP_SHADE_LT_YELLOW : MAP_SHADE_DK_YELLOW ); + } + else + { + // sector not visited but air controlled + ShadeMapElem( cnt, cnt2, fAlreadyVisited ? MAP_SHADE_LT_GREEN : MAP_SHADE_DK_GREEN ); + } } - else if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType == AIRSPACE_ENEMY_ACTIVE ) + else if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType & AIRSPACE_ENEMY_ACTIVE ) { // sector not visited and not air controlled - ShadeMapElem( cnt, cnt2, MAP_SHADE_DK_RED ); + ShadeMapElem( cnt, cnt2, fAlreadyVisited ? MAP_SHADE_LT_RED : MAP_SHADE_DK_RED ); } else { // sector not visited, currently nobody controls the airspace - ShadeMapElem( cnt, cnt2, MAP_SHADE_MD_BLUE ); + //ShadeMapElem( cnt, cnt2, fAlreadyVisited ? MAP_SHADE_LT_BLUE : MAP_SHADE_MD_BLUE ); } } - else + break; + + case MAP_DISPLAY_AIRSPACE_COLOURED_SAMS: { - if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType == AIRSPACE_PLAYER_ACTIVE ) + CHAR16 sString[20]; + swprintf( sString, L"%d", ubSAMControlledSectors[cnt2][cnt] ); + + BOOLEAN a = FALSE; + BOOLEAN b = FALSE; + BOOLEAN c = FALSE; + BOOLEAN d = FALSE; + { - // sector visited and air controlled - ShadeMapElem( cnt, cnt2, MAP_SHADE_LT_GREEN ); + for ( int i = 0; i < NUMBER_OF_SAMS; ++i ) + { + BOOLEAN samworking = FALSE; + if ( DoesSamCoverSector( i, SECTOR( cnt, cnt2 ), &samworking ) && samworking ) + { + if ( i == 0 ) a = TRUE; + if ( i == 1 ) b = TRUE; + if ( i == 2 ) c = TRUE; + if ( i == 3 ) d = TRUE; + } + } } - else if ( StrategicMap[cnt + cnt2 * WORLD_MAP_X].usAirType == AIRSPACE_ENEMY_ACTIVE ) - { - // sector visited but not air controlled - ShadeMapElem( cnt, cnt2, MAP_SHADE_LT_RED ); - } - else - { - // sector visited, currently nobody controls the airspace - ShadeMapElem( cnt, cnt2, MAP_SHADE_LT_BLUE ); - } - } + + if ( a && !b && !c && !d) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED ); + else if ( !a && b && !c && !d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_GREEN ); + else if ( !a && !b && c && !d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_BLUE ); + else if ( !a && !b && !c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_YELLOW ); + else if ( a && b && !c && !d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_GREEN ); + else if ( a && !b && c && !d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_BLUE ); + else if ( a && !b && !c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_YELLOW ); + else if ( !a && b && c && !d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_GREEN_BLUE ); + else if ( !a && b && !c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_GREEN_YELLOW ); + else if ( !a && !b && c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_BLUE_YELLOW ); + else if ( a && b && c && !d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_GREEN_BLUE ); + else if ( a && b && !c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_GREEN_YELLOW ); + else if ( a && !b && c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_BLUE_YELLOW ); + else if ( !a && b && c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_GREEN_BLUE_YELLOW ); + else if ( a && b && c && d ) + ShadeMapElem( cnt, cnt2, MAP_SHADE_MIX_RED_GREEN_BLUE_YELLOW ); + + INT16 sXCorner = (INT16)(MAP_VIEW_START_X + (cnt * MAP_GRID_X)); + INT16 sYCorner = (INT16)(MAP_VIEW_START_Y + (cnt2 * MAP_GRID_Y)); + + INT16 usXPos, usYPos; + FindFontCenterCoordinates( sXCorner, sYCorner, MAP_GRID_X, MAP_GRID_Y, sString, FONT14ARIAL, &usXPos, &usYPos ); + + gprintfdirty( usXPos, usYPos, sString ); + mprintf( usXPos, usYPos, sString ); + } break; case MAP_DISPLAY_MOBILEMILITIARESTRICTIONS: @@ -905,7 +966,7 @@ UINT32 DrawMap( void ) DrawTownMilitiaForcesOnMap( ); } - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE && !gfInChangeArrivalSectorMode ) + if ( ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) && !gfInChangeArrivalSectorMode ) { DrawBullseye(); } @@ -1172,7 +1233,7 @@ INT32 ShowAssignedTeam(INT16 sMapX, INT16 sMapY, INT32 iCount) ( !PlayerIDGroupInMotion( pSoldier->ubGroupID ) ) ) { // skip mercs inside the helicopter if we're showing airspace level - they show up inside chopper icon instead - if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE || (pSoldier->bAssignment != VEHICLE) || (pSoldier->iVehicleId != iHelicopterVehicleId) ) + if ( (gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE && gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) || (pSoldier->bAssignment != VEHICLE) || (pSoldier->iVehicleId != iHelicopterVehicleId) ) { ++sNumberOfAssigned; } @@ -1577,7 +1638,29 @@ BOOLEAN InitializePalettesForMap( void ) pMapPalette[MAP_SHADE_LT_PINK] = Create16BPPPaletteShaded( pPalette, 200, 40, 175, TRUE ); pMapPalette[MAP_SHADE_ORANGE] = Create16BPPPaletteShaded( pPalette, 255, 127, 40, TRUE ); + + pMapPalette[MAP_SHADE_MIX_RED] = Create16BPPPaletteShaded( pPalette, 400, 0, 0, TRUE ); + pMapPalette[MAP_SHADE_MIX_GREEN] = Create16BPPPaletteShaded( pPalette, 0, 400, 0, TRUE ); + pMapPalette[MAP_SHADE_MIX_BLUE] = Create16BPPPaletteShaded( pPalette, 0, 0, 400, TRUE ); + pMapPalette[MAP_SHADE_MIX_YELLOW] = Create16BPPPaletteShaded( pPalette, 200, 200, 0, TRUE ); + pMapPalette[MAP_SHADE_MIX_RED_GREEN] = Create16BPPPaletteShaded( pPalette, 200, 200, 0, TRUE ); + pMapPalette[MAP_SHADE_MIX_RED_BLUE] = Create16BPPPaletteShaded( pPalette, 200, 0, 200, TRUE ); + pMapPalette[MAP_SHADE_MIX_RED_YELLOW] = Create16BPPPaletteShaded( pPalette, 300, 100, 0, TRUE ); + + pMapPalette[MAP_SHADE_MIX_GREEN_BLUE] = Create16BPPPaletteShaded( pPalette, 0, 200, 200, TRUE ); + pMapPalette[MAP_SHADE_MIX_GREEN_YELLOW] = Create16BPPPaletteShaded( pPalette, 100, 300, 0, TRUE ); + + pMapPalette[MAP_SHADE_MIX_BLUE_YELLOW] = Create16BPPPaletteShaded( pPalette, 100, 100, 200, TRUE ); + + pMapPalette[MAP_SHADE_MIX_RED_GREEN_BLUE] = Create16BPPPaletteShaded( pPalette, 133, 133, 133, TRUE ); + pMapPalette[MAP_SHADE_MIX_RED_GREEN_YELLOW] = Create16BPPPaletteShaded( pPalette, 200, 200, 0, TRUE ); + pMapPalette[MAP_SHADE_MIX_RED_BLUE_YELLOW] = Create16BPPPaletteShaded( pPalette, 200, 66, 133, TRUE ); + + pMapPalette[MAP_SHADE_MIX_GREEN_BLUE_YELLOW] = Create16BPPPaletteShaded( pPalette, 66, 200, 133, TRUE ); + + pMapPalette[MAP_SHADE_MIX_RED_GREEN_BLUE_YELLOW] = Create16BPPPaletteShaded( pPalette, 150, 150, 100, TRUE ); + // delete image DeleteVideoSurfaceFromIndex(uiTempMap); @@ -2007,7 +2090,7 @@ void PlotPathForHelicopter( INT16 sX, INT16 sY ) // will plot the path for the helicopter // no heli...go back - if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE || !fHelicopterAvailable ) + if ( (gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE && gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) || !fHelicopterAvailable ) { return; } @@ -2943,9 +3026,9 @@ void DisplayThePotentialPathForHelicopter(INT16 sMapX, INT16 sMapY ) static INT16 sOldMapX, sOldMapY; INT32 iDifference = 0; - if ( fOldShowAirCraft != (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE) ) + if ( fOldShowAirCraft != (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) ) { - fOldShowAirCraft = (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE); + fOldShowAirCraft = (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS); giPotHeliPathBaseTime = GetJA2Clock( ); sOldMapX = sMapX; @@ -4396,7 +4479,7 @@ BOOLEAN CheckForClickOverHelicopterIcon( INT16 sClickedSectorX, INT16 sClickedSe fIgnoreClick = TRUE; } - if ( !fHelicopterAvailable || gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE ) + if ( !fHelicopterAvailable || (gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE && gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) ) { return( FALSE ); } @@ -6813,7 +6896,7 @@ void ShowSAMSitesOnStrategicMap( void ) INT8 ubVidObjIndex = 0; CHAR16 wString[ 40 ]; - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { BlitSAMGridMarkers( ); } @@ -6843,7 +6926,7 @@ void ShowSAMSitesOnStrategicMap( void ) GetVideoObject( &hHandle, guiSAMICON); BltVideoObject( guiSAVEBUFFER, hHandle, ubVidObjIndex, sIconX, sIconY, VO_BLT_SRCTRANSPARENCY, NULL ); - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // write "SAM Site" centered underneath @@ -6911,7 +6994,7 @@ void ShowSAMSitesOnStrategicMap( void ) sX += 5; sY += 3; - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // write "Refuel Site" centered underneath diff --git a/Strategic/Map Screen Interface Map.h b/Strategic/Map Screen Interface Map.h index e4a38665..1a888275 100644 --- a/Strategic/Map Screen Interface Map.h +++ b/Strategic/Map Screen Interface Map.h @@ -61,6 +61,29 @@ enum{ MAP_SHADE_ORANGE, + // Flugente: more colours + MAP_SHADE_MIX_RED, + MAP_SHADE_MIX_GREEN, + MAP_SHADE_MIX_BLUE, + MAP_SHADE_MIX_YELLOW, + + MAP_SHADE_MIX_RED_GREEN, + MAP_SHADE_MIX_RED_BLUE, + MAP_SHADE_MIX_RED_YELLOW, + + MAP_SHADE_MIX_GREEN_BLUE, + MAP_SHADE_MIX_GREEN_YELLOW, + + MAP_SHADE_MIX_BLUE_YELLOW, + + MAP_SHADE_MIX_RED_GREEN_BLUE, + MAP_SHADE_MIX_RED_GREEN_YELLOW, + MAP_SHADE_MIX_RED_BLUE_YELLOW, + + MAP_SHADE_MIX_GREEN_BLUE_YELLOW, + + MAP_SHADE_MIX_RED_GREEN_BLUE_YELLOW, + MAP_SHADE_MAX, }; diff --git a/Strategic/Map Screen Interface TownMine Info.cpp b/Strategic/Map Screen Interface TownMine Info.cpp index be06b60e..388216e4 100644 --- a/Strategic/Map Screen Interface TownMine Info.cpp +++ b/Strategic/Map Screen Interface TownMine Info.cpp @@ -949,6 +949,21 @@ void AddCommonInfoToBox(void) } AddSecondColumnMonoString( &hStringHandle, wString ); + + // Flugente: if this is a SAM site we know about, display (hack-)status + for ( UINT16 x = 0; x < MAX_NUMBER_OF_SAMS; ++x ) + { + if ( pSamList[x] == usSectorValue && fSamSiteFound[x] ) + { + swprintf( wString, pwMiscSectorStrings[7] ); + AddMonoString( &hStringHandle, wString ); + + swprintf( wString, L"%d / %d", StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( usSectorValue )].bSAMCondition, StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( usSectorValue )].sSamHackStatus ); + AddSecondColumnMonoString( &hStringHandle, wString ); + + break; + } + } } diff --git a/Strategic/Map Screen Interface.cpp b/Strategic/Map Screen Interface.cpp index fe59f1d0..ceff0c12 100644 --- a/Strategic/Map Screen Interface.cpp +++ b/Strategic/Map Screen Interface.cpp @@ -6659,7 +6659,7 @@ void TurnOnSectorLocator( UINT8 ubProfileID ) { // if helicopter position is being shown, don't do this, too, cause the helicopter icon is on top and it looks // like crap. I tried moving the heli icon blit to before, but that screws up it's blitting. - if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE && gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // can't use his profile, he's where his chopper is Assert( iHelicopterVehicleId != -1 ); diff --git a/Strategic/Strategic AI.cpp b/Strategic/Strategic AI.cpp index ed7a7e51..b787de55 100644 --- a/Strategic/Strategic AI.cpp +++ b/Strategic/Strategic AI.cpp @@ -2251,6 +2251,7 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Strategic5"); if( IsThisSectorASAMSector( pGroup->ubSectorX, pGroup->ubSectorY, 0 ) ) { StrategicMap[ pGroup->ubSectorX + pGroup->ubSectorY * MAP_WORLD_X ].bSAMCondition = 100; + StrategicMap[ pGroup->ubSectorX + pGroup->ubSectorY * MAP_WORLD_X ].sSamHackStatus = 100; StrategicMap[ pGroup->ubSectorX + pGroup->ubSectorY * MAP_WORLD_X ].usFlags &= ~SAMSITE_REPAIR_ORDERED; UpdateSAMDoneRepair( pGroup->ubSectorX, pGroup->ubSectorY, 0 ); diff --git a/Strategic/Strategic Movement.cpp b/Strategic/Strategic Movement.cpp index 559fef0e..0b4ca5b8 100644 --- a/Strategic/Strategic Movement.cpp +++ b/Strategic/Strategic Movement.cpp @@ -3806,7 +3806,7 @@ BOOLEAN PlayersBetweenTheseSectors( INT16 sSource, INT16 sDest, INT32 *iCountEnt // if this group is aboard the helicopter and we're showing the airspace layer, don't count any mercs aboard the // chopper, because the chopper icon itself serves the function of showing the location/size of this group - if ( !fHelicopterGroup || gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE ) + if ( !fHelicopterGroup || !(gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) ) { // if only showing retreat paths, ignore groups not in the battle sector // if NOT showing retreat paths, ignore groups not between sectors diff --git a/Strategic/Strategic Pathing.cpp b/Strategic/Strategic Pathing.cpp index 252285e3..854f78e8 100644 --- a/Strategic/Strategic Pathing.cpp +++ b/Strategic/Strategic Pathing.cpp @@ -335,7 +335,7 @@ INT32 FindStratPath(INT16 sStart, INT16 sDestination, INT16 sMvtGroupNumber, BOO { // is a heli, its pathing is determined not by time (it's always the same) but by total cost // Skyrider will avoid uncontrolled airspace as much as possible... - if ( StrategicMap[curLoc].usAirType == AIRSPACE_ENEMY_ACTIVE ) + if ( StrategicMap[curLoc].usAirType & AIRSPACE_ENEMY_ACTIVE ) { nextCost = COST_AIRSPACE_UNSAFE; } diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index 398f98ce..c549216d 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -5641,14 +5641,14 @@ UINT32 MapScreenHandle(void) // if heli is around, show it - if ( fHelicopterAvailable && (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || fShowTeamFlag) && (iCurrentMapSectorZ == 0) && !fShowMapInventoryPool ) + if ( fHelicopterAvailable && ( (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) || fShowTeamFlag) && (iCurrentMapSectorZ == 0) && !fShowMapInventoryPool ) { // this is done on EVERY frame, I guess it beats setting entire map dirty all the time while he's moving... DisplayPositionOfHelicopter( ); } // Flugente: enemy helicopter - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE && (iCurrentMapSectorZ == 0) && !fShowMapInventoryPool ) + if ( (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) && (iCurrentMapSectorZ == 0) && !fShowMapInventoryPool ) { DisplayPositionOfEnemyHelicopter(); } @@ -6625,7 +6625,7 @@ UINT32 HandleMapUI( ) if( SectorInfo[ ( SECTOR( sMapX, sMapY ) ) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] != GROUNDBARRIER ) { // if it's not enemy air controlled - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sMapX, sMapY )].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( sMapX, sMapY )].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { CHAR16 sMsgString[ 128 ], sMsgSubString[ 64 ]; @@ -6730,7 +6730,7 @@ UINT32 HandleMapUI( ) // if we're in airspace mode - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // if not moving soldiers, and not yet plotting the helicopter if ( (bSelectedDestChar == -1) && !fPlotForHelicopter && !fPlotForMilitia ) @@ -11700,7 +11700,7 @@ void PlotTemporaryPaths( void ) { if ( fPlotForHelicopter ) { - Assert( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ); + Assert( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ); // plot temp path PlotATemporaryPathForHelicopter( sMapX, sMapY); @@ -11979,7 +11979,7 @@ void CheckIfPlottingForCharacterWhileAirCraft( void ) // if the plotting modes are inconsistent, stop plotting BOOLEAN fAbort = FALSE; - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { if ( (bSelectedDestChar != -1 || fPlotForMilitia) && !fPlotForHelicopter ) fAbort = TRUE; @@ -12762,7 +12762,7 @@ void UpdateCursorIfInLastSector( void ) { GetMouseMapXY(&sMapX, &sMapY); - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // check for helicopter if ( fPlotForHelicopter ) @@ -14592,7 +14592,6 @@ void CheckForInventoryModeCancellation() } } - void ChangeSelectedMapSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) { // ignore while map inventory pool is showing, or else items can be replicated, since sector inventory always applies @@ -14609,14 +14608,13 @@ void ChangeSelectedMapSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) // disallow going underground while plotting (surface) movement if ( ( bMapZ != 0 ) && ( ( bSelectedDestChar != -1 ) || fPlotForHelicopter ) ) return; - - + sSelMapX = sMapX; sSelMapY = sMapY; iCurrentMapSectorZ = bMapZ; // if going underground while in airspace mode - if ( (bMapZ > 0) && (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE) ) + if ( (bMapZ > 0) && (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) ) { // turn off airspace mode ToggleAirspaceMode( ); @@ -14629,8 +14627,6 @@ void ChangeSelectedMapSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) fTeamPanelDirty = TRUE; } - - BOOLEAN CanChangeDestinationForCharSlot( INT8 bCharNumber, BOOLEAN fShowErrorMessage ) { SOLDIERTYPE *pSoldier = NULL; @@ -14786,7 +14782,7 @@ void CancelOrShortenPlottedPath( void ) GetMouseMapXY(&sMapX, &sMapY); // check if we are in aircraft mode - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // check for helicopter path being plotted if( !fPlotForHelicopter ) @@ -14830,7 +14826,6 @@ void CancelOrShortenPlottedPath( void ) uiReturnValue = ClearPathAfterThisSectorForCharacter( &Menptr[gCharactersList[bSelectedDestChar].usSolID], sMapX, sMapY ); } - switch ( uiReturnValue ) { case ABORT_PLOTTING: @@ -15312,7 +15307,6 @@ void MakeMapModesSuitableForDestPlotting( INT8 bCharNumber ) { SOLDIERTYPE *pSoldier = NULL; - if( gCharactersList[ bCharNumber ].fValid == TRUE ) { pSoldier = MercPtrs[ gCharactersList[ bCharNumber ].usSolID ]; @@ -15323,7 +15317,7 @@ void MakeMapModesSuitableForDestPlotting( INT8 bCharNumber ) if ( ( pSoldier->bAssignment == VEHICLE ) && ( pSoldier->iVehicleId == iHelicopterVehicleId ) ) { - if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE && gusMapDisplayColourMode != MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // turn on airspace mode automatically ToggleAirspaceMode(); @@ -15331,7 +15325,7 @@ void MakeMapModesSuitableForDestPlotting( INT8 bCharNumber ) } else { - if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE ) + if ( gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS ) { // turn off airspace mode automatically ToggleAirspaceMode(); @@ -15791,7 +15785,7 @@ void StartChangeSectorArrivalMode( void ) BOOLEAN CanMoveBullseyeAndClickedOnIt( INT16 sMapX, INT16 sMapY ) { // if in airspace mode, and not plotting paths - if ( (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE) && (bSelectedDestChar == -1) && (fPlotForHelicopter == FALSE) ) + if ( (gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE || gusMapDisplayColourMode == MAP_DISPLAY_AIRSPACE_COLOURED_SAMS) && (bSelectedDestChar == -1) && (fPlotForHelicopter == FALSE) ) { if (is_networked) { diff --git a/Strategic/strategic.h b/Strategic/strategic.h index d7a591d2..00af9255 100644 --- a/Strategic/strategic.h +++ b/Strategic/strategic.h @@ -5,14 +5,9 @@ #include "mapscreen.h" #include "soldier control.h" -// different states for airspace control -enum -{ - AIRSPACE_PLAYER_ACTIVE, // controlled by player and active - AIRSPACE_ENEMY_ACTIVE, // controlled by enemy and active - AIRSPACE_PLAYER_INACTIVE, // controlled by player but not active - AIRSPACE_ENEMY_INACTIVE, // controlled by enemy but not active -}; +// Flugente: airspace flags +#define AIRSPACE_ENEMY_ACTIVE 0x01 //1 // controlled by enemy and active +#define AIRSPACE_PLAYER_ACTIVE 0x02 //2 // controlled by enemy and active struct strategicmapelement{ UINT8 UNUSEDuiFootEta[4]; // eta/mvt costs for feet @@ -21,11 +16,11 @@ struct strategicmapelement{ UINT8 uiBadVehicleSector[4]; // blocking mvt from vehicles INT8 bNameId; BOOLEAN fEnemyControlled; // enemy controlled or not - UINT8 usAirType; + UINT8 usAirType; // Flugente: This will now be used as a flagmask BOOLEAN UNUSEDfLostControlAtSomeTime; INT8 bSAMCondition; // SAM Condition .. 0 - 100, just like an item's status - - INT8 bFillupforPadding[ 3 ]; // necessary for padding + INT8 sSamHackStatus; // this status influences a SAM's max radius + INT8 bFillupforPadding[ 2 ]; // necessary for padding UINT32 usFlags; // Flugente: various flags INT8 bPadding[ 20 ]; }; diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index 21f84271..c2848271 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -238,7 +238,7 @@ INT8 gbMercIsNewInThisSector[MAX_NUM_SOLDIERS]; BOOLEAN localizedMapTextOnly; -UINT8 ubSAMControlledSectors[MAP_WORLD_Y][MAP_WORLD_X];// = { +UINT32 ubSAMControlledSectors[MAP_WORLD_Y][MAP_WORLD_X];// = { // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // @@ -1973,7 +1973,8 @@ void InitializeSAMSites( void ) for ( UINT32 cnt = 0; cnt < NUMBER_OF_SAMS; ++cnt ) { StrategicMap[gpSamSectorX[cnt] + (MAP_WORLD_X * gpSamSectorY[cnt])].bSAMCondition = 100; - }; + StrategicMap[gpSamSectorX[cnt] + (MAP_WORLD_X * gpSamSectorY[cnt])].sSamHackStatus = 100; + } // Flugente, bizarrely enough, this structure is not NULLED when starting a new campaign. For now, we NULL the flagmask to clear at least that for ( INT32 cnt = 0; cnt < MAP_WORLD_X*MAP_WORLD_Y; ++cnt ) @@ -5147,6 +5148,15 @@ INT8 GetSAMIdFromSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) return(-1); } +void SetSamHackStatus( INT16 sSectorX, INT16 sSectorY, INT8 sStatus ) +{ + if ( IsThisSectorASAMSector( sSectorX, sSectorY, 0 ) ) + { + StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].sSamHackStatus = sStatus; + + UpdateAirspaceControl(); + } +} BOOLEAN CanGoToTacticalInSector( INT16 sX, INT16 sY, UINT8 ubZ ) { @@ -5222,47 +5232,40 @@ INT32 SAMSitesUnderPlayerControl( INT16 sX, INT16 sY ) void UpdateAirspaceControl( void ) { - UINT8 ubControllingSAM; StrategicMapElement *pSAMStrategicMap = NULL; for ( INT32 iCounterA = 1; iCounterA < (INT32)(MAP_WORLD_X - 1); ++iCounterA ) { for ( INT32 iCounterB = 1; iCounterB < (INT32)(MAP_WORLD_Y - 1); ++iCounterB ) { + StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = 0; + // if networked, disable SAM airspace restrictions... if ( is_networked ) { - StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = AIRSPACE_ENEMY_INACTIVE; continue; } - - // IMPORTANT: B and A are reverse here, since the table is stored transposed - ubControllingSAM = ubSAMControlledSectors[iCounterB][iCounterA]; - - if ( ubControllingSAM >= 1 && ubControllingSAM <= NUMBER_OF_SAMS ) + + for ( int samcnt = 0; samcnt < NUMBER_OF_SAMS; ++samcnt ) { - pSAMStrategicMap = &(StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ubControllingSAM - 1] )]); + BOOLEAN samworking = FALSE; + if ( DoesSamCoverSector( samcnt, SECTOR( iCounterA, iCounterB ), &samworking ) ) + { + if ( samworking ) + { + pSAMStrategicMap = &(StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[samcnt] )]); - // different status depending on who controls the SAM sector, and whether the SAM is operational - if ( pSAMStrategicMap->fEnemyControlled ) - { - if ( pSAMStrategicMap->bSAMCondition >= MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) - StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = AIRSPACE_ENEMY_ACTIVE; - else - StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = AIRSPACE_ENEMY_INACTIVE; + // different status depending on who controls the SAM sector, and whether the SAM is operational + if ( pSAMStrategicMap->fEnemyControlled ) + { + StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType |= AIRSPACE_ENEMY_ACTIVE; + } + else + { + StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType |= AIRSPACE_PLAYER_ACTIVE; + } + } } - else - { - if ( pSAMStrategicMap->bSAMCondition >= MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) - StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = AIRSPACE_PLAYER_ACTIVE; - else - StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = AIRSPACE_PLAYER_INACTIVE; - } - } - else - { - // no controlling SAM site - StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].usAirType = AIRSPACE_ENEMY_INACTIVE; } } } @@ -5270,7 +5273,7 @@ void UpdateAirspaceControl( void ) // check if currently selected arrival sector still has secure airspace // if it's not enemy air controlled - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( gsMercArriveSectorX, gsMercArriveSectorY )].usAirType == AIRSPACE_ENEMY_ACTIVE ) + if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( gsMercArriveSectorX, gsMercArriveSectorY )].usAirType & AIRSPACE_ENEMY_ACTIVE ) { // NOPE! CHAR16 sMsgString[256], sMsgSubString1[64], sMsgSubString2[64]; @@ -5280,7 +5283,7 @@ void UpdateAirspaceControl( void ) // move the landing zone over to Omerta // HEADROCK HAM 3.5: Externalized coordinates - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY )].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY )].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { gsMercArriveSectorX = gGameExternalOptions.ubDefaultArrivalSectorX; gsMercArriveSectorY = gGameExternalOptions.ubDefaultArrivalSectorY; @@ -5291,7 +5294,7 @@ void UpdateAirspaceControl( void ) { for ( UINT8 ubSectorY = 1; ubSectorY <= 16; ++ubSectorY ) { - if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( ubSectorX, ubSectorY )].usAirType != AIRSPACE_ENEMY_ACTIVE && !sBadSectorsList[ubSectorX][ubSectorY] ) + if ( !(StrategicMap[CALCULATE_STRATEGIC_INDEX( ubSectorX, ubSectorY )].usAirType & AIRSPACE_ENEMY_ACTIVE) && !sBadSectorsList[ubSectorX][ubSectorY] ) { gsMercArriveSectorX = ubSectorX; gsMercArriveSectorY = ubSectorY; @@ -5324,6 +5327,72 @@ void UpdateAirspaceControl( void ) UpdateRefuelSiteAvailability( ); } +// get the maximum distance from a SAM to its covered sectors (in principle, not taking into account status) +FLOAT GetSAMMaxDistanceToCoveredSector( UINT8 usSam ) +{ + FLOAT maxdist = 0.0f; + + if ( usSam < NUMBER_OF_SAMS ) + { + UINT8 samsector = pSamList[usSam]; + UINT8 sam_x = SECTORX( samsector ); + UINT8 sam_y = SECTORY( samsector ); + + for ( int x = 1; x < MAP_WORLD_X - 1; ++x ) + { + for ( int y = 1; y < MAP_WORLD_Y - 1; ++y ) + { + if ( ubSAMControlledSectors[y][x] & (1 << usSam) ) + { + FLOAT dist = std::sqrt( (sam_x - x)*(sam_x - x) + (sam_y - y)*(sam_y - y) ); + + if ( maxdist < dist ) + maxdist = dist; + } + } + } + } + + return maxdist; +} + + +BOOLEAN DoesSamCoverSector( UINT8 usSam, UINT8 usSector, BOOLEAN* apSamIsWorking ) +{ + if ( apSamIsWorking ) + *apSamIsWorking = FALSE; + + if ( usSam < NUMBER_OF_SAMS ) + { + // can this sam control this sector in principle? + if ( ubSAMControlledSectors[SECTORY( usSector )][SECTORX( usSector )] & (1 << usSam) ) + { + UINT8 samsector = pSamList[usSam]; + + if ( StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( samsector )].bSAMCondition >= MIN_CONDITION_FOR_SAM_SITE_TO_WORK && StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( samsector )].sSamHackStatus > 0 ) + { + if ( apSamIsWorking ) + *apSamIsWorking = TRUE; + } + + FLOAT dist = std::sqrt( (SECTORX( samsector ) - SECTORX( usSector ))*(SECTORX( samsector ) - SECTORX( usSector )) + (SECTORY( samsector ) - SECTORY( usSector ))*(SECTORY( samsector ) - SECTORY( usSector )) ); + + // determine max distance SAM has to cover + FLOAT sammaxdistance = GetSAMMaxDistanceToCoveredSector( usSam ); + + // allowed range ratio according to status and hacking + FLOAT samrangeratio = StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( samsector )].bSAMCondition * StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( samsector )].sSamHackStatus / 10000.0f; + + // can this SAM currently cover this sector? + if ( dist <= sammaxdistance * samrangeratio ) + { + return TRUE; + } + } + } + + return FALSE; +} BOOLEAN IsThereAFunctionalSAMSiteInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { @@ -5333,24 +5402,23 @@ BOOLEAN IsThereAFunctionalSAMSiteInSector( INT16 sSectorX, INT16 sSectorY, INT8 } if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].bSAMCondition < MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) - { return(FALSE); - } + + if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY )].sSamHackStatus <= 0 ) + return(FALSE); return(TRUE); } BOOLEAN IsThisSectorASAMSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { - INT32 cnt; - // is the sector above ground? if ( bSectorZ != 0 ) { return(FALSE); } - for ( cnt = 0; cnt < NUMBER_OF_SAMS; cnt++ ) + for ( INT32 cnt = 0; cnt < NUMBER_OF_SAMS; ++cnt ) { if ( (sSectorX == gpSamSectorX[cnt]) && (sSectorY == gpSamSectorY[cnt]) ) return(TRUE); @@ -5446,6 +5514,15 @@ BOOLEAN LoadStrategicInfoFromSavedFile( HWFILE hFile ) if ( guiCurrentSaveGameVersion < MILITIA_MOVEMENT ) StrategicMap[i].usFlags = 0; } + + if ( guiCurrentSaveGameVersion < HACKABLE_SAMS ) + { + // all SAM sites start game in perfect working condition + for ( UINT32 cnt = 0; cnt < NUMBER_OF_SAMS; ++cnt ) + { + StrategicMap[gpSamSectorX[cnt] + (MAP_WORLD_X * gpSamSectorY[cnt])].sSamHackStatus = 100; + } + } // Load the Sector Info uiSize = sizeof(SECTORINFO); diff --git a/Strategic/strategicmap.h b/Strategic/strategicmap.h index 7f78ad7f..fd0d704a 100644 --- a/Strategic/strategicmap.h +++ b/Strategic/strategicmap.h @@ -119,8 +119,12 @@ void JumpIntoAdjacentSector( UINT8 ubDirection, UINT8 ubJumpCode, INT32 sAdditio BOOLEAN CanGoToTacticalInSector( INT16 sX, INT16 sY, UINT8 ubZ ); +extern UINT32 ubSAMControlledSectors[MAP_WORLD_Y][MAP_WORLD_X]; + void UpdateAirspaceControl( void ); +BOOLEAN DoesSamCoverSector( UINT8 usSam, UINT8 usSector, BOOLEAN* apSamIsWorking = NULL ); + BOOLEAN IsThisSectorASAMSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ); // init sam sites @@ -186,6 +190,7 @@ BOOLEAN IsSectorRoad( INT16 sSectorX, INT16 sSectorY ); // added by Flugente INT32 SAMSitesUnderPlayerControl( INT16 sX, INT16 sY ); INT8 GetSAMIdFromSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ); +void SetSamHackStatus( INT16 sSectorX, INT16 sSectorY, INT8 sStatus ); void SetupProfileInsertionDataForSoldier( SOLDIERTYPE *pSoldier ); diff --git a/Tactical/Merc Hiring.cpp b/Tactical/Merc Hiring.cpp index 5a403df9..54401181 100644 --- a/Tactical/Merc Hiring.cpp +++ b/Tactical/Merc Hiring.cpp @@ -872,7 +872,7 @@ void CheckForValidArrivalSector( ) if( sSectorGridNo2 >=1 && sSectorGridNo2 < ( ( MAP_WORLD_X - 1 ) * ( MAP_WORLD_X - 1 ) ) && sSectorGridNo2 >= leftmost && sSectorGridNo2 < ( leftmost + MAP_WORLD_X ) ) { - if ( !StrategicMap[ sSectorGridNo2 ].fEnemyControlled && !StrategicMap[ sSectorGridNo2 ].usAirType != AIRSPACE_ENEMY_ACTIVE ) + if ( !StrategicMap[ sSectorGridNo2 ].fEnemyControlled && (StrategicMap[ sSectorGridNo2 ].usAirType & AIRSPACE_ENEMY_ACTIVE) ) { uiRange = StrategicPythSpacesAway( sSectorGridNo2, sSectorGridNo ); diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 4321e7e3..3c892390 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -4227,9 +4227,11 @@ STR16 pwMiscSectorStrings[] = L"分区", L"物品数量", L"未知", + L"已占领", L"是", L"否", + L"Status/Software status:", // TODO.Translate }; // error strings for inventory diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index e0a5b7e8..8cd95c9e 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -4229,9 +4229,11 @@ STR16 pwMiscSectorStrings[] = L"Sector", L"# Items", L"Onbekend", + L"Gecontrolleerd", L"Ja", L"Nee", + L"Status/Software status:", // TODO.Translate }; // error strings for inventory diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 63325846..56743c88 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -4227,9 +4227,11 @@ STR16 pwMiscSectorStrings[] = L"Sector", L"# of Items", L"Unknown", + L"Controlled", L"Yes", L"No", + L"Status/Software status:", }; // error strings for inventory diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 02b171ee..7c6943fa 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -4233,9 +4233,11 @@ STR16 pwMiscSectorStrings[] = L"Secteur ", L"Nombre d'objets ", L"Inconnu", + L"Contrôlé ", L"Oui", L"Non", + L"Status/Software status:", // TODO.Translate }; // error strings for inventory diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 5463fba4..7d71d865 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -4225,9 +4225,11 @@ STR16 pwMiscSectorStrings[] = L"Sektor", L"# der Gegenstände", L"Unbekannt", + L"Kontrolliert", L"Ja", L"Nein", + L"Status/Software Status:", }; // error strings for inventory diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 8d12424f..cd89f7df 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -4223,9 +4223,11 @@ STR16 pwMiscSectorStrings[] = L"Settore", L"# di oggetti", L"Sconosciuto", + L"Controllato", L"Sì", L"No", + L"Status/Software status:", // TODO.Translate }; // error strings for inventory diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 1770a93d..df1c9ec5 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -4234,9 +4234,11 @@ STR16 pwMiscSectorStrings[] = L"Sektor", L"Przedmiotów", L"Nieznane", + L"Pod kontrolą", L"Tak", L"Nie", + L"Status/Software status:", // TODO.Translate }; // error strings for inventory diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index fca9cf7c..d9606fc8 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -4227,9 +4227,11 @@ STR16 pwMiscSectorStrings[] = L"Сектор", L"Количество предметов", L"Неизвестно", + L"Под контролем", L"Да", L"Нет", + L"Status/Software status:", // TODO.Translate }; // error strings for inventory