From 7efe3966cf1554f9dd2016080f8c6d473a35602a Mon Sep 17 00:00:00 2001 From: Flugente Date: Sun, 3 May 2015 20:02:01 +0000 Subject: [PATCH] New Feature: Improved ambushes have mercs face the enemy and be somewhat scattered, while the enemy encircles the player but is not at point-blank range. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7846 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 8 +++- GameSettings.h | 8 ++++ Strategic/strategicmap.cpp | 22 +++++++-- Tactical/Soldier Add.cpp | 86 +++++++++++++++++++++++++--------- Tactical/Soldier Add.h | 5 ++ Tactical/Soldier Init List.cpp | 28 +++++++++++ 6 files changed, 131 insertions(+), 26 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 8692b4e1..c6d64021 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1149,7 +1149,13 @@ void LoadGameExternalOptions() // SANDRO - changed this so on any difficulty there is a chance to beambushed. Hoever this chance is calculated differently to not lead to instant load game like before gGameExternalOptions.fEnableChanceOfEnemyAmbushes = iniReader.ReadBoolean("Tactical Difficulty Settings", "ENABLE_CHANCE_OF_ENEMY_AMBUSHES", TRUE); gGameExternalOptions.bChanceModifierEnemyAmbushes = iniReader.ReadInteger("Tactical Difficulty Settings","ENEMY_AMBUSHES_CHANCE_MODIFIER ", 0, -100, 100); - + + gGameExternalOptions.fAmbushSpreadMercs = iniReader.ReadBoolean( "Tactical Difficulty Settings", "AMBUSH_MERCS_SPREAD", TRUE ); + gGameExternalOptions.usAmbushSpreadRadiusMercs = iniReader.ReadInteger( "Tactical Difficulty Settings", "AMBUSH_MERCS_SPREAD_RADIUS", 10, 1, 20 ); + gGameExternalOptions.uAmbushEnemyEncircle = iniReader.ReadInteger( "Tactical Difficulty Settings", "AMBUSH_ENEMY_ENCIRCLEMENT", 2, 0, 2 ); + gGameExternalOptions.usAmbushEnemyEncircleRadius1 = iniReader.ReadInteger( "Tactical Difficulty Settings", "AMBUSH_ENEMY_ENCIRCLEMENT_RADIUS1", 15, gGameExternalOptions.usAmbushSpreadRadiusMercs, 50 ); + gGameExternalOptions.usAmbushEnemyEncircleRadius2 = iniReader.ReadInteger( "Tactical Difficulty Settings", "AMBUSH_ENEMY_ENCIRCLEMENT_RADIUS2", 30, gGameExternalOptions.usAmbushEnemyEncircleRadius1, 100 ); + // SANDRO - Special NPCs strength increased by percent gGameExternalOptions.usSpecialNPCStronger = iniReader.ReadInteger("Tactical Difficulty Settings", "SPECIAL_NPCS_STRONGER",0, 0, 200); diff --git a/GameSettings.h b/GameSettings.h index 5044d189..342dca6c 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -349,8 +349,16 @@ typedef struct // changed/added these - SANDRO UINT8 sMercsAutoresolveOffenseBonus; UINT8 sMercsAutoresolveDeffenseBonus; + + // Flugente: more ambush options BOOLEAN fEnableChanceOfEnemyAmbushes; INT8 bChanceModifierEnemyAmbushes; + BOOLEAN fAmbushSpreadMercs; + UINT16 usAmbushSpreadRadiusMercs; + UINT8 uAmbushEnemyEncircle; + UINT16 usAmbushEnemyEncircleRadius1; + UINT16 usAmbushEnemyEncircleRadius2; + UINT8 usSpecialNPCStronger; // Flugente: should kingpin's hitmen be disguised? This will make them have random clothes among other stuff diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index 13aa5dca..d9b0e0e1 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -2625,7 +2625,7 @@ void PrepareLoadedSector() if( gubEnemyEncounterCode == ENEMY_AMBUSH_CODE || gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE ) { - if( gMapInformation.sCenterGridNo != -1 ) + if( gMapInformation.sCenterGridNo != NOWHERE ) { CallAvailableEnemiesTo( gMapInformation.sCenterGridNo ); } @@ -3118,11 +3118,25 @@ void UpdateMercsInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { - if( gMapInformation.sCenterGridNo != -1 && gfBlitBattleSectorLocator && + if( gMapInformation.sCenterGridNo != NOWHERE && gfBlitBattleSectorLocator && (gubEnemyEncounterCode == ENEMY_AMBUSH_CODE || gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE) && pSoldier->bTeam != CIV_TEAM ) { - pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; - pSoldier->usStrategicInsertionData = gMapInformation.sCenterGridNo; + // Flugente: improved ambush + if ( gGameExternalOptions.fAmbushSpreadMercs ) + { + UINT8 ubDirection; + + pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; + pSoldier->usStrategicInsertionData = FindRandomGridNoFromSweetSpotExcludingSweetSpot( pSoldier, gMapInformation.sCenterGridNo, gGameExternalOptions.usAmbushSpreadRadiusMercs, &ubDirection ); + + // have the merc look outward. We add + 100 because later on we use this to signify that we want really enforce this direction + pSoldier->ubInsertionDirection = (UINT8)GetDirectionToGridNoFromGridNo( gMapInformation.sCenterGridNo, pSoldier->usStrategicInsertionData ) + 100; + } + else + { + pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; + pSoldier->usStrategicInsertionData = gMapInformation.sCenterGridNo; + } } else if( gfOverrideInsertionWithExitGrid ) { diff --git a/Tactical/Soldier Add.cpp b/Tactical/Soldier Add.cpp index 067af318..65687dbe 100644 --- a/Tactical/Soldier Add.cpp +++ b/Tactical/Soldier Add.cpp @@ -1044,30 +1044,26 @@ INT32 FindRandomGridNoFromSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, } +// Flugente: I've altered this function in two ways: +// 1. The gridno is now drawn from the entirety of the circle - not just for sX and sY being positive +// 2. The direction now points to sSweetGridNo, center of the circle, instead of the map center INT32 FindRandomGridNoFromSweetSpotExcludingSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ) { - INT16 sX, sY; + INT16 sX, sY; INT32 sGridNo = NOWHERE; - INT32 leftmost; BOOLEAN fFound = FALSE; - UINT32 cnt = 0; + UINT32 cnt = 0; do { - sX = (UINT16)Random( ubRadius ); - sY = (UINT16)Random( ubRadius ); - - leftmost = ( ( sSweetGridNo + ( WORLD_COLS * sY ) )/ WORLD_COLS ) * WORLD_COLS; - - sGridNo = sSweetGridNo + ( WORLD_COLS * sY ) + sX; - - if ( sGridNo == sSweetGridNo ) - { - continue; - } - - if ( sGridNo >=0 && sGridNo < WORLD_MAX && - sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) ) + sX = (UINT16)Random( 2 * ubRadius ) - ubRadius; + sY = (UINT16)Random( 2 * ubRadius ) - ubRadius; + + sGridNo = sSweetGridNo + (WORLD_COLS * sY ) + sX; + + if ( sGridNo == sSweetGridNo || TileIsOutOfBounds( sGridNo ) || PythSpacesAway( sGridNo, sSweetGridNo ) >= ubRadius || !IsLocationSittable( sGridNo, 0 ) ) + sGridNo = NOWHERE; + else { // Go on sweet stop if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) ) @@ -1076,20 +1072,68 @@ INT32 FindRandomGridNoFromSweetSpotExcludingSweetSpot( SOLDIERTYPE *pSoldier, IN } } - cnt++; + ++cnt; if ( cnt > 2000 ) { return( NOWHERE ); } - } while( !fFound ); // Set direction to center of map! - *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) ); + *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, sSweetGridNo ); return( sGridNo ); +} +// Flugente: returns random gridno in a circle around sCenterGridNo with radius uOuterRadius that is not inside the circle with radius uInnerRadius +INT32 FindRandomGridNoBetweenCircles( INT32 sCenterGridNo, UINT8 uInnerRadius, UINT8 uOuterRadius, UINT8& urDirection ) +{ + INT16 sX, sY; + INT32 sGridNo = NOWHERE; + BOOLEAN fFound = FALSE; + UINT32 cnt = 0; + + if ( uInnerRadius >= uOuterRadius ) + return NOWHERE; + + do + { + /*sX = (UINT16)Random( 2 * (uOuterRadius - uInnerRadius) ) - (uOuterRadius - uInnerRadius); + sY = (UINT16)Random( 2 * (uOuterRadius - uInnerRadius) ) - (uOuterRadius - uInnerRadius); + + if ( sX > 0 ) + sX += uInnerRadius; + else + sX -= uInnerRadius; + + if ( sY > 0 ) + sY += uInnerRadius; + else + sY -= uInnerRadius;*/ + + sX = (UINT16)Random( 2 * uOuterRadius ) - uOuterRadius; + sY = (UINT16)Random( 2 * uOuterRadius ) - uOuterRadius; + + sGridNo = sCenterGridNo + (WORLD_COLS * sY) + sX; + + if ( TileIsOutOfBounds( sGridNo ) || !IsLocationSittable( sGridNo, 0 ) || PythSpacesAway( sGridNo, sCenterGridNo ) <= uInnerRadius || PythSpacesAway( sGridNo, sCenterGridNo ) > uOuterRadius ) + sGridNo = NOWHERE; + else + fFound = TRUE; + + ++cnt; + + if ( cnt > 2000 ) + { + return(NOWHERE); + } + } while ( !fFound ); + + // Set direction to center of map! + urDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, sCenterGridNo ); + + return(sGridNo); } @@ -1238,7 +1282,7 @@ BOOLEAN InternalAddSoldierToSector( UINT8 ubID, BOOLEAN fCalculateDirection, BOO pSoldier->usSoldierFlagMask |= SOLDIER_ASSAULT_BONUS; // Override calculated direction if we were told to.... - if ( pSoldier->ubInsertionDirection > 100 ) + if ( pSoldier->ubInsertionDirection >= 100 ) { pSoldier->ubInsertionDirection = pSoldier->ubInsertionDirection - 100; fCalculateDirection = FALSE; diff --git a/Tactical/Soldier Add.h b/Tactical/Soldier Add.h index 51bbfd39..589ad05e 100644 --- a/Tactical/Soldier Add.h +++ b/Tactical/Soldier Add.h @@ -20,8 +20,13 @@ INT32 FindGridNoFromSweetSpotExcludingSweetSpotInQuardent( SOLDIERTYPE *pSoldier INT32 FindRandomGridNoFromSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ); // Finds a sweetspot but excluding this one! +// Flugente: I've altered this function in two ways: +// 1. The gridno is now drawn from the entirety of the circle - not just for sX and sY being positive +// 2. The direction now points to sSweetGridNo, center of the circle, instead of the map center INT32 FindRandomGridNoFromSweetSpotExcludingSweetSpot( SOLDIERTYPE *pSoldier, INT32 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection ); +// Flugente: returns random gridno in a circle around sCenterGridNo with radius uOuterRadius that is not inside the circle with radius uInnerRadius +INT32 FindRandomGridNoBetweenCircles( INT32 sCenterGridNo, UINT8 uInnerRadius, UINT8 uOuterRadius, UINT8& urDirection ); // Adds a soldier ( already created in mercptrs[] array )! // Finds a good placement based on data in the loaded sector and if they are enemy's or not, etc... diff --git a/Tactical/Soldier Init List.cpp b/Tactical/Soldier Init List.cpp index 58a6161a..93c9a125 100644 --- a/Tactical/Soldier Init List.cpp +++ b/Tactical/Soldier Init List.cpp @@ -56,6 +56,7 @@ #include "Campaign.h" // added by Flugente for HighestPlayerProgressPercentage() #include "CampaignStats.h" // added by Flugente #include "Town Militia.h" // added by Flugente +#include "PreBattle Interface.h" // added by Flugente BOOLEAN gfOriginalList = TRUE; @@ -769,6 +770,33 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddPlacementToWorld: set npcs to enemies")); OkayToUpgradeEliteToSpecialProfiledEnemy( &tempDetailedPlacement ); } + + // Flugente: if this is an enemy, and we are using ambush code, place us somewhat away from the map center, where the player will be + if ( (tempDetailedPlacement.bTeam == ENEMY_TEAM && gubEnemyEncounterCode == ENEMY_AMBUSH_CODE) || + (tempDetailedPlacement.bTeam == CREATURE_TEAM && gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE) ) + { + if ( ( gGameExternalOptions.uAmbushEnemyEncircle == 1 && PythSpacesAway( tempDetailedPlacement.sInsertionGridNo, gMapInformation.sCenterGridNo ) <= gGameExternalOptions.usAmbushEnemyEncircleRadius1 ) || + ( gGameExternalOptions.uAmbushEnemyEncircle == 2) ) + { + // we simply look for a entry point inside a bigger circle, but not inside the merc deployment zone. + INT32 bettergridno = NOWHERE; + UINT16 counter = 0; + UINT8 ubDirection = DIRECTION_IRRELEVANT; + + while ( counter < 100 && (bettergridno == NOWHERE || PythSpacesAway( bettergridno, gMapInformation.sCenterGridNo ) <= gGameExternalOptions.usAmbushEnemyEncircleRadius1) ) + { + bettergridno = FindRandomGridNoBetweenCircles( gMapInformation.sCenterGridNo, gGameExternalOptions.usAmbushEnemyEncircleRadius1, gGameExternalOptions.usAmbushEnemyEncircleRadius2, ubDirection ); + } + + if ( bettergridno != NOWHERE ) + { + tempDetailedPlacement.sInsertionGridNo = bettergridno; + + // have the soldier look inward. We add + 100 because later on we use this to signify that we want really enforce this direction + tempDetailedPlacement.ubDirection = (UINT8)GetDirectionToGridNoFromGridNo( tempDetailedPlacement.sInsertionGridNo, gMapInformation.sCenterGridNo ) + 100; + } + } + } } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddPlacementToWorld: create soldier"));