From 80674249cc4600c8ca704742db866028152a4f1b Mon Sep 17 00:00:00 2001 From: Flugente Date: Sun, 15 Apr 2018 14:42:01 +0000 Subject: [PATCH] New feature: bloodcat/zombie/bandit raids on player-controlled sectors For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&th=23711&goto=353154&#msg_353154 Requires GameDir >= r2420 git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8554 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 19 +- GameSettings.h | 15 + GameVersion.cpp | 18 +- GameVersion.h | 3 +- Laptop/CampaignStats.cpp | 2 + Laptop/LaptopSave.h | 7 +- SaveLoadGame.cpp | 6 + Strategic/Auto Resolve.cpp | 387 ++++++++++++------ Strategic/Auto Resolve.h | 5 + Strategic/Creature Spreading.cpp | 547 ++++++++++++++++++++++---- Strategic/Creature Spreading.h | 11 + Strategic/Game Event Hook.cpp | 16 + Strategic/Game Event Hook.h | 5 + Strategic/LuaInitNPCs.cpp | 22 +- Strategic/PreBattle Interface.cpp | 78 +++- Strategic/PreBattle Interface.h | 4 + Strategic/Queen Command.cpp | 55 +-- Strategic/Strategic Event Handler.cpp | 85 ++++ Strategic/Strategic Event Handler.h | 1 + Strategic/Strategic Status.cpp | 7 +- Strategic/Strategic Town Loyalty.cpp | 18 +- Strategic/Strategic Town Loyalty.h | 2 +- Strategic/Town Militia.cpp | 15 + Strategic/Town Militia.h | 4 + Strategic/XML_SectorNames.cpp | 31 +- Strategic/mapscreen.cpp | 6 +- Strategic/strategicmap.cpp | 18 +- Tactical/Civ Quotes.cpp | 6 + Tactical/Inventory Choosing.cpp | 9 +- Tactical/Items.h | 6 + Tactical/Overhead.cpp | 62 ++- Tactical/Rotting Corpses.cpp | 31 +- Tactical/Soldier Ani.cpp | 13 + Tactical/Soldier Control.cpp | 8 +- Tactical/Soldier Control.h | 1 + Tactical/Soldier Create.cpp | 249 +++++++----- Tactical/Soldier Create.h | 3 + Tactical/Soldier Init List.cpp | 132 ++++++- Tactical/Soldier Init List.h | 1 + Tactical/Weapons.cpp | 73 ++-- Tactical/opplist.cpp | 29 +- TacticalAI/AIUtils.cpp | 14 +- TacticalAI/DecideAction.cpp | 2 +- Utils/Text.h | 9 + Utils/_ChineseText.cpp | 9 + Utils/_DutchText.cpp | 9 + Utils/_EnglishText.cpp | 9 + Utils/_FrenchText.cpp | 9 + Utils/_GermanText.cpp | 9 + Utils/_ItalianText.cpp | 9 + Utils/_PolishText.cpp | 9 + Utils/_RussianText.cpp | 9 + 52 files changed, 1628 insertions(+), 479 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 24550d65..592190c8 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2087,7 +2087,24 @@ void LoadGameExternalOptions() gGameExternalOptions.gEnemyHeliSAMDamage_Var = iniReader.ReadInteger( "Enemy Helicopter Settings", "ENEMYHELI_SAM_DAMAGE_VAR", 30, 10, 100 ); gGameExternalOptions.gEnemyHeliMANPADSDamage_Base = iniReader.ReadInteger( "Enemy Helicopter Settings", "ENEMYHELI_MANPADS_DAMAGE_BASE", 40, 10, 100 ); gGameExternalOptions.gEnemyHeliMANPADSDamage_Var = iniReader.ReadInteger( "Enemy Helicopter Settings", "ENEMYHELI_MANPADS_DAMAGE_VAR", 80, 10, 100 ); - + + // Flugente: raids + //################# Raid Settings ################## + + gGameExternalOptions.gRaid_Bloodcats = iniReader.ReadBoolean( "Raid Settings", "RAID_BLOODCATS", FALSE ); + gGameExternalOptions.gRaid_Zombies = iniReader.ReadBoolean( "Raid Settings", "RAID_ZOMBIES", FALSE ); + gGameExternalOptions.gRaid_Bandits = iniReader.ReadBoolean( "Raid Settings", "RAID_BANDITS", FALSE ); + + gGameExternalOptions.gRaidReplenish_BaseValue = iniReader.ReadInteger( "Raid Settings", "RAID_REPLENISH_BASEVALUE", 3, 1, 100 ); + + gGameExternalOptions.gRaidMaxSize_Bloodcats = iniReader.ReadInteger( "Raid Settings", "RAID_MAXSIZE_BLOODCATS", 8, 1, 254 ); + gGameExternalOptions.gRaidMaxSize_Zombies = iniReader.ReadInteger( "Raid Settings", "RAID_MAXSIZE_ZOMBIES", 15, 1, 254 ); + gGameExternalOptions.gRaidMaxSize_Bandits = iniReader.ReadInteger( "Raid Settings", "RAID_MAXSIZE_BANDITS", 6, 1, 254 ); + + gGameExternalOptions.gRaid_MaxAttackPerNight_Bloodcats = iniReader.ReadInteger( "Raid Settings", "RAID_MAXATTACKSPERNIGHT_BLOODCATS", 3, 0, 100 ); + gGameExternalOptions.gRaid_MaxAttackPerNight_Zombies = iniReader.ReadInteger( "Raid Settings", "RAID_MAXATTACKSPERNIGHT_ZOMBIES", 3, 0, 100 ); + gGameExternalOptions.gRaid_MaxAttackPerNight_Bandits = iniReader.ReadInteger( "Raid Settings", "RAID_MAXATTACKSPERNIGHT_BANDITS", 3, 0, 100 ); + //################# Militia Training Settings ################## gGameExternalOptions.iMaxMilitiaPerSector = iniReader.ReadInteger("Militia Training Settings","MAX_MILITIA_PER_SECTOR",20, 1, CODE_MAXIMUM_NUMBER_OF_REBELS); diff --git a/GameSettings.h b/GameSettings.h index bdeaef79..a26b1278 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -689,6 +689,21 @@ typedef struct UINT16 gEnemyHeliSAMDamage_Var; UINT16 gEnemyHeliMANPADSDamage_Base; UINT16 gEnemyHeliMANPADSDamage_Var; + + // Flugente: raids + BOOLEAN gRaid_Bloodcats; + BOOLEAN gRaid_Zombies; + BOOLEAN gRaid_Bandits; + + UINT8 gRaidReplenish_BaseValue; + + UINT16 gRaidMaxSize_Bloodcats; + UINT16 gRaidMaxSize_Zombies; + UINT16 gRaidMaxSize_Bandits; + + UINT8 gRaid_MaxAttackPerNight_Bloodcats; + UINT8 gRaid_MaxAttackPerNight_Zombies; + UINT8 gRaid_MaxAttackPerNight_Bandits; INT32 ubEnemiesItemDrop; diff --git a/GameVersion.cpp b/GameVersion.cpp index e6db5f8b..fc5a3c97 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8554 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8554 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8554 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8554 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8554 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8554 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8554 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8548 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8554 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 18.03.14" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 18.04.15" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/GameVersion.h b/GameVersion.h index 7da1a337..67c728ca 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 RAID_EVENTS 178 // Flugente: added bloodcat/zombie/bandit raids #define INDIVIDUAL_MILITIA_EXP_FLOAT 177 // Flugente: changed the way we store individual militia experience #define CHAT_TO_DISTRACT 176 // Flugente: internal variable allows us to distract enemies by engrossing them in conversation #define INDIVIDUAL_MILITIA_RANKFIX 175 // Flugente: internal individual militia change requires variable change @@ -96,7 +97,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 INDIVIDUAL_MILITIA_EXP_FLOAT +#define SAVE_GAME_VERSION RAID_EVENTS //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/Laptop/CampaignStats.cpp b/Laptop/CampaignStats.cpp index 70965d11..c545777d 100644 --- a/Laptop/CampaignStats.cpp +++ b/Laptop/CampaignStats.cpp @@ -168,6 +168,8 @@ Incident_Stats::AddStat( SOLDIERTYPE* pSoldier, UINT8 aType ) case CREATURE_TEAM: if ( pSoldier->IsZombie() ) group = CAMPAIGNHISTORY_SD_ZOMBIE; + else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) + group = CAMPAIGNHISTORY_SD_ENEMY_ADMIN; else group = CAMPAIGNHISTORY_SD_CREATURE; break; diff --git a/Laptop/LaptopSave.h b/Laptop/LaptopSave.h index 46333b89..ba269c5d 100644 --- a/Laptop/LaptopSave.h +++ b/Laptop/LaptopSave.h @@ -181,7 +181,12 @@ typedef struct UINT32 usMapIntelFlags; INT16 sIntelInfoForThisHour[INTELINFO_MAXNUMBER]; - UINT8 bPadding[ 48 ]; + // Flugente: raid + INT32 sRaidBloodcats; + INT32 sRaidZombies; + INT32 sRaidBandits; + + UINT8 bPadding[ 36 ]; } LaptopSaveInfoStruct; diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index ed9f1b6e..97764de3 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -6568,6 +6568,12 @@ BOOLEAN LoadSavedGame( int ubSavedGameID ) CalcAproximateAmountPaidToSpeck(); } + // Flugente: if we load an old savegame, this event has to be added + if ( guiCurrentSaveGameVersion < RAID_EVENTS ) + { + AddEveryDayStrategicEvent( EVENT_DAILY_RAID_EVENTS, (7*60), 0 ); + } + gfLoadedGame = TRUE; uiRelEndPerc = 100; diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 90a17d11..4ecd48b1 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -170,6 +170,9 @@ typedef struct AUTORESOLVE_STRUCT UINT8 ubMercs, ubCivs, ubEnemies; UINT8 ubAdmins, ubTroops, ubElites, ubTanks, ubJeeps; UINT8 ubYMCreatures, ubYFCreatures, ubAMCreatures, ubAFCreatures; + UINT8 ubBloodcats; + UINT8 ubZombies; + UINT8 ubBandits; UINT8 ubAliveMercs, ubAliveCivs, ubAliveEnemies; UINT8 ubMercCols, ubMercRows; UINT8 ubEnemyCols, ubEnemyRows; @@ -230,14 +233,18 @@ typedef struct AUTORESOLVE_STRUCT #define CELL_ROBOT 0x00200000 #define CELL_TANK 0x00400000 #define CELL_JEEP 0x00800000 +#define CELL_BLOODCAT 0x01000000 +#define CELL_ZOMBIE 0x02000000 +#define CELL_BANDIT 0x04000000 //Combined flags #define CELL_PLAYER ( CELL_MERC | CELL_MILITIA ) -#define CELL_ENEMY ( CELL_ELITE | CELL_TROOP | CELL_ADMIN | CELL_TANK | CELL_JEEP ) -#define CELL_CREATURE ( CELL_AF_CREATURE | CELL_AM_CREATURE | CELL_YF_CREATURE | CELL_YM_CREATURE ) -#define CELL_FEMALECREATURE ( CELL_AF_CREATURE | CELL_YF_CREATURE ) -#define CELL_MALECREATURE ( CELL_AM_CREATURE | CELL_YM_CREATURE ) -#define CELL_YOUNGCREATURE ( CELL_YF_CREATURE | CELL_YM_CREATURE ) +#define CELL_ENEMY ( CELL_ELITE | CELL_TROOP | CELL_ADMIN | CELL_TANK | CELL_JEEP | CELL_BANDIT ) +#define CELL_FEMALECREATURE ( CELL_AF_CREATURE | CELL_YF_CREATURE ) +#define CELL_MALECREATURE ( CELL_AM_CREATURE | CELL_YM_CREATURE ) +#define CELL_BUG ( CELL_FEMALECREATURE | CELL_MALECREATURE ) +#define CELL_CREATURE ( CELL_BUG | CELL_BLOODCAT | CELL_ZOMBIE ) + #define CELL_INVOLVEDINCOMBAT ( CELL_FIREDATTARGET | CELL_DODGEDATTACK | CELL_HITBYATTACKER ) enum @@ -303,7 +310,6 @@ void CalculateSoldierCells( BOOLEAN fReset ); void CalculateRowsAndColumns(); void CreateAutoResolveInterface(); void RemoveAutoResolveInterface( BOOLEAN fDeleteForGood ); -INT32 CalcIndexFromColRowsXY( INT32 iMaxCols, INT32 iMaxRows, INT32 iCol, INT32 iRow ); //Battle system routines void DetermineTeamLeader( BOOLEAN fFriendlyTeam ); @@ -484,7 +490,7 @@ void EliminateAllEnemies( UINT8 ubSectorX, UINT8 ubSectorY ) if( gpAR ) { - for( i = 0; i < gpAR->ubEnemies; i++ ) + for( i = 0; i < gpAR->ubEnemies; ++i ) { gpEnemies[ i ].pSoldier->stats.bLife = 0; } @@ -626,6 +632,9 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve1"); gpAR->ubPlayerDefenceAdvantage = 0; break; case CREATURE_ATTACK_CODE: + case BLOODCAT_ATTACK_CODE: + case ZOMBIE_ATTACK_CODE: + case BANDIT_ATTACK_CODE: gpAR->ubPlayerDefenceAdvantage = 0; break; default: @@ -746,7 +755,10 @@ void AssociateEnemiesWithStrategicGroups() UINT8 ubDirAmount; UINT8 ubCurrSI; - if( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE ) + if ( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE || + GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) return; pSector = &SectorInfo[ SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ) ]; @@ -1007,8 +1019,7 @@ void CalculateSoldierCells( BOOLEAN fReset ) // WDS TO BE FIXED -- Why is this 40 here? (18 April 2008) //iMaxTeamSize = max( gpAR->ubMercs + gpAR->ubCivs, gpAR->ubEnemies ); iMaxTeamSize = max( min( 40, gpAR->ubMercs + gpAR->ubCivs ), min( 40, gpAR->ubEnemies ) ); - - + if( iMaxTeamSize > 12 ) { gpAR->ubTimeModifierPercentage = (UINT8)(118 - iMaxTeamSize*1.5); @@ -1017,6 +1028,7 @@ void CalculateSoldierCells( BOOLEAN fReset ) { gpAR->ubTimeModifierPercentage = 100; } + gpAR->uiTimeSlice = gpAR->uiTimeSlice * gpAR->ubTimeModifierPercentage / 100; iTop = iScreenHeightOffset + (240 - gpAR->sHeight/2); @@ -1064,6 +1076,7 @@ void CalculateSoldierCells( BOOLEAN fReset ) gpAR->ubAliveMercs--; } } + if( gpAR->ubCivs ) { iStartY = iTop + (gpAR->sHeight - (min(10,gpAR->ubMercRows+gpAR->ubCivRows)*47+7))/2 + gpAR->ubMercRows*47 + 5; @@ -1081,6 +1094,7 @@ void CalculateSoldierCells( BOOLEAN fReset ) gpCivs[ index ].uiFlags |= CELL_MILITIA; } } + if( gpAR->ubEnemies ) { iStartY = iTop + (gpAR->sHeight - (min(10,gpAR->ubEnemyRows)*47+7))/2 + 5; @@ -1095,21 +1109,8 @@ void CalculateSoldierCells( BOOLEAN fReset ) gpEnemies[ index ].xp = (UINT16)(gpAR->sCenterStartX + 141 + 55*x); gpEnemies[ index ].yp = iStartY + y*47; - - if( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE ) - { - if ( index < gpAR->ubElites ) - gpEnemies[ index ].uiFlags = CELL_ELITE; - else if ( index < gpAR->ubElites + gpAR->ubTroops ) - gpEnemies[ index ].uiFlags = CELL_TROOP; - else if ( index < gpAR->ubElites + gpAR->ubTroops + gpAR->ubAdmins ) - gpEnemies[index].uiFlags = CELL_ADMIN; - else if ( index < gpAR->ubElites + gpAR->ubTroops + gpAR->ubAdmins + gpAR->ubTanks ) - gpEnemies[index].uiFlags = CELL_TANK; - else - gpEnemies[index].uiFlags = CELL_JEEP; - } - else + + if ( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE ) { if( index < gpAR->ubAFCreatures ) gpEnemies[ index ].uiFlags = CELL_AF_CREATURE; @@ -1120,6 +1121,30 @@ void CalculateSoldierCells( BOOLEAN fReset ) else gpEnemies[ index ].uiFlags = CELL_YM_CREATURE; } + else if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + if ( index < gpAR->ubBloodcats ) + gpEnemies[index].uiFlags = CELL_BLOODCAT; + else if ( index < gpAR->ubBloodcats + gpAR->ubZombies ) + gpEnemies[index].uiFlags = CELL_ZOMBIE; + else + gpEnemies[index].uiFlags = CELL_BANDIT; + } + else + { + if ( index < gpAR->ubElites ) + gpEnemies[index].uiFlags = CELL_ELITE; + else if ( index < gpAR->ubElites + gpAR->ubTroops ) + gpEnemies[index].uiFlags = CELL_TROOP; + else if ( index < gpAR->ubElites + gpAR->ubTroops + gpAR->ubAdmins ) + gpEnemies[index].uiFlags = CELL_ADMIN; + else if ( index < gpAR->ubElites + gpAR->ubTroops + gpAR->ubAdmins + gpAR->ubTanks ) + gpEnemies[index].uiFlags = CELL_TANK; + else + gpEnemies[index].uiFlags = CELL_JEEP; + } } } } @@ -1182,8 +1207,10 @@ void RenderSoldierCell( SOLDIERCELL *pCell ) if( !pCell->pSoldier->stats.bLife ) { SetObjectHandleShade( pCell->uiVObjectID, 0 ); - - if ( (pCell->uiFlags & CELL_CREATURE) ) + + if ( ( pCell->uiFlags & CELL_BLOODCAT ) ) + BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, BLOODCAT_SKULL, pCell->xp + 3 + x, pCell->yp + 3, VO_BLT_SRCTRANSPARENCY, NULL ); + else if ( (pCell->uiFlags & CELL_BUG ) ) BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, CREATURE_SKULL, pCell->xp + 3 + x, pCell->yp + 3, VO_BLT_SRCTRANSPARENCY, NULL ); else if ( (pCell->uiFlags & CELL_TANK) ) BltVideoObjectFromIndex( FRAME_BUFFER, gpAR->iFaces, TANK_WRECK, pCell->xp + 3 + x, pCell->yp + 3, VO_BLT_SRCTRANSPARENCY, NULL ); @@ -1677,6 +1704,9 @@ void RenderAutoResolve() break; case ENEMY_INVASION_CODE: case CREATURE_ATTACK_CODE: + case BLOODCAT_ATTACK_CODE: + case ZOMBIE_ATTACK_CODE: + case BANDIT_ATTACK_CODE: swprintf( str, gpStrategicString[STR_AR_DEFEND_HEADER] ); break; default: @@ -1802,8 +1832,14 @@ void RenderAutoResolve() break; case BATTLE_DEFEAT: HandleMoraleEvent( NULL, MORALE_HEARD_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 ); - if( ProcessLoyalty() )HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 ); - if( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE ) + + if( ProcessLoyalty() ) + HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_BATTLE_LOST, gpAR->ubSectorX, gpAR->ubSectorY, 0 ); + + if ( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && + GetEnemyEncounterCode () != BLOODCAT_ATTACK_CODE && + GetEnemyEncounterCode () != ZOMBIE_ATTACK_CODE && + GetEnemyEncounterCode () != BANDIT_ATTACK_CODE ) { gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ); } @@ -1828,8 +1864,13 @@ void RenderAutoResolve() //Tack on 5 minutes for retreat. gpAR->uiTotalElapsedBattleTimeInMilliseconds += 300000; - if( ProcessLoyalty() )HandleLoyaltyImplicationsOfMercRetreat( RETREAT_AUTORESOLVE, gpAR->ubSectorX, gpAR->ubSectorY, 0 ); - if( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE ) + if( ProcessLoyalty() ) + HandleLoyaltyImplicationsOfMercRetreat( RETREAT_AUTORESOLVE, gpAR->ubSectorX, gpAR->ubSectorY, 0 ); + + if ( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && + GetEnemyEncounterCode () != BLOODCAT_ATTACK_CODE && + GetEnemyEncounterCode () != ZOMBIE_ATTACK_CODE && + GetEnemyEncounterCode () != BANDIT_ATTACK_CODE ) { gsEnemyGainedControlOfSectorID = (INT16)SECTOR( gpAR->ubSectorX, gpAR->ubSectorY ); } @@ -2133,7 +2174,7 @@ void CreateAutoResolveInterface() // Add the militia in the surrounding sectors // HEADROCK HAM 3.4: Changed argument configuration. GenerateDirectionInfos( gpAR->ubSectorX, gpAR->ubSectorY, &uiDirNumber, pMoveDir, FALSE, TRUE ); - for( i=0; iubElites; ++i, ++index ) + for ( i = 0; i < gpAR->ubAFCreatures; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateCreature( ADULTFEMALEMONSTER ); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + gpEnemies[index].usIndex = AF_CREATURE_FACE; + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); + } + for( i = 0; i < gpAR->ubAMCreatures; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateCreature( AM_MONSTER ); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + gpEnemies[index].usIndex = AM_CREATURE_FACE; + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); + } + for( i = 0; i < gpAR->ubYFCreatures; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateCreature( YAF_MONSTER ); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + gpEnemies[index].usIndex = YF_CREATURE_FACE; + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); + } + for( i = 0; i < gpAR->ubYMCreatures; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateCreature( YAM_MONSTER ); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + gpEnemies[index].usIndex = YM_CREATURE_FACE; + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); + } + } + else if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + for ( i = 0; i < gpAR->ubBloodcats; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateCreature( BLOODCAT ); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + gpEnemies[index].usIndex = BLOODCAT_FACE; + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, pDisplayBodyTypeInfoText[27] ); + } + + for ( i = 0; i < gpAR->ubZombies; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateZombie(); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + gpEnemies[index].usIndex = ZOMBIE_FACE; + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[STR_PB_ZOMBIE] ); + } + + for ( i = 0; i < gpAR->ubBandits; ++i, ++index ) + { + gpEnemies[index].pSoldier = TacticalCreateBandit(); + gpEnemies[index].uiVObjectID = gpAR->iFaces; + + if ( gpEnemies[index].pSoldier->ubBodyType == REGFEMALE ) + { + gpEnemies[index].usIndex = BANDIT1_F_FACE + Random( BLOODCAT_FACE - BANDIT1_F_FACE ); + } + else + { + gpEnemies[index].usIndex = BANDIT1_FACE + Random( BANDIT1_F_FACE - BANDIT1_FACE ); + } + + gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; + gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[STR_PB_BANDIT] ); + } + } + else + { + for ( i = 0; i < gpAR->ubElites; ++i, ++index ) { gpEnemies[index].pSoldier = TacticalCreateEliteEnemy(); gpEnemies[index].uiVObjectID = gpAR->iFaces; - if( gpEnemies[i].pSoldier->ubBodyType == REGFEMALE ) + if ( gpEnemies[index].pSoldier->ubBodyType == REGFEMALE ) { gpEnemies[index].usIndex = ELITEF_FACE; } @@ -2160,39 +2286,39 @@ void CreateAutoResolveInterface() } gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_ELITE_NAME ] ); + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[STR_AR_ELITE_NAME] ); } - for( i = 0; i < gpAR->ubTroops; ++i, ++index ) + for ( i = 0; i < gpAR->ubTroops; ++i, ++index ) { gpEnemies[index].pSoldier = TacticalCreateArmyTroop(); gpEnemies[index].uiVObjectID = gpAR->iFaces; - if ( gpEnemies[i].pSoldier->ubBodyType == REGFEMALE ) + if ( gpEnemies[index].pSoldier->ubBodyType == REGFEMALE ) { gpEnemies[index].usIndex = TROOPF_FACE; } else { - gpEnemies[index].usIndex = TROOP_FACE; + gpEnemies[index].usIndex = TROOP_FACE; } gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_TROOP_NAME ] ); + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[STR_AR_TROOP_NAME] ); } for ( i = 0; i < gpAR->ubAdmins; ++i, ++index ) { gpEnemies[index].pSoldier = TacticalCreateAdministrator(); gpEnemies[index].uiVObjectID = gpAR->iFaces; - if ( gpEnemies[i].pSoldier->ubBodyType == REGFEMALE ) + if ( gpEnemies[index].pSoldier->ubBodyType == REGFEMALE ) { gpEnemies[index].usIndex = ADMINF_FACE; } else { - gpEnemies[index].usIndex = ADMIN_FACE; + gpEnemies[index].usIndex = ADMIN_FACE; } gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_ADMINISTRATOR_NAME ] ); + swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[STR_AR_ADMINISTRATOR_NAME] ); } for ( i = 0; i < gpAR->ubTanks; ++i, ++index ) @@ -2207,7 +2333,7 @@ void CreateAutoResolveInterface() for ( i = 0; i < gpAR->ubJeeps; ++i, ++index ) { - gpEnemies[index].pSoldier = TacticalCreateEnemyJeep( ); + gpEnemies[index].pSoldier = TacticalCreateEnemyJeep(); gpEnemies[index].uiVObjectID = gpAR->iFaces; gpEnemies[index].usIndex = JEEP_FACE; gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; @@ -2217,45 +2343,6 @@ void CreateAutoResolveInterface() AssociateEnemiesWithStrategicGroups(); } - else - { - for ( i = 0, index = 0; i < gpAR->ubAFCreatures; ++i, ++index ) - { - gpEnemies[index].pSoldier = TacticalCreateCreature( ADULTFEMALEMONSTER ); - gpEnemies[index].uiVObjectID = gpAR->iFaces; - gpEnemies[index].usIndex = AF_CREATURE_FACE; - gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; - gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); - } - for( i = 0; i < gpAR->ubAMCreatures; i++, index++ ) - { - gpEnemies[index].pSoldier = TacticalCreateCreature( AM_MONSTER ); - gpEnemies[index].uiVObjectID = gpAR->iFaces; - gpEnemies[index].usIndex = AM_CREATURE_FACE; - gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; - gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); - } - for( i = 0; i < gpAR->ubYFCreatures; i++, index++ ) - { - gpEnemies[index].pSoldier = TacticalCreateCreature( YAF_MONSTER ); - gpEnemies[index].uiVObjectID = gpAR->iFaces; - gpEnemies[index].usIndex = YF_CREATURE_FACE; - gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; - gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); - } - for( i = 0; i < gpAR->ubYMCreatures; i++, index++ ) - { - gpEnemies[index].pSoldier = TacticalCreateCreature( YAM_MONSTER ); - gpEnemies[index].uiVObjectID = gpAR->iFaces; - gpEnemies[index].usIndex = YM_CREATURE_FACE; - gpEnemies[index].pSoldier->sSectorX = gpAR->ubSectorX; - gpEnemies[index].pSoldier->sSectorY = gpAR->ubSectorY; - swprintf( gpEnemies[index].pSoldier->name, gpStrategicString[ STR_AR_CREATURE_NAME ] ); - } - } if( gpAR->ubSectorX == gWorldSectorX && gpAR->ubSectorY == gWorldSectorY && !gbWorldSectorZ ) { @@ -2616,7 +2703,10 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Autoresolve2"); gpBattleGroup = NULL; - if( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE ) + if ( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE || + GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) { gubNumCreaturesAttackingTown = 0; gubSectorIDOfCreatureAttack = 0; @@ -2915,14 +3005,7 @@ void CalculateAutoResolveInfo() Assert( gpAR->ubSectorX >= 1 && gpAR->ubSectorX <= 16 ); Assert( gpAR->ubSectorY >= 1 && gpAR->ubSectorY <= 16 ); - if( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE ) - { -// GetNumberOfEnemiesInSector( gpAR->ubSectorX, gpAR->ubSectorY, - GetNumberOfEnemiesInFiveSectors( gpAR->ubSectorX, gpAR->ubSectorY, - &gpAR->ubAdmins, &gpAR->ubTroops, &gpAR->ubElites, &gpAR->ubTanks, &gpAR->ubJeeps ); - gpAR->ubEnemies = (UINT8)min( gpAR->ubAdmins + gpAR->ubTroops + gpAR->ubElites + gpAR->ubTanks + gpAR->ubJeeps, MAX_AR_TEAM_SIZE ); - } - else + if ( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE ) { if( gfTransferTacticalOppositionToAutoResolve ) { @@ -2948,8 +3031,35 @@ void CalculateAutoResolveInfo() gpAR->ubEnemies = (UINT8)min( gpAR->ubYMCreatures + gpAR->ubYFCreatures + gpAR->ubAMCreatures + gpAR->ubAFCreatures, MAX_AR_TEAM_SIZE ); } + else if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + if ( gfTransferTacticalOppositionToAutoResolve || gubNumCreaturesAttackingTown == 0 ) + { + DetermineOtherCreatureTownCompositionBasedOnTacticalInformation( &gubNumCreaturesAttackingTown, &gpAR->ubBloodcats, &gpAR->ubZombies, &gpAR->ubBandits ); + } + else + { + if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE ) + gpAR->ubBloodcats = gubNumCreaturesAttackingTown; + else if ( GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE ) + gpAR->ubZombies = gubNumCreaturesAttackingTown; + else if ( GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + gpAR->ubBandits = gubNumCreaturesAttackingTown; + } + + gpAR->ubEnemies = (UINT8)min( gubNumCreaturesAttackingTown, MAX_AR_TEAM_SIZE ); + } + else + { + GetNumberOfEnemiesInFiveSectors( gpAR->ubSectorX, gpAR->ubSectorY, + &gpAR->ubAdmins, &gpAR->ubTroops, &gpAR->ubElites, &gpAR->ubTanks, &gpAR->ubJeeps ); + + gpAR->ubEnemies = (UINT8)min( gpAR->ubAdmins + gpAR->ubTroops + gpAR->ubElites + gpAR->ubTanks + gpAR->ubJeeps, MAX_AR_TEAM_SIZE ); + } + gfTransferTacticalOppositionToAutoResolve = FALSE; -// gpAR->ubCivs = CountAllMilitiaInSector( gpAR->ubSectorX, gpAR->ubSectorY ); gpAR->ubCivs = CountAllMilitiaInFiveSectors( gpAR->ubSectorX, gpAR->ubSectorY ); gpAR->ubMercs = 0; VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; @@ -3693,6 +3803,7 @@ FLOAT CalcClassBonusOrPenalty( SOLDIERTYPE *pSoldier ) return 1.0f; case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_GREEN_MILITIA: + case SOLDIER_CLASS_BANDIT: return 0.7f; } @@ -3821,7 +3932,8 @@ void CalculateAttackValues() gpAR->usPlayerDefence += pCell->usDefence; ResetNextAttackCounter( pCell ); if( i > 6 ) - { //Too many militia, delay attack entry of extra mercs. + { + //Too many militia, delay attack entry of extra mercs. pCell->usNextAttack += (UINT32)(( i - 4 ) * 2000 ); } @@ -3832,10 +3944,10 @@ void CalculateAttackValues() pCell->usNextAttack += REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR * militiainsector; } - if( pCell->usNextAttack < usBestAttack ) usBestAttack = pCell->usNextAttack; } + //ENEMIES gpAR->usEnemyAttack = 0; gpAR->usEnemyDefence = 0; @@ -3847,7 +3959,7 @@ void CalculateAttackValues() // sOutnumberBonus = (INT16)min( sOutnumberBonus, max( sMaxBonus, 0 ) ); //} - for( i = 0; i < gpAR->ubEnemies; i++ ) + for( i = 0; i < gpAR->ubEnemies; ++i ) { pCell = &gpEnemies[ i ]; pSoldier = pCell->pSoldier; @@ -3886,19 +3998,21 @@ void CalculateAttackValues() ResetNextAttackCounter( pCell ); if( i > 4 && !(pCell->uiFlags & CELL_CREATURE) ) - { //Too many enemies, delay attack entry of extra mercs. + { + //Too many enemies, delay attack entry of extra mercs. pCell->usNextAttack += (UINT32)( ( i - 4 ) * 1000 ); } if ( i >= NumNonPlayerTeamMembersInSector( gpAR->ubSectorX, gpAR->ubSectorY, ENEMY_TEAM ) && !(pCell->uiFlags & CELL_CREATURE) ) - { //Extra delay if it's a reinforcement + { + //Extra delay if it's a reinforcement pCell->usNextAttack += REINFORCMENT_ATTACK_DELAY_PER_SOLDIER_IN_SECTOR * NumNonPlayerTeamMembersInSector( gpAR->ubSectorX, gpAR->ubSectorY, ENEMY_TEAM ); } - - + if( pCell->usNextAttack < usBestAttack ) usBestAttack = pCell->usNextAttack; } + //Now, because we are starting a new battle, we want to get the ball rolling a bit earlier. So, //we will take the usBestAttack value and subtract 60% of it from everybody's next attack. usBestAttack = usBestAttack * 60 / 100; @@ -3964,9 +4078,11 @@ SOLDIERCELL* ChooseTarget( SOLDIERCELL *pAttacker ) INT32 iRandom = -1; SOLDIERCELL *pTarget = NULL; UINT32 usSavedDefence; + //Determine what team we are attacking if( pAttacker->uiFlags & (CELL_ENEMY | CELL_CREATURE) ) - { //enemy team attacking a player + { + //enemy team attacking a player iAvailableTargets = gpAR->ubMercs + gpAR->ubCivs; index = 0; usSavedDefence = gpAR->usPlayerDefence; @@ -4302,11 +4418,13 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) usDefence = max(0, min(1000, usDefence )); /////////////////////////////////////////////////////////////////////////////////////////// - if( pAttacker->uiFlags & CELL_FEMALECREATURE ) + if( pAttacker->uiFlags & (CELL_FEMALECREATURE | CELL_BLOODCAT | CELL_ZOMBIE) ) { pAttacker->bWeaponSlot = HANDPOS; fMelee = TRUE; - fClaw = TRUE; + + if ( pAttacker->uiFlags & ( CELL_FEMALECREATURE | CELL_BLOODCAT ) ) + fClaw = TRUE; } // Flugente: if we are a tank, we might fire the main cannon... else if ( FireTankCannon( pAttacker ) ) @@ -4383,13 +4501,27 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) PlayAutoResolveSample( MISS_KNIFE, RATE_11025, 50, 1, MIDDLEPAN ); else if( fClaw ) { - if( Chance( 50 ) ) + if ( pAttacker->uiFlags & ( CELL_BLOODCAT ) ) { - PlayAutoResolveSample( ACR_SWIPE, RATE_11025, 50, 1, MIDDLEPAN ); + if ( Chance( 50 ) ) + { + PlayAutoResolveSample( BLOODCAT_ATTACK, RATE_11025, 50, 1, MIDDLEPAN ); + } + else + { + PlayAutoResolveSample( BLOODCAT_ROAR, RATE_11025, 50, 1, MIDDLEPAN ); + } } else { - PlayAutoResolveSample( ACR_LUNGE, RATE_11025, 50, 1, MIDDLEPAN ); + if ( Chance( 50 ) ) + { + PlayAutoResolveSample( ACR_SWIPE, RATE_11025, 50, 1, MIDDLEPAN ); + } + else + { + PlayAutoResolveSample( ACR_LUNGE, RATE_11025, 50, 1, MIDDLEPAN ); + } } } else @@ -4657,6 +4789,14 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) UpdateMilitia( militia ); } + // Flugente: for raids, we need to keep track of killed forces + if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + AddRaidPersonnel( -( pTarget->pSoldier->ubBodyType == BLOODCAT ), -( pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE ), -( pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) ); + } + // Flugente: disease HandleDeathDiseaseImplications( pTarget->pSoldier ); @@ -4681,6 +4821,9 @@ void AttackTarget( SOLDIERCELL *pAttacker, SOLDIERCELL *pTarget ) case SOLDIER_CLASS_ZOMBIE : gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsZombies++; break; + case SOLDIER_CLASS_BANDIT: + gMercProfiles[pAttacker->pSoldier->ubProfile].records.usKillsOthers++; + break; default : if ( CREATURE_OR_BLOODCAT( pTarget->pSoldier ) ) gMercProfiles[ pAttacker->pSoldier->ubProfile ].records.usKillsCreatures++; @@ -4875,6 +5018,14 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) UpdateMilitia( militia ); } + // Flugente: for raids, we need to keep track of killed forces + if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + AddRaidPersonnel( -( pTarget->pSoldier->ubBodyType == BLOODCAT ), -( pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE ), -( pTarget->pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) ); + } + // Flugente: disease HandleDeathDiseaseImplications( pTarget->pSoldier ); @@ -4915,6 +5066,9 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) case SOLDIER_CLASS_ZOMBIE : gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsZombies++; break; + case SOLDIER_CLASS_BANDIT: + gMercProfiles[pKiller->pSoldier->ubProfile].records.usKillsOthers++; + break; default : if ( CREATURE_OR_BLOODCAT( pTarget->pSoldier ) ) gMercProfiles[ pKiller->pSoldier->ubProfile ].records.usKillsCreatures++; @@ -4993,7 +5147,8 @@ void TargetHitCallback( SOLDIERCELL *pTarget, INT32 index ) PlayAutoResolveSample( (UINT8)DOORCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); PlayAutoResolveSample( (UINT8)HEADCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); } - if( iNewLife < -60 && !(pTarget->uiFlags & CELL_CREATURE) ) + + if( iNewLife < -60 && !(pTarget->uiFlags & CELL_BUG ) ) { //High damage death if( gpAR->fSound ) { @@ -5186,7 +5341,9 @@ BOOLEAN IsBattleOver() { if( gpEnemies[ i ].pSoldier->stats.bLife ) { - if( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE ) + if ( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && + GetEnemyEncounterCode() != BLOODCAT_ATTACK_CODE && + GetEnemyEncounterCode() != ZOMBIE_ATTACK_CODE ) { gpEnemies[ i ].pSoldier->DoMercBattleSound( BATTLE_SOUND_LAUGH1 ); } @@ -5473,7 +5630,9 @@ void ProcessBattleFrame() return; } CONTINUE_BATTLE: - if( IsBattleOver() || GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && AttemptPlayerCapture() ) + if ( IsBattleOver() || + (GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && GetEnemyEncounterCode() != BLOODCAT_ATTACK_CODE && GetEnemyEncounterCode() != ZOMBIE_ATTACK_CODE && GetEnemyEncounterCode() != BANDIT_ATTACK_CODE ) + && AttemptPlayerCapture() ) return; iRandom = PreRandom( iTotal ); @@ -5558,7 +5717,8 @@ void ProcessBattleFrame() else { if( pAttacker->uiFlags & CELL_RETREATING ) - { //The merc has successfully retreated. Remove the stats, and continue on. + { + //The merc has successfully retreated. Remove the stats, and continue on. if( pAttacker == gpAR->pRobotCell ) { if( gpAR->pRobotCell->pSoldier->ubRobotRemoteHolderID == NOBODY ) @@ -5574,16 +5734,19 @@ void ProcessBattleFrame() pAttacker->uiFlags |= CELL_RETREATED; continue; } + if( pAttacker->usAttack ) { pTarget = ChooseTarget( pAttacker ); - if( pAttacker->uiFlags & CELL_CREATURE && PreRandom( 100 ) < 7 ) + + if( pAttacker->uiFlags & CELL_BUG && PreRandom( 100 ) < 7 ) PlayAutoResolveSample( ACR_SMELL_THREAT + PreRandom( 2 ), RATE_11025, 50, 1, MIDDLEPAN ); else AttackTarget( pAttacker, pTarget ); + ResetNextAttackCounter( pAttacker ); pAttacker->usNextAttack += (UINT32)iTime; //tack on the remainder - iAttacksThisFrame++; + ++iAttacksThisFrame; } } } diff --git a/Strategic/Auto Resolve.h b/Strategic/Auto Resolve.h index 6154886b..a1d0acb5 100644 --- a/Strategic/Auto Resolve.h +++ b/Strategic/Auto Resolve.h @@ -28,6 +28,11 @@ enum MILITIA3F_FACE, JEEP_FACE, JEEP_WRECK, + ZOMBIE_FACE, + BANDIT1_FACE, + BANDIT1_F_FACE = BANDIT1_FACE + 4, + BLOODCAT_FACE = BANDIT1_F_FACE + 4, + BLOODCAT_SKULL, AUTORESOLVEFACES_MAX }; diff --git a/Strategic/Creature Spreading.cpp b/Strategic/Creature Spreading.cpp index d6fdad0f..18ffe2d1 100644 --- a/Strategic/Creature Spreading.cpp +++ b/Strategic/Creature Spreading.cpp @@ -32,6 +32,10 @@ #include "MessageBoxScreen.h" #include "Map Information.h" #include "Queen Command.h" + #include "ai.h" // added by Flugente + #include "Isometric Utils.h" // added by Flugente + #include "Soldier Create.h" // added by Flugente + #include "Player Command.h" // added by Flugente #endif #include "Strategic Mines.h" @@ -148,11 +152,16 @@ UINT8 gubAdultFemalesAttackingTown = 0; UINT8 gubCreatureBattleCode = CREATURE_BATTLE_CODE_NONE; UINT8 gubSectorIDOfCreatureAttack = 0; +// Flugente: bloodcat raids are mostly similar to creature attacks, so use the same code and differ when necessary with this +UINT8 guCreatureAttackType = CREATURE_ATTACK_TYPE_CREATURE; + extern UNDERGROUND_SECTORINFO* FindUnderGroundSector( INT16 sMapX, INT16 sMapY, UINT8 bMapZ ); extern UNDERGROUND_SECTORINFO* NewUndergroundNode( UINT8 ubSectorX, UINT8 ubSectorY, UINT8 ubSectorZ ); extern void BuildUndergroundSectorInfoList(); void DeleteCreatureDirectives(); +extern SECTOR_EXT_DATA SectorExternalData[256][4]; + //extern MINE_STATUS_TYPE gMineStatus[ MAX_NUMBER_OF_MINES ]; @@ -684,10 +693,10 @@ void AddCreaturesToBattle( UINT8 ubNumYoungMales, UINT8 ubNumYoungFemales, UINT8 switch( gsCreatureInsertionCode ) { case INSERTION_CODE_NORTH: bDesiredDirection = SOUTHEAST; break; - case INSERTION_CODE_EAST: bDesiredDirection = SOUTHWEST; break; + case INSERTION_CODE_EAST: bDesiredDirection = SOUTHWEST; break; case INSERTION_CODE_SOUTH: bDesiredDirection = NORTHWEST; break; - case INSERTION_CODE_WEST: bDesiredDirection = NORTHEAST; break; - case INSERTION_CODE_GRIDNO: break; + case INSERTION_CODE_WEST: bDesiredDirection = NORTHEAST; break; + case INSERTION_CODE_GRIDNO: break; default: AssertMsg( 0, "Illegal direction passed to AddCreaturesToBattle()" ); break; } @@ -772,6 +781,101 @@ void AddCreaturesToBattle( UINT8 ubNumYoungMales, UINT8 ubNumYoungFemales, UINT8 AllTeamsLookForAll( FALSE ); } +void AddCreaturesToBattle_Other( UINT8 ubNum ) +{ + INT32 iRandom; + SOLDIERTYPE *pSoldier; + MAPEDGEPOINTINFO MapEdgepointInfo = {}; + UINT8 bDesiredDirection = 0; + UINT8 ubCurrSlot = 0; + + switch ( gsCreatureInsertionCode ) + { + case INSERTION_CODE_NORTH: bDesiredDirection = SOUTHEAST; break; + case INSERTION_CODE_EAST: bDesiredDirection = SOUTHWEST; break; + case INSERTION_CODE_SOUTH: bDesiredDirection = NORTHWEST; break; + case INSERTION_CODE_WEST: bDesiredDirection = NORTHEAST; break; + case INSERTION_CODE_GRIDNO: break; + default: AssertMsg( 0, "Illegal direction passed to AddCreaturesToBattle_Bloodcats()" ); break; + } + +#ifdef JA2TESTVERSION + ScreenMsg( FONT_RED, MSG_INTERFACE, L"Creature attackers have arrived!" ); +#endif + + if ( gsCreatureInsertionCode != INSERTION_CODE_GRIDNO ) + { + ChooseMapEdgepoints( &MapEdgepointInfo, (UINT8)gsCreatureInsertionCode, ubNum ); + ubCurrSlot = 0; + } + + while ( ubNum ) + { + --ubNum; + + switch ( guCreatureAttackType ) + { + // this should not happen! + case CREATURE_ATTACK_TYPE_CREATURE: + pSoldier = TacticalCreateCreature( ADULTFEMALEMONSTER ); + break; + case CREATURE_ATTACK_TYPE_ZOMBIE: + pSoldier = TacticalCreateZombie(); + break; + case CREATURE_ATTACK_TYPE_BANDIT: + pSoldier = TacticalCreateBandit(); + break; + case CREATURE_ATTACK_TYPE_BLOODCAT: + pSoldier = TacticalCreateCreature( BLOODCAT ); + break; + } + + pSoldier->ubInsertionDirection = bDesiredDirection; + + //Setup the position + pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; + pSoldier->aiData.bHunting = TRUE; + + if ( gsCreatureInsertionCode != INSERTION_CODE_GRIDNO ) + { + if ( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) + { + //using an edgepoint + pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ubCurrSlot++]; + } + else + { + //no edgepoints left, so put him at the entrypoint. + pSoldier->ubStrategicInsertionCode = (UINT8)gsCreatureInsertionCode; + } + } + else + { + pSoldier->usStrategicInsertionData = gsCreatureInsertionGridNo; + } + + UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 ); + + if ( pSoldier ) + { + // send soldier to centre of map, roughly + pSoldier->aiData.sNoiseGridno = ( CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS ); + pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION; + } + } + + gsCreatureInsertionCode = 0; + gsCreatureInsertionGridNo = 0; + gubNumCreaturesAttackingTown = 0; + gubYoungMalesAttackingTown = 0; + gubYoungFemalesAttackingTown = 0; + gubAdultMalesAttackingTown = 0; + gubAdultFemalesAttackingTown = 0; + gubCreatureBattleCode = CREATURE_BATTLE_CODE_NONE; + gubSectorIDOfCreatureAttack = 0; + AllTeamsLookForAll( FALSE ); +} + void ChooseTownSectorToAttack( UINT8 ubSectorID, BOOLEAN fOverrideTest ) { INT32 iRandom, i; @@ -1061,6 +1165,7 @@ void CreatureAttackTown( UINT8 ubSectorID, BOOLEAN fOverrideTest ) return; } + guCreatureAttackType = CREATURE_ATTACK_TYPE_CREATURE; gubCreatureBattleCode = CREATURE_BATTLE_CODE_NONE; ubSectorX = SECTORX( ubSectorID ); @@ -1098,22 +1203,18 @@ void CreatureAttackTown( UINT8 ubSectorID, BOOLEAN fOverrideTest ) //Now that the sector has been chosen, attack it! if( PlayerGroupsInSector( ubSectorX, ubSectorY, 0 ) ) - { //we have players in the sector + { + gubCreatureBattleCode = CREATURE_BATTLE_CODE_PREBATTLEINTERFACE; + + //we have players in the sector if( ubSectorX == gWorldSectorX && ubSectorY == gWorldSectorY && !gbWorldSectorZ ) - { //This is the currently loaded sector. All we have to do is change the music and insert + { + //This is the currently loaded sector. All we have to do is change the music and insert //the creatures tactically. if( guiCurrentScreen == GAME_SCREEN ) { gubCreatureBattleCode = CREATURE_BATTLE_CODE_TACTICALLYADD; } - else - { - gubCreatureBattleCode = CREATURE_BATTLE_CODE_PREBATTLEINTERFACE; - } - } - else - { - gubCreatureBattleCode = CREATURE_BATTLE_CODE_PREBATTLEINTERFACE; } } else if ( NumNonPlayerTeamMembersInSector( ubSectorX, ubSectorY, MILITIA_TEAM ) ) @@ -1123,7 +1224,7 @@ void CreatureAttackTown( UINT8 ubSectorID, BOOLEAN fOverrideTest ) } else if( !StrategicMap[ ubSectorX + MAP_WORLD_X * ubSectorY ].fEnemyControlled ) { //player controlled sector -- eat some civilians - AdjustLoyaltyForCivsEatenByMonsters( ubSectorX, ubSectorY, gubNumCreaturesAttackingTown ); + AdjustLoyaltyForCivsEatenByMonsters( ubSectorX, ubSectorY, gubNumCreaturesAttackingTown, ( guCreatureAttackType == CREATURE_ATTACK_TYPE_BANDIT ) ); SectorInfo[ ubSectorID ].ubDayOfLastCreatureAttack = (UINT8)GetWorldDay(); return; } @@ -1157,6 +1258,223 @@ void CreatureAttackTown( UINT8 ubSectorID, BOOLEAN fOverrideTest ) LockPauseState( 2 ); } +void CreatureAttackTown_OtherCreatures( UINT8 ubSectorID, UINT8 ubType ) +{ + if ( gfWorldLoaded && gTacticalStatus.fEnemyInSector ) + { + //Battle currently in progress, repost the event + AddStrategicEvent( ubType, GetWorldTotalMin() + Random( 10 ), ubSectorID ); + return; + } + + // if this sector is not under player control, nothing to do + if ( StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX ( ubSectorID )].fEnemyControlled ) + return; + + guCreatureAttackType = ubType; + + UINT8 ubSectorX = SECTORX( ubSectorID ); + UINT8 ubSectorY = SECTORY( ubSectorID ); + + // test whether the attack is possible in this sector according to xml settings + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_BLOODCAT: + if ( !gGameExternalOptions.gRaid_Bloodcats || !( SectorExternalData[ubSectorID][0].usSectorFlagMask & SECTORFLAG_RAIDPOSSIBLE_BLOODCAT ) ) + return; + break; + + case CREATURE_ATTACK_TYPE_ZOMBIE: + if ( !gGameExternalOptions.gRaid_Zombies || !( SectorExternalData[ubSectorID][0].usSectorFlagMask & SECTORFLAG_RAIDPOSSIBLE_ZOMBIE ) ) + return; + break; + + case CREATURE_ATTACK_TYPE_BANDIT: + if ( !gGameExternalOptions.gRaid_Bandits || !( SectorExternalData[ubSectorID][0].usSectorFlagMask & SECTORFLAG_RAIDPOSSIBLE_BANDIT ) ) + return; + break; + + default: + return; + break; + } + + // choose an insertion direction. The intention is to make it seem as if the raids occur from wilderness sectors into the sector + // note that attacks against non-town sectors work, but if no player forces are present, nothing happens (as loyalty won't be affected) + std::vector possibleinsertioncodes; + for ( INT16 i = 0; i < THROUGH_STRATEGIC_MOVE; ++i ) + { + INT16 loopX = ubSectorX; + INT16 loopY = ubSectorY; + + if ( i == NORTH_STRATEGIC_MOVE ) --loopY; + else if ( i == EAST_STRATEGIC_MOVE ) ++loopX; + else if ( i == SOUTH_STRATEGIC_MOVE ) ++loopY; + else if ( i == WEST_STRATEGIC_MOVE ) --loopX; + + if ( loopX < 1 || loopX >= MAP_WORLD_X - 1 || loopY < 1 || loopY >= MAP_WORLD_Y - 1 ) + continue; + + UINT8 townid_B = GetTownIdForSector( loopX, loopY ); + + if ( townid_B != BLANK_SECTOR ) + continue; + + UINT8 originsector = SECTOR(loopX, loopY); + + // valid entry + if ( SectorInfo[originsector].ubTraversability[i] != GROUNDBARRIER && + SectorInfo[originsector].ubTraversability[i] != TOWN && + SectorInfo[originsector].ubTraversability[i] != WATER && + SectorInfo[originsector].ubTraversability[i] != EDGEOFWORLD ) + { + possibleinsertioncodes.push_back( i ); + + gsCreatureInsertionCode = i; + } + } + + if ( possibleinsertioncodes.empty() ) + return; + + gsCreatureInsertionCode = possibleinsertioncodes[Random( possibleinsertioncodes.size())]; + + // determine how strong the player presence is, and then determine whether to attack, with how many dudes, or not to attack because the sector is defended to strongly + UINT16 militiapresent = NumNonPlayerTeamMembersInSector( ubSectorX, ubSectorY, MILITIA_TEAM ); + UINT16 mercspresent = NumPlayerTeamMembersInSector( ubSectorX, ubSectorY, 0 ); + + // determine how many troops are available and stop the attack if we don't have enough + INT32 available_bloodcats; + INT32 available_zombies; + INT32 available_bandits; + GetRaidPersonnel( available_bloodcats, available_zombies, available_bandits ); + + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_BLOODCAT: + gubNumCreaturesAttackingTown = min( gGameExternalOptions.gRaidMaxSize_Bloodcats, available_bloodcats ); + + // bloodcats won't attack if player forces are too numerous or not enough forces are available + if ( gubNumCreaturesAttackingTown < mercspresent + militiapresent ) + return; + break; + + case CREATURE_ATTACK_TYPE_ZOMBIE: + gubNumCreaturesAttackingTown = min( gGameExternalOptions.gRaidMaxSize_Zombies, available_zombies ); + break; + + case CREATURE_ATTACK_TYPE_BANDIT: + gubNumCreaturesAttackingTown = min( gGameExternalOptions.gRaidMaxSize_Bandits, available_bandits ); + + // bandits won't attack if player forces are too numerous or not enough forces are available + if ( gubNumCreaturesAttackingTown < mercspresent + militiapresent ) + return; + break; + + default: + return; + break; + } + + // safety check - not too small + if ( gubNumCreaturesAttackingTown < 1 ) + return; + + gubCreatureBattleCode = CREATURE_BATTLE_CODE_NONE; + gubSectorIDOfCreatureAttack = ubSectorID; + + //Now that the sector has been chosen, attack it! + if ( mercspresent ) + { + gubCreatureBattleCode = CREATURE_BATTLE_CODE_PREBATTLEINTERFACE; + + //we have players in the sector + if ( ubSectorX == gWorldSectorX && ubSectorY == gWorldSectorY && !gbWorldSectorZ ) + { + //This is the currently loaded sector. All we have to do is change the music and insert + //the creatures tactically. + if ( guiCurrentScreen == GAME_SCREEN ) + { + gubCreatureBattleCode = CREATURE_BATTLE_CODE_TACTICALLYADD; + } + } + } + else if ( militiapresent ) + { + //we have militia in the sector + gubCreatureBattleCode = CREATURE_BATTLE_CODE_AUTORESOLVE; + } + else + { + // player controlled sector -- eat some civilians + AdjustLoyaltyForCivsEatenByMonsters( ubSectorX, ubSectorY, min(1, gubNumCreaturesAttackingTown / 2), ( guCreatureAttackType == CREATURE_ATTACK_TYPE_BANDIT ) ); + + // do NOT change sector ownership. This would cause the AI to stop attacking, and we can't have that, no? + //SetThisSectorAsEnemyControlled( ubSectorX, ubSectorY, 0, TRUE ); + SectorInfo[ubSectorID].ubDayOfLastCreatureAttack = GetWorldDay(); + + return; + } + + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_BLOODCAT: + SetEnemyEncounterCode( BLOODCAT_ATTACK_CODE ); + break; + + case CREATURE_ATTACK_TYPE_ZOMBIE: + SetEnemyEncounterCode( ZOMBIE_ATTACK_CODE ); + break; + + case CREATURE_ATTACK_TYPE_BANDIT: + SetEnemyEncounterCode( BANDIT_ATTACK_CODE ); + break; + + default: + SetEnemyEncounterCode( CREATURE_ATTACK_CODE ); + break; + } + + SectorInfo[ubSectorID].ubDayOfLastCreatureAttack = (UINT8)GetWorldDay(); + + switch ( gubCreatureBattleCode ) + { + case CREATURE_BATTLE_CODE_PREBATTLEINTERFACE: + InitPreBattleInterface( NULL, TRUE ); + break; + case CREATURE_BATTLE_CODE_AUTORESOLVE: + gfAutomaticallyStartAutoResolve = TRUE; + InitPreBattleInterface( NULL, TRUE ); + break; + case CREATURE_BATTLE_CODE_TACTICALLYADD: + if ( is_networked ) + { + if ( is_server && gCreatureEnabled == 1 ) + PrepareCreaturesForBattle(); + } + else + PrepareCreaturesForBattle(); + + // alert the creatures + HandleInitialRedAlert( CREATURE_TEAM, FALSE ); + + // we need this so that the battle is properly recognized in strategic + { + //Set up the location as well as turning on the blit flag. + gubPBSectorX = (UINT8)ubSectorX; + gubPBSectorY = (UINT8)ubSectorY; + gubPBSectorZ = (UINT8)0; + gfBlitBattleSectorLocator = TRUE; + } + + break; + } + + InterruptTime(); + PauseGame(); + LockPauseState( 2 ); +} + //Called by campaign init. void ChooseCreatureQuestStartDay() { @@ -1376,15 +1694,13 @@ void DetermineCreatureTownComposition( UINT8 ubNumCreatures, UINT8 *pubNumYoungM void DetermineCreatureTownCompositionBasedOnTacticalInformation( UINT8 *pubNumCreatures, UINT8 *pubNumYoungMales, UINT8 *pubNumYoungFemales, UINT8 *pubNumAdultMales, UINT8 *pubNumAdultFemales ) { - SECTORINFO *pSector; - INT32 i; SOLDIERTYPE *pSoldier; - pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; + SECTORINFO* pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; *pubNumCreatures = 0; pSector->ubNumCreatures = 0; pSector->ubCreaturesInBattle = 0; - for( i = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ ) + for( INT32 i = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; ++i ) { pSoldier = MercPtrs[ i ]; if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife ) @@ -1412,7 +1728,37 @@ void DetermineCreatureTownCompositionBasedOnTacticalInformation( UINT8 *pubNumCr } } +void DetermineOtherCreatureTownCompositionBasedOnTacticalInformation( UINT8* pubNumCreatures, UINT8* pubNumBloodcats, UINT8* pubNumZombies, UINT8* pubNumBandits ) +{ + SOLDIERTYPE *pSoldier; + SECTORINFO* pSector = &SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )]; + *pubNumCreatures = 0; + pSector->ubNumCreatures = 0; + pSector->ubCreaturesInBattle = 0; + for ( INT32 i = gTacticalStatus.Team[CREATURE_TEAM].bFirstID; i <= gTacticalStatus.Team[CREATURE_TEAM].bLastID; ++i ) + { + pSoldier = MercPtrs[i]; + if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife ) + { + if ( pSoldier->IsZombie() ) + { + ( *pubNumCreatures )++; + ( *pubNumZombies )++; + } + else if ( pSoldier->ubBodyType == BLOODCAT ) + { + ( *pubNumCreatures )++; + ( *pubNumBloodcats )++; + } + else + { + ( *pubNumCreatures )++; + ( *pubNumBandits )++; + } + } + } +} BOOLEAN PrepareCreaturesForBattle() { @@ -1481,27 +1827,29 @@ BOOLEAN PrepareCreaturesForBattle() { return FALSE; } + //gfUseCreatureMusic = TRUE; //creatures are here, so play creature music ubCreatureHabitat = pSector->ubCreatureHabitat; ubNumCreatures = pSector->ubNumCreatures; } else - { //creatures are attacking a town sector + { + //creatures are attacking a town sector //gfUseCreatureMusic = TRUE; UseCreatureMusic(TRUE); - #ifdef NEWMUSIC +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalNothing[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalNothing[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_NOTHING, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalNothing[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_NOTHING ); // externalize to xml data //ubCreatureHabitat = MINE_EXIT; //assign the B1 underground habitat composition to the attacking creatures - for (UINT8 i = 0; i < MAX_NUMBER_OF_CREATURE_SECTORS; i++) + for (UINT8 i = 0; i < MAX_NUMBER_OF_CREATURE_SECTORS; ++i) { INT16 sX = gCreaturePlacements[ giLairID ].sAttackSourceX; INT16 sY = gCreaturePlacements[ giLairID ].sAttackSourceY; @@ -1515,6 +1863,7 @@ BOOLEAN PrepareCreaturesForBattle() break; } } + ubNumCreatures = gubNumCreaturesAttackingTown; } @@ -1576,90 +1925,132 @@ BOOLEAN PrepareCreaturesForBattle() } */ - //out-of-range habitat - if( ubCreatureHabitat >= NUMBER_OF_CREATURE_COMPOSITIONS ) + if ( guCreatureAttackType == CREATURE_ATTACK_TYPE_CREATURE ) { - AssertMsg( 0, String( "Invalid creature habitat ID of %d for PrepareCreaturesForBattle.", ubCreatureHabitat ) ); - } - - //queen sector - if( !ubCreatureHabitat ) - fQueen = TRUE; - else - fQueen = FALSE; + //out-of-range habitat + if ( ubCreatureHabitat >= NUMBER_OF_CREATURE_COMPOSITIONS ) + { + AssertMsg( 0, String( "Invalid creature habitat ID of %d for PrepareCreaturesForBattle.", ubCreatureHabitat ) ); + } - ubLarvaePercentage = gCreatureComposition[ ubCreatureHabitat ].ubLarvaePercent; - ubInfantPercentage = gCreatureComposition[ ubCreatureHabitat ].ubInfantPercent; - ubYoungMalePercentage = gCreatureComposition[ ubCreatureHabitat ].ubYoungMalePercent; - ubYoungFemalePercentage = gCreatureComposition[ ubCreatureHabitat ].ubYoungFemalePercent; - ubAdultMalePercentage = gCreatureComposition[ ubCreatureHabitat ].ubAdultMalePercent; - ubAdultFemalePercentage = gCreatureComposition[ ubCreatureHabitat ].ubAdultFemalePercent; - - //First step is to convert the percentages into the numbers we will use. - if( fQueen ) - { - ubNumCreatures--; - } - ubInfantPercentage += ubLarvaePercentage; - ubYoungMalePercentage += ubInfantPercentage; - ubYoungFemalePercentage += ubYoungMalePercentage; - ubAdultMalePercentage += ubYoungFemalePercentage; - ubAdultFemalePercentage += ubAdultMalePercentage; - if( ubAdultFemalePercentage != 100 ) - { - AssertMsg( 0, "Percentage for adding creatures don't add up to 100." ); - } - //Second step is to determine the breakdown of the creatures randomly. - i = ubNumCreatures; - while( i-- ) - { - iRandom = Random( 100 ); - if( iRandom < ubLarvaePercentage ) - ubNumLarvae++; - else if( iRandom < ubInfantPercentage ) - ubNumInfants++; - else if( iRandom < ubYoungMalePercentage ) - ubNumYoungMales++; - else if( iRandom < ubYoungFemalePercentage ) - ubNumYoungFemales++; - else if( iRandom < ubAdultMalePercentage ) - ubNumAdultMales++; + //queen sector + if ( !ubCreatureHabitat ) + fQueen = TRUE; else - ubNumAdultFemales++; + fQueen = FALSE; + + ubLarvaePercentage = gCreatureComposition[ubCreatureHabitat].ubLarvaePercent; + ubInfantPercentage = gCreatureComposition[ubCreatureHabitat].ubInfantPercent; + ubYoungMalePercentage = gCreatureComposition[ubCreatureHabitat].ubYoungMalePercent; + ubYoungFemalePercentage = gCreatureComposition[ubCreatureHabitat].ubYoungFemalePercent; + ubAdultMalePercentage = gCreatureComposition[ubCreatureHabitat].ubAdultMalePercent; + ubAdultFemalePercentage = gCreatureComposition[ubCreatureHabitat].ubAdultFemalePercent; + + //First step is to convert the percentages into the numbers we will use. + if ( fQueen ) + { + ubNumCreatures--; + } + ubInfantPercentage += ubLarvaePercentage; + ubYoungMalePercentage += ubInfantPercentage; + ubYoungFemalePercentage += ubYoungMalePercentage; + ubAdultMalePercentage += ubYoungFemalePercentage; + ubAdultFemalePercentage += ubAdultMalePercentage; + if ( ubAdultFemalePercentage != 100 ) + { + AssertMsg( 0, "Percentage for adding creatures don't add up to 100." ); + } + //Second step is to determine the breakdown of the creatures randomly. + i = ubNumCreatures; + while ( i-- ) + { + iRandom = Random( 100 ); + if ( iRandom < ubLarvaePercentage ) + ubNumLarvae++; + else if ( iRandom < ubInfantPercentage ) + ubNumInfants++; + else if ( iRandom < ubYoungMalePercentage ) + ubNumYoungMales++; + else if ( iRandom < ubYoungFemalePercentage ) + ubNumYoungFemales++; + else if ( iRandom < ubAdultMalePercentage ) + ubNumAdultMales++; + else + ubNumAdultFemales++; + } } if( gbWorldSectorZ ) { - UNDERGROUND_SECTORINFO *pUndergroundSector; - pUndergroundSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); + UNDERGROUND_SECTORINFO *pUndergroundSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); + if( !pUndergroundSector ) - { //No info?!!!!! + { + //No info?!!!!! AssertMsg( 0, "Please report underground sector you are in or going to and send save if possible. KM : 0" ); return FALSE; } + pUndergroundSector->ubCreaturesInBattle = pUndergroundSector->ubNumCreatures; } else { SECTORINFO *pSector; pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; - pSector->ubNumCreatures = ubNumCreatures; - pSector->ubCreaturesInBattle = ubNumCreatures; - } + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_CREATURE: + case CREATURE_ATTACK_TYPE_ZOMBIE: + case CREATURE_ATTACK_TYPE_BANDIT: + pSector->ubNumCreatures = ubNumCreatures; + pSector->ubCreaturesInBattle = ubNumCreatures; + break; + + case CREATURE_ATTACK_TYPE_BLOODCAT: + pSector->bBloodCats = ubNumCreatures; + pSector->ubCreaturesInBattle = ubNumCreatures; + break; + } + } + switch( gubCreatureBattleCode ) { case CREATURE_BATTLE_CODE_NONE: //in the mines - AddSoldierInitListCreatures( fQueen, ubNumLarvae, ubNumInfants, - ubNumYoungMales, ubNumYoungFemales, ubNumAdultMales, ubNumAdultFemales ); + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_CREATURE: + AddSoldierInitListCreatures( fQueen, ubNumLarvae, ubNumInfants, ubNumYoungMales, ubNumYoungFemales, ubNumAdultMales, ubNumAdultFemales ); + break; + + case CREATURE_ATTACK_TYPE_ZOMBIE: + case CREATURE_ATTACK_TYPE_BANDIT: + case CREATURE_ATTACK_TYPE_BLOODCAT: + AddSoldierInitListOtherCreatures( ubNumCreatures ); + break; + } break; + case CREATURE_BATTLE_CODE_TACTICALLYADD: //creature attacking a town sector case CREATURE_BATTLE_CODE_PREBATTLEINTERFACE: - AddCreaturesToBattle( ubNumYoungMales, ubNumYoungFemales, ubNumAdultMales, ubNumAdultFemales ); + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_CREATURE: + AddCreaturesToBattle( ubNumYoungMales, ubNumYoungFemales, ubNumAdultMales, ubNumAdultFemales ); + break; + + case CREATURE_ATTACK_TYPE_ZOMBIE: + case CREATURE_ATTACK_TYPE_BANDIT: + case CREATURE_ATTACK_TYPE_BLOODCAT: + AddCreaturesToBattle_Other( ubNumCreatures ); + break; + } break; + case CREATURE_BATTLE_CODE_AUTORESOLVE: return FALSE; } + return TRUE; } diff --git a/Strategic/Creature Spreading.h b/Strategic/Creature Spreading.h index 3ec1a195..fed460c9 100644 --- a/Strategic/Creature Spreading.h +++ b/Strategic/Creature Spreading.h @@ -15,6 +15,7 @@ UINT8 CreaturesInUndergroundSector( UINT8 ubSectorID, UINT8 ubSectorZ ); BOOLEAN PrepareCreaturesForBattle(); void CreatureNightPlanning(); void CreatureAttackTown( UINT8 ubSectorID, BOOLEAN fOverrideTest ); +void CreatureAttackTown_OtherCreatures( UINT8 ubSectorID, UINT8 ubType ); void CheckConditionsForTriggeringCreatureQuest( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ); @@ -44,6 +45,14 @@ enum{ }; extern UINT8 gubCreatureBattleCode; +enum { + CREATURE_ATTACK_TYPE_CREATURE, + CREATURE_ATTACK_TYPE_BLOODCAT, + CREATURE_ATTACK_TYPE_ZOMBIE, + CREATURE_ATTACK_TYPE_BANDIT, +}; +extern UINT8 guCreatureAttackType; + void DetermineCreatureTownComposition( UINT8 ubNumCreatures, UINT8 *pubNumYoungMales, UINT8 *pubNumYoungFemales, UINT8 *pubNumAdultMales, UINT8 *pubNumAdultFemales ); @@ -52,6 +61,8 @@ void DetermineCreatureTownCompositionBasedOnTacticalInformation( UINT8 *pubNumCr UINT8 *pubNumYoungMales, UINT8 *pubNumYoungFemales, UINT8 *pubNumAdultMales, UINT8 *pubNumAdultFemales ); +void DetermineOtherCreatureTownCompositionBasedOnTacticalInformation( UINT8* pubNumCreatures, UINT8* pubNumBloodcats, UINT8* pubNumZombies, UINT8* pubNumBandits ); + BOOLEAN PlayerGroupIsInACreatureInfestedMine(); diff --git a/Strategic/Game Event Hook.cpp b/Strategic/Game Event Hook.cpp index 5e7c229d..58b26428 100644 --- a/Strategic/Game Event Hook.cpp +++ b/Strategic/Game Event Hook.cpp @@ -644,6 +644,22 @@ BOOLEAN ExecuteStrategicEvent( STRATEGICEVENT *pEvent ) case EVENT_INTEL_PHOTOFACT_VERIFY: LuaVerifyPhotoState( (INT16)pEvent->uiParam ); break; + + case EVENT_DAILY_RAID_EVENTS: + HandleRaidEventPlanning(); + break; + + case EVENT_BLOODCAT_ATTACK: + CreatureAttackTown_OtherCreatures( (UINT8)pEvent->uiParam, CREATURE_ATTACK_TYPE_BLOODCAT ); + break; + + case EVENT_ZOMBIE_ATTACK: + CreatureAttackTown_OtherCreatures( (UINT8)pEvent->uiParam, CREATURE_ATTACK_TYPE_ZOMBIE ); + break; + + case EVENT_BANDIT_ATTACK: + CreatureAttackTown_OtherCreatures( (UINT8)pEvent->uiParam, CREATURE_ATTACK_TYPE_BANDIT ); + break; } gfPreventDeletionOfAnyEvent = fOrigPreventFlag; return TRUE; diff --git a/Strategic/Game Event Hook.h b/Strategic/Game Event Hook.h index 6fa8c7a3..6bf86724 100644 --- a/Strategic/Game Event Hook.h +++ b/Strategic/Game Event Hook.h @@ -143,6 +143,11 @@ enum EVENT_INTEL_ENRICO_EMAIL, // Flugente: Enrico sends us an email introduction to intel EVENT_INTEL_PHOTOFACT_VERIFY, // verification of a photo fact + EVENT_DAILY_RAID_EVENTS, // Flugente: daily planning of raids on player sectors + EVENT_BLOODCAT_ATTACK, // Flugente: like a creature attack, but with cats + EVENT_ZOMBIE_ATTACK, // Flugente: like a creature attack, but with zombies + EVENT_BANDIT_ATTACK, // Flugente: like a creature attack, but with bandits + NUMBER_OF_EVENT_TYPES_PLUS_ONE, NUMBER_OF_EVENT_TYPES = NUMBER_OF_EVENT_TYPES_PLUS_ONE - 1 }; diff --git a/Strategic/LuaInitNPCs.cpp b/Strategic/LuaInitNPCs.cpp index a4b4d7a6..1e295adc 100644 --- a/Strategic/LuaInitNPCs.cpp +++ b/Strategic/LuaInitNPCs.cpp @@ -4051,22 +4051,18 @@ return 0; // used when monsters attack a town sector without going through tactical and they win static int l_AdjustLoyaltyForCivsEatenByMonsters(lua_State *L) { -UINT8 n = lua_gettop(L); -int i; -INT16 sSectorX; -INT16 sSectorY; -UINT8 ubHowMany; - - for (i= 1; i<=n; i++ ) + UINT8 n = lua_gettop(L); + + if ( n >= 3 ) { - if (i == 1 ) sSectorX = lua_tointeger(L,i); - if (i == 2 ) sSectorY = lua_tointeger(L,i); - if (i == 3 ) ubHowMany = lua_tointeger(L,i); + INT16 sSectorX = lua_tointeger( L, 1 ); + INT16 sSectorY = lua_tointeger( L, 2 ); + UINT8 ubHowMany = lua_tointeger( L, 3 ); + + AdjustLoyaltyForCivsEatenByMonsters( sSectorX, sSectorY, ubHowMany, ( guCreatureAttackType == CREATURE_ATTACK_TYPE_BANDIT ) ); } - AdjustLoyaltyForCivsEatenByMonsters( sSectorX, sSectorY, ubHowMany); - -return 0; + return 0; } static int l_IsTownUnderCompleteControlByPlayer(lua_State *L) diff --git a/Strategic/PreBattle Interface.cpp b/Strategic/PreBattle Interface.cpp index d3510b93..36a77f82 100644 --- a/Strategic/PreBattle Interface.cpp +++ b/Strategic/PreBattle Interface.cpp @@ -414,7 +414,7 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) //There are hostile civilians, so no autoresolve allowed. SetExplicitEnemyEncounterCode( HOSTILE_CIVILIANS_CODE ); } - else if( HostileBloodcatsPresent() ) + else if ( GetEnemyEncounterCode() != BLOODCAT_ATTACK_CODE && HostileBloodcatsPresent() ) { //There are bloodcats in the sector, so no autoresolve allowed SetExplicitEnemyEncounterCode( HOSTILE_BLOODCATS_CODE ); @@ -447,8 +447,12 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) GetEnemyEncounterCode() == CREATURE_ATTACK_CODE || GetEnemyEncounterCode() == ENEMY_AMBUSH_DEPLOYMENT_CODE || GetEnemyEncounterCode() == ENEMY_INVASION_AIRDROP_CODE || - GetEnemyEncounterCode() == CONCEALINSERTION_CODE ) - { //use same code + GetEnemyEncounterCode() == CONCEALINSERTION_CODE || + GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + //use same code SetExplicitEnemyEncounterCode( GetEnemyEncounterCode() ); } else @@ -636,7 +640,24 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) else { //creature's attacking! - SetEnemyEncounterCode( CREATURE_ATTACK_CODE); + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_BLOODCAT: + SetEnemyEncounterCode( BLOODCAT_ATTACK_CODE ); + break; + + case CREATURE_ATTACK_TYPE_ZOMBIE: + SetEnemyEncounterCode( ZOMBIE_ATTACK_CODE ); + break; + + case CREATURE_ATTACK_TYPE_BANDIT: + SetEnemyEncounterCode( BANDIT_ATTACK_CODE ); + break; + + default: + SetEnemyEncounterCode( CREATURE_ATTACK_CODE ); + break; + } } } else if ( gpBattleGroup->usGroupTeam == OUR_TEAM ) @@ -901,7 +922,10 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) GetEnemyEncounterCode() == BLOODCAT_AMBUSH_CODE || GetEnemyEncounterCode() == ENEMY_AMBUSH_DEPLOYMENT_CODE || GetEnemyEncounterCode() == CREATURE_ATTACK_CODE || - is_client) + GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE || + is_client) { DisableButton( iPBButton[ 2 ] ); SetButtonFastHelpText( iPBButton[ 2 ], gzNonPersistantPBIText[ 9 ] ); @@ -931,6 +955,9 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) case ENEMY_ENCOUNTER_CODE: case ENEMY_INVASION_CODE: case ENEMY_INVASION_AIRDROP_CODE: + case BLOODCAT_ATTACK_CODE: + case ZOMBIE_ATTACK_CODE: + case BANDIT_ATTACK_CODE: SetButtonFastHelpText( iPBButton[ 0 ], gzNonPersistantPBIText[ 2 ] ); break; case ENTERING_ENEMY_SECTOR_CODE: @@ -1211,6 +1238,18 @@ void RenderPBHeader( INT32 *piX, INT32 *piWidth) case CONCEALINSERTION_CODE: swprintf( str, gpStrategicString[STR_PB_ENEMYENCOUNTER_HEADER] ); break; + case BLOODCAT_ATTACK_CODE: + swprintf( str, gpStrategicString[STR_PB_BLOODCATRAID_HEADER] ); + gfBlinkHeader = TRUE; + break; + case ZOMBIE_ATTACK_CODE: + swprintf( str, gpStrategicString[STR_PB_ZOMBIERAID_HEADER] ); + gfBlinkHeader = TRUE; + break; + case BANDIT_ATTACK_CODE: + swprintf( str, gpStrategicString[STR_PB_BANDITRAID_HEADER] ); + gfBlinkHeader = TRUE; + break; } width = StringPixLength( str, FONT10ARIALBOLD ); x = 130 - width / 2; @@ -1307,10 +1346,20 @@ void RenderPreBattleInterface() { swprintf( str, gpStrategicString[STR_PB_CREATURES ] ); } - else if( GetEnemyEncounterCode() == BLOODCAT_AMBUSH_CODE || GetEnemyEncounterCode() == ENTERING_BLOODCAT_LAIR_CODE ) + else if( GetEnemyEncounterCode() == BLOODCAT_AMBUSH_CODE || + GetEnemyEncounterCode() == ENTERING_BLOODCAT_LAIR_CODE || + GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE ) { swprintf( str, gpStrategicString[ STR_PB_BLOODCATS ] ); } + else if ( GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE ) + { + swprintf( str, gpStrategicString[STR_PB_ZOMBIES] ); + } + else if ( GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + swprintf( str, gpStrategicString[STR_PB_BANDITS] ); + } else { swprintf( str, gpStrategicString[ STR_PB_ENEMIES ] ); @@ -1371,6 +1420,9 @@ void RenderPreBattleInterface() if( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE || GetEnemyEncounterCode() == BLOODCAT_AMBUSH_CODE || GetEnemyEncounterCode() == ENTERING_BLOODCAT_LAIR_CODE || + GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE || WhatPlayerKnowsAboutEnemiesInSector( gubPBSectorX, gubPBSectorY ) < KNOWS_THEYRE_THERE_AND_HOW_MANY ) // HEADROCK HAM 5: New case above this one... { // don't know how many @@ -1600,7 +1652,10 @@ void GoToSectorCallback( GUI_BUTTON *btn, INT32 reason ) GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && GetEnemyEncounterCode() != BLOODCAT_AMBUSH_CODE && GetEnemyEncounterCode() != ENEMY_INVASION_AIRDROP_CODE && - GetEnemyEncounterCode() != CONCEALINSERTION_CODE ) + GetEnemyEncounterCode() != CONCEALINSERTION_CODE && + GetEnemyEncounterCode() != BLOODCAT_ATTACK_CODE && + GetEnemyEncounterCode() != ZOMBIE_ATTACK_CODE && + GetEnemyEncounterCode() != BANDIT_ATTACK_CODE ) { gfEnterTacticalPlacementGUI = TRUE; } @@ -2404,6 +2459,7 @@ void LogBattleResults( UINT8 ubVictoryCode) switch( GetEnemyEncounterCode() ) { case ENEMY_INVASION_CODE: + case BANDIT_ATTACK_CODE: AddHistoryToPlayersLog( HISTORY_DEFENDEDTOWNSECTOR, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case ENEMY_ENCOUNTER_CODE: @@ -2418,10 +2474,12 @@ void LogBattleResults( UINT8 ubVictoryCode) AddHistoryToPlayersLog( HISTORY_SUCCESSFULATTACK, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case CREATURE_ATTACK_CODE: + case ZOMBIE_ATTACK_CODE: AddHistoryToPlayersLog( HISTORY_CREATURESATTACKED, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case BLOODCAT_AMBUSH_CODE: case ENTERING_BLOODCAT_LAIR_CODE: + case BLOODCAT_ATTACK_CODE: AddHistoryToPlayersLog( HISTORY_SLAUGHTEREDBLOODCATS, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case CONCEALINSERTION_CODE: @@ -2436,6 +2494,7 @@ void LogBattleResults( UINT8 ubVictoryCode) switch( GetEnemyEncounterCode() ) { case ENEMY_INVASION_CODE: + case BANDIT_ATTACK_CODE: AddHistoryToPlayersLog( HISTORY_LOSTTOWNSECTOR, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case ENEMY_ENCOUNTER_CODE: @@ -2450,10 +2509,12 @@ void LogBattleResults( UINT8 ubVictoryCode) AddHistoryToPlayersLog( HISTORY_UNSUCCESSFULATTACK, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case CREATURE_ATTACK_CODE: + case ZOMBIE_ATTACK_CODE: AddHistoryToPlayersLog( HISTORY_CREATURESATTACKED, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case BLOODCAT_AMBUSH_CODE: case ENTERING_BLOODCAT_LAIR_CODE: + case BLOODCAT_ATTACK_CODE: AddHistoryToPlayersLog( HISTORY_KILLEDBYBLOODCATS, 0, GetWorldTotalMin(), sSectorX, sSectorY ); break; case CONCEALINSERTION_CODE: @@ -2464,6 +2525,9 @@ void LogBattleResults( UINT8 ubVictoryCode) switch( GetEnemyEncounterCode() ) { case ENEMY_INVASION_CODE: + case BLOODCAT_ATTACK_CODE: + case ZOMBIE_ATTACK_CODE: + case BANDIT_ATTACK_CODE: gCurrentIncident.usIncidentFlags |= INCIDENT_ATTACK_ENEMY; break; case ENEMY_ENCOUNTER_CODE: diff --git a/Strategic/PreBattle Interface.h b/Strategic/PreBattle Interface.h index 80f241e6..d8bc045e 100644 --- a/Strategic/PreBattle Interface.h +++ b/Strategic/PreBattle Interface.h @@ -43,6 +43,10 @@ enum ENEMY_AMBUSH_DEPLOYMENT_CODE, // Flugente: an ambush in which we can deploy our mercs (somewhat) ENEMY_INVASION_AIRDROP_CODE, // Flugente: enemy airdrop CONCEALINSERTION_CODE, // Flugente: a spy is no longer concealed, this causes us to drop into combat + + BLOODCAT_ATTACK_CODE, // Flugente: like CREATURE_ATTACK_CODE, but with cats + ZOMBIE_ATTACK_CODE, // Flugente: like CREATURE_ATTACK_CODE, but with zombies + BANDIT_ATTACK_CODE, // Flugente: like CREATURE_ATTACK_CODE, but with bandits }; extern BOOLEAN gfAutoAmbush; diff --git a/Strategic/Queen Command.cpp b/Strategic/Queen Command.cpp index 02e7c13a..2f1b0849 100644 --- a/Strategic/Queen Command.cpp +++ b/Strategic/Queen Command.cpp @@ -738,7 +738,8 @@ BOOLEAN PrepareEnemyForSectorBattle() { switch( curr->pBasicPlacement->ubSoldierClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: ubTotalAdmins++; break; + case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: ubTotalAdmins++; break; case SOLDIER_CLASS_ARMY: ubTotalTroops++; break; case SOLDIER_CLASS_ELITE: ubTotalElites++; break; case SOLDIER_CLASS_TANK: ubTotalTanks++; break; @@ -1459,14 +1460,15 @@ void ProcessQueenCmdImplicationsOfDeath( SOLDIERTYPE *pSoldier ) } break; case SOLDIER_CLASS_CREATURE: + case SOLDIER_CLASS_BANDIT: if( pSoldier->ubBodyType != BLOODCAT ) { #ifdef JA2BETAVERSION if( guiCurrentScreen == GAME_SCREEN ) { if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumCreatures != pSector->ubCreaturesInBattle || - !pSector->ubNumCreatures || !pSector->ubCreaturesInBattle || - pSector->ubNumCreatures > 50 || pSector->ubCreaturesInBattle > 50 ) + !pSector->ubNumCreatures || !pSector->ubCreaturesInBattle || + pSector->ubNumCreatures > 50 || pSector->ubCreaturesInBattle > 50 ) { DoScreenIndependantMessageBox( L"Sector creature counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } @@ -1563,6 +1565,7 @@ void ProcessQueenCmdImplicationsOfDeath( SOLDIERTYPE *pSoldier ) } break; case SOLDIER_CLASS_CREATURE: + case SOLDIER_CLASS_BANDIT: if (pSoldier->ubBodyType == BLOODCAT) { if( pSector->ubNumBloodcats > 0 ) @@ -1570,14 +1573,16 @@ void ProcessQueenCmdImplicationsOfDeath( SOLDIERTYPE *pSoldier ) } else { - #ifdef JA2BETAVERSION +#ifdef JA2BETAVERSION if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumCreatures != pSector->ubCreaturesInBattle || - !pSector->ubNumCreatures || !pSector->ubCreaturesInBattle || - pSector->ubNumCreatures > gGameExternalOptions.ubGameMaximumNumberOfCreatures || pSector->ubCreaturesInBattle > gGameExternalOptions.ubGameMaximumNumberOfCreatures ) + !pSector->ubNumCreatures || + !pSector->ubCreaturesInBattle || + pSector->ubNumCreatures > gGameExternalOptions.ubGameMaximumNumberOfCreatures || + pSector->ubCreaturesInBattle > gGameExternalOptions.ubGameMaximumNumberOfCreatures ) { DoScreenIndependantMessageBox( L"Underground sector creature counters are bad. What were the last 2-3 things to die, and how? Save game and send to KM with info!!!", MSG_BOX_FLAG_OK, NULL ); } - #endif +#endif if( pSector->ubNumCreatures ) { pSector->ubNumCreatures--; @@ -1587,23 +1592,27 @@ void ProcessQueenCmdImplicationsOfDeath( SOLDIERTYPE *pSoldier ) pSector->ubCreaturesInBattle--; } - if( !pSector->ubNumCreatures && gWorldSectorX != 9 && gWorldSectorY != 10 ) - { //If the player has successfully killed all creatures in ANY underground sector except J9 - //then cancel any pending creature town attack. - DeleteAllStrategicEventsOfType( EVENT_CREATURE_ATTACK ); - } - - // a monster has died. Post an event to immediately check whether a mine has been cleared. - AddStrategicEventUsingSeconds( EVENT_CHECK_IF_MINE_CLEARED, GetWorldTotalSeconds() + 15, 0); - - if( pSoldier->ubBodyType == QUEENMONSTER ) + if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_CREATURE ) { - //Need to call this, as the queen is really big, and killing her leaves a bunch - //of bad tiles in behind her. Calling this function cleans it up. - InvalidateWorldRedundency(); - //Now that the queen is dead, turn off the creature quest. - EndCreatureQuest(); - EndQuest( QUEST_CREATURES, gWorldSectorX, gWorldSectorY ); + if ( !pSector->ubNumCreatures && gWorldSectorX != 9 && gWorldSectorY != 10 ) + { + //If the player has successfully killed all creatures in ANY underground sector except J9 + //then cancel any pending creature town attack. + DeleteAllStrategicEventsOfType( EVENT_CREATURE_ATTACK ); + } + + // a monster has died. Post an event to immediately check whether a mine has been cleared. + AddStrategicEventUsingSeconds( EVENT_CHECK_IF_MINE_CLEARED, GetWorldTotalSeconds() + 15, 0 ); + + if ( pSoldier->ubBodyType == QUEENMONSTER ) + { + //Need to call this, as the queen is really big, and killing her leaves a bunch + //of bad tiles in behind her. Calling this function cleans it up. + InvalidateWorldRedundency(); + //Now that the queen is dead, turn off the creature quest. + EndCreatureQuest(); + EndQuest( QUEST_CREATURES, gWorldSectorX, gWorldSectorY ); + } } } break; diff --git a/Strategic/Strategic Event Handler.cpp b/Strategic/Strategic Event Handler.cpp index b5c24db0..d479ecd8 100644 --- a/Strategic/Strategic Event Handler.cpp +++ b/Strategic/Strategic Event Handler.cpp @@ -27,6 +27,8 @@ #include "strategic.h" #include "GameSettings.h" #include "history.h" + #include "Town Militia.h" // added by Flugente + #include "Campaign.h" // added by Flugente #endif #include "Luaglobal.h" @@ -52,6 +54,8 @@ UINT8 gubCambriaMedicalObjects; extern INT8 NumMercsNear( UINT8 ubProfileID, UINT8 ubMaxDist ); +extern SECTOR_EXT_DATA SectorExternalData[256][4]; + // WANNE: No more used for the new airport code //void DropOffItemsInMeduna( UINT8 ubOrderNum ); @@ -1097,6 +1101,87 @@ void HandleEarlyMorningEvents( void ) #endif } +// Flugente: creature raids +void HandleRaidEventPlanning() +{ + INT32 personnelgain[3]; + + // the raid order is bloodcats/zombies/bandits + for ( int raidtype = 0; raidtype < 3; ++raidtype ) + { + personnelgain[raidtype] = 0; + + if ( raidtype == 0 ) + { + if ( !gGameExternalOptions.gRaid_Bloodcats ) + continue; + + // if the lair has been cleared, way less growth + if ( CheckFact(FACT_BLOODCAT_LAIR_CLEANED, NO_PROFILE) ) + personnelgain[raidtype] += gGameExternalOptions.gRaidReplenish_BaseValue; + else + personnelgain[raidtype] += 3 * gGameExternalOptions.gRaidReplenish_BaseValue; + } + else if ( raidtype == 1 ) + { + if ( !gGameExternalOptions.gRaid_Zombies || !gGameSettings.fOptions[TOPTION_ZOMBIES] ) + continue; + + // zombies grow very fast + personnelgain[raidtype] += 15 * gGameExternalOptions.gRaidReplenish_BaseValue; + } + else if ( raidtype == 2 ) + { + if ( !gGameExternalOptions.gRaid_Bandits ) + continue; + + // bandit growth depends on progress + personnelgain[raidtype] += gGameExternalOptions.gRaidReplenish_BaseValue * (150 + HighestPlayerProgressPercentage()) / 100; + } + + std::vector attacksectors; + + for ( INT16 sector = 0; sector <= 255; ++sector ) + { + if ( !StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX( sector )].fEnemyControlled ) + { + if ( ( SectorExternalData[sector][0].usSectorFlagMask & (SECTORFLAG_RAIDPOSSIBLE_BLOODCAT << raidtype ) ) ) + { + attacksectors.push_back( sector ); + } + } + } + + if ( attacksectors.empty() ) + continue; + + int numattacks = gGameExternalOptions.gRaid_MaxAttackPerNight_Bandits; + if ( raidtype == 0 ) + numattacks = gGameExternalOptions.gRaid_MaxAttackPerNight_Bloodcats; + else if ( raidtype == 1 ) + numattacks = gGameExternalOptions.gRaid_MaxAttackPerNight_Zombies; + + for ( int attack = 0; attack < numattacks; ++attack ) + { + UINT32 sector = attacksectors[Random(attacksectors.size())]; + + // attack random sector + if ( raidtype == 0 ) + // bloodcats only attack at night (it is 7:00 now) + //AddStrategicEvent( EVENT_BLOODCAT_ATTACK, GetWorldTotalMin() + 14 * 60 + Random(10 * 60), sector ); + AddStrategicEvent( EVENT_BLOODCAT_ATTACK, GetWorldTotalMin() + 12 * 60 + 30, sector ); + else + // other attacks can happen all the time + //AddStrategicEvent( EVENT_BLOODCAT_ATTACK + raidtype, GetWorldTotalMin() + Random( 24 * 60 ), sector ); + AddStrategicEvent( EVENT_BLOODCAT_ATTACK + raidtype, GetWorldTotalMin() + 13 * 60 + 15, sector ); + } + } + + // every day we refill our available personnel + // this also means that at day 1 we have no personnel for raids, forces have to slowly be built up + AddRaidPersonnel( personnelgain[0], personnelgain[1], personnelgain[2] ); +} + void MakeCivGroupHostileOnNextSectorEntrance( UINT8 ubCivGroup ) { // if it's the rebels that will become hostile, reduce town loyalties NOW, not later diff --git a/Strategic/Strategic Event Handler.h b/Strategic/Strategic Event Handler.h index caaaa9bd..1f30da0f 100644 --- a/Strategic/Strategic Event Handler.h +++ b/Strategic/Strategic Event Handler.h @@ -21,6 +21,7 @@ void AddSecondAirportAttendant( void ); void SetPabloToUnbribed( void ); void HandleNPCSystemEvent( UINT32 uiEvent ); void HandleEarlyMorningEvents( void ); +void HandleRaidEventPlanning(); void MakeCivGroupHostileOnNextSectorEntrance( UINT8 ubCivGroup ); diff --git a/Strategic/Strategic Status.cpp b/Strategic/Strategic Status.cpp index 0b75d11f..f60186ec 100644 --- a/Strategic/Strategic Status.cpp +++ b/Strategic/Strategic Status.cpp @@ -396,7 +396,6 @@ void HandleEnricoEmail(void) gStrategicStatus.ubNumNewSectorsVisitedToday = __min( gStrategicStatus.ubNumNewSectorsVisitedToday, NEW_SECTORS_EQUAL_TO_ACTIVITY ) / 3; } - void TrackEnemiesKilled( UINT8 ubKilledHow, UINT8 ubSoldierClass ) { INT8 bRankIndex; @@ -418,7 +417,6 @@ void TrackEnemiesKilled( UINT8 ubKilledHow, UINT8 ubSoldierClass ) } } - INT8 SoldierClassToRankIndex( UINT8 ubSoldierClass ) { INT8 bRankIndex = -1; @@ -426,7 +424,8 @@ INT8 SoldierClassToRankIndex( UINT8 ubSoldierClass ) // the soldier class defines are not in natural ascending order, elite comes before army! switch ( ubSoldierClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: bRankIndex = 0; break; + case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: bRankIndex = 0; break; case SOLDIER_CLASS_ELITE: bRankIndex = 2; break; case SOLDIER_CLASS_ARMY: bRankIndex = 1; break; @@ -438,8 +437,6 @@ INT8 SoldierClassToRankIndex( UINT8 ubSoldierClass ) return( bRankIndex ); } - - UINT8 RankIndexToSoldierClass( UINT8 ubRankIndex ) { UINT8 ubSoldierClass = 0; diff --git a/Strategic/Strategic Town Loyalty.cpp b/Strategic/Strategic Town Loyalty.cpp index f56b0dd7..f113750f 100644 --- a/Strategic/Strategic Town Loyalty.cpp +++ b/Strategic/Strategic Town Loyalty.cpp @@ -1592,14 +1592,13 @@ INT32 IsTownUnderCompleteControlByEnemy( INT8 bTownId ) return( FALSE ); } -void AdjustLoyaltyForCivsEatenByMonsters( INT16 sSectorX, INT16 sSectorY, UINT8 ubHowMany) +void AdjustLoyaltyForCivsEatenByMonsters( INT16 sSectorX, INT16 sSectorY, UINT8 ubHowMany, BOOLEAN aBandits) { INT8 bTownId = 0; UINT32 uiLoyaltyChange = 0; CHAR16 str[256]; CHAR16 pSectorString[128]; - - + // get town id bTownId = GetTownIdForSector( sSectorX, sSectorY ); @@ -1611,12 +1610,19 @@ void AdjustLoyaltyForCivsEatenByMonsters( INT16 sSectorX, INT16 sSectorY, UINT8 //Report this to player GetSectorIDString( sSectorX, sSectorY, 0, pSectorString, TRUE ); + + if ( aBandits ) + swprintf( str, gpStrategicString[STR_PB_BANDIT_KILLCIVS_IN_SECTOR], ubHowMany, pSectorString ); + else + { #ifdef CHINESE - //diffrent order of words in Chinese - swprintf( str, gpStrategicString[ STR_DIALOG_CREATURES_KILL_CIVILIANS ], pSectorString, ubHowMany); + //diffrent order of words in Chinese + swprintf( str, gpStrategicString[STR_DIALOG_CREATURES_KILL_CIVILIANS], pSectorString, ubHowMany ); #else - swprintf( str, gpStrategicString[ STR_DIALOG_CREATURES_KILL_CIVILIANS ], ubHowMany, pSectorString ); + swprintf( str, gpStrategicString[STR_DIALOG_CREATURES_KILL_CIVILIANS], ubHowMany, pSectorString ); #endif + } + DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback ); // use same formula as if it were a civilian "murder" in tactical!!! diff --git a/Strategic/Strategic Town Loyalty.h b/Strategic/Strategic Town Loyalty.h index 4564c310..1f339c97 100644 --- a/Strategic/Strategic Town Loyalty.h +++ b/Strategic/Strategic Town Loyalty.h @@ -175,7 +175,7 @@ INT32 GetNumberOfWholeTownsUnderControl( void ); INT32 IsTownUnderCompleteControlByPlayer( INT8 bTownId ); // used when monsters attack a town sector without going through tactical and they win -void AdjustLoyaltyForCivsEatenByMonsters( INT16 sSectorX, INT16 sSectorY, UINT8 ubHowMany); +void AdjustLoyaltyForCivsEatenByMonsters( INT16 sSectorX, INT16 sSectorY, UINT8 ubHowMany, BOOLEAN aBandits ); // these are used to handle global loyalty events (ones that effect EVERY town on the map) void IncrementTownLoyaltyEverywhere( UINT32 uiLoyaltyIncrease ); diff --git a/Strategic/Town Militia.cpp b/Strategic/Town Militia.cpp index 59f769c0..c829fae4 100644 --- a/Strategic/Town Militia.cpp +++ b/Strategic/Town Militia.cpp @@ -2347,3 +2347,18 @@ FLOAT GetIntel() { return LaptopSaveInfo.dIntelPool; } + +// Flugente: raid 'personnel' +void AddRaidPersonnel( INT32 aBloodcats, INT32 aZombie, INT32 aBandits ) +{ + LaptopSaveInfo.sRaidBloodcats = max( 0, LaptopSaveInfo.sRaidBloodcats + aBloodcats ); + LaptopSaveInfo.sRaidZombies = max( 0, LaptopSaveInfo.sRaidZombies + aZombie ); + LaptopSaveInfo.sRaidBandits = max( 0, LaptopSaveInfo.sRaidBandits + aBandits ); +} + +void GetRaidPersonnel( INT32& arBloodcats, INT32& arZombie, INT32& arBandits ) +{ + arBloodcats = LaptopSaveInfo.sRaidBloodcats; + arZombie = LaptopSaveInfo.sRaidZombies; + arBandits = LaptopSaveInfo.sRaidBandits; +} diff --git a/Strategic/Town Militia.h b/Strategic/Town Militia.h index 2a77c1ac..33824893 100644 --- a/Strategic/Town Militia.h +++ b/Strategic/Town Militia.h @@ -131,4 +131,8 @@ void DevalueResources(UINT8 aOldProgress, UINT8 aNewProgress); void AddIntel( FLOAT aValue, BOOLEAN aDoMessage = FALSE ); FLOAT GetIntel(); +// Flugente: raid 'personnel' +void AddRaidPersonnel( INT32 aBloodcats, INT32 aZombie, INT32 aBandits ); +void GetRaidPersonnel( INT32& arBloodcats, INT32& arZombie, INT32& arBandits ); + #endif \ No newline at end of file diff --git a/Strategic/XML_SectorNames.cpp b/Strategic/XML_SectorNames.cpp index 6989fe89..2812d932 100644 --- a/Strategic/XML_SectorNames.cpp +++ b/Strategic/XML_SectorNames.cpp @@ -99,7 +99,10 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char ** strcmp(name, "snowchance" ) == 0 || strcmp(name, "snakechance" ) == 0 || strcmp(name, "numsnakes" ) == 0 || - strcmp(name, "maxworkers" ) == 0 )) + strcmp(name, "maxworkers" ) == 0 || + strcmp(name, "bloodcatraidpossible" ) == 0 || + strcmp(name, "zombieraidpossible" ) == 0 || + strcmp(name, "banditraidpossible" ) == 0 )) { pData->curElement = ELEMENT_PROPERTY; @@ -230,7 +233,12 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) SectorExternalData[ubSectorId][1].maxworkers = 0; SectorExternalData[ubSectorId][2].maxworkers = 0; SectorExternalData[ubSectorId][3].maxworkers = 0; - + + SectorExternalData[ubSectorId][0].usSectorFlagMask = pData->sectordata.usSectorFlagMask; + SectorExternalData[ubSectorId][1].usSectorFlagMask = 0; + SectorExternalData[ubSectorId][2].usSectorFlagMask = 0; + SectorExternalData[ubSectorId][3].usSectorFlagMask = 0; + if ( !prisonroomvector.empty( ) ) { std::vector::iterator itend = prisonroomvector.end( ); @@ -255,6 +263,7 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) pData->sectordata.snakechance = 0; pData->sectordata.numsnakes = 0; pData->sectordata.maxworkers = 0; + pData->sectordata.usSectorFlagMask = 0; } else { @@ -378,6 +387,24 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->sectordata.maxworkers = (UINT8)atoi( pData->szCharData ); } + else if ( strcmp( name, "bloodcatraidpossible" ) == 0 ) + { + pData->curElement = ELEMENT; + if ( (UINT8)atoi( pData->szCharData ) ) + pData->sectordata.usSectorFlagMask |= SECTORFLAG_RAIDPOSSIBLE_BLOODCAT; + } + else if ( strcmp( name, "zombieraidpossible" ) == 0 ) + { + pData->curElement = ELEMENT; + if ( (UINT8)atoi( pData->szCharData ) ) + pData->sectordata.usSectorFlagMask |= SECTORFLAG_RAIDPOSSIBLE_ZOMBIE; + } + else if ( strcmp( name, "banditraidpossible" ) == 0 ) + { + pData->curElement = ELEMENT; + if ( (UINT8)atoi( pData->szCharData ) ) + pData->sectordata.usSectorFlagMask |= SECTORFLAG_RAIDPOSSIBLE_BANDIT; + } pData->maxReadDepth--; } diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index e5127999..e7bcb835 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -15804,9 +15804,9 @@ void HandlePostAutoresolveMessages() //are "virtually" murdered and loyalty hits will be processed. if( gsCiviliansEatenByMonsters >= 1 ) { - AdjustLoyaltyForCivsEatenByMonsters( (UINT8)SECTORX( gsEnemyGainedControlOfSectorID ), - (UINT8)SECTORY( gsEnemyGainedControlOfSectorID ), - (UINT8)gsCiviliansEatenByMonsters ); + AdjustLoyaltyForCivsEatenByMonsters( (UINT8)SECTORX( gsEnemyGainedControlOfSectorID ), (UINT8)SECTORY( gsEnemyGainedControlOfSectorID ), + (UINT8)gsCiviliansEatenByMonsters, ( guCreatureAttackType == CREATURE_ATTACK_TYPE_BANDIT ) ); + gsCiviliansEatenByMonsters = -2; } else if( gsCiviliansEatenByMonsters == -2 ) diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index acac4eed..fe8cb3dc 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -148,11 +148,11 @@ class SOLDIERTYPE; //Used by PickGridNoToWalkIn #define MAX_ATTEMPTS 200 -#define QUEST_CHECK_EVENT_TIME ( 8 * 60 ) -#define BOBBYRAY_UPDATE_TIME ( 9 * 60 ) -#define INSURANCE_UPDATE_TIME 0 -#define EARLY_MORNING_TIME ( 4 * 60 ) -#define ENRICO_MAIL_TIME ( 7 * 60 ) +#define QUEST_CHECK_EVENT_TIME ( 8 * 60 ) +#define BOBBYRAY_UPDATE_TIME ( 9 * 60 ) +#define INSURANCE_UPDATE_TIME 0 +#define EARLY_MORNING_TIME ( 4 * 60 ) +#define ENRICO_MAIL_TIME ( 7 * 60 ) enum { @@ -2242,6 +2242,7 @@ BOOLEAN SetCurrentWorldSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) PrepareCreaturesForBattle( ); } } + if ( gfGotoSectorTransition ) { BeginLoadScreen( ); @@ -5114,6 +5115,8 @@ void SetupNewStrategicGame( ) AddEveryDayStrategicEvent( EVENT_DAILY_UPDATE_BOBBY_RAY_INVENTORY, BOBBYRAY_UPDATE_TIME, 0 ); //Daily Update of the M.E.R.C. site. AddEveryDayStrategicEvent( EVENT_DAILY_UPDATE_OF_MERC_SITE, 0, 0 ); + // Flugente: plan when to conduct raids on player sectors + AddEveryDayStrategicEvent( EVENT_DAILY_RAID_EVENTS, ENRICO_MAIL_TIME, 0 ); #ifdef JA2UB //Ja25: No insurance for mercs @@ -6528,7 +6531,10 @@ BOOLEAN HandlePotentialBringUpAutoresolveToFinishBattle( int pSectorX, int pSect gubPBSectorZ = (UINT8)pSectorZ; gfBlitBattleSectorLocator = TRUE; gfTransferTacticalOppositionToAutoResolve = TRUE; - if ( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE ) + if ( GetEnemyEncounterCode() != CREATURE_ATTACK_CODE && + GetEnemyEncounterCode() != BLOODCAT_ATTACK_CODE && + GetEnemyEncounterCode() != ZOMBIE_ATTACK_CODE && + GetEnemyEncounterCode() != BANDIT_ATTACK_CODE ) { SetEnemyEncounterCode( ENEMY_INVASION_CODE ); //has to be, if militia are here. } diff --git a/Tactical/Civ Quotes.cpp b/Tactical/Civ Quotes.cpp index b6e96199..c3e1272b 100644 --- a/Tactical/Civ Quotes.cpp +++ b/Tactical/Civ Quotes.cpp @@ -316,6 +316,11 @@ INT8 GetCivType( SOLDIERTYPE *pCiv ) return( CIV_TYPE_ENEMY ); } + if ( pCiv->bTeam == CREATURE_TEAM && pCiv->ubSoldierClass == SOLDIER_CLASS_BANDIT ) + { + return( CIV_TYPE_ENEMY ); + } + if ( pCiv->bTeam != CIV_TEAM && pCiv->bTeam != MILITIA_TEAM ) { return( CIV_TYPE_NA ); @@ -1627,6 +1632,7 @@ void StartEnemyTaunt( SOLDIERTYPE *pCiv, TAUNTTYPE iTauntType, SOLDIERTYPE *pTar switch( pCiv->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_ADMIN) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_ADMIN] != -1 ) && !(zTaunt[ i ].value[TAUNT_PROFILE_ADMIN] == pCiv->usSoldierProfile ) ) diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index f4a50c67..443c9a6e 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -333,10 +333,11 @@ void GenerateRandomEquipment( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass, INT8 // handled differently than for enemies. case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_GREEN_MILITIA: + case SOLDIER_CLASS_BANDIT: bRating = BAD_ADMINISTRATOR_EQUIPMENT_RATING + bEquipmentModifier; // Equipment quality modifier - if ( bSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) + if ( bSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || bSoldierClass == SOLDIER_CLASS_BANDIT ) { if ( gGameExternalOptions.sEnemyAdminEquipmentQualityModifier != 0 ) bRating += gGameExternalOptions.sEnemyAdminEquipmentQualityModifier; @@ -360,7 +361,7 @@ void GenerateRandomEquipment( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass, INT8 bHelmetClass = bRating; // no leggings - if ( bSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) + if ( bSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || bSoldierClass == SOLDIER_CLASS_BANDIT ) { if ( Chance( 30 ) ) bKnifeClass = bRating; @@ -919,6 +920,7 @@ void ChooseWeaponForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bWeaponC case SOLDIER_CLASS_ARMY: case SOLDIER_CLASS_GREEN_MILITIA: case SOLDIER_CLASS_REG_MILITIA: + case SOLDIER_CLASS_BANDIT: //Admins/Troops: 60-75% + 1% every 4% progress bStatus = (INT8)(60 + Random( 16 )); bStatus += (INT8)(HighestPlayerProgressPercentage() / 4); @@ -1987,13 +1989,12 @@ void ChooseFaceGearForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp ) case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_GREEN_MILITIA: case SOLDIER_CLASS_ZOMBIE: + case SOLDIER_CLASS_BANDIT: break; } } - - void ChooseKitsForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bKitClass ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ChooseKitsForSoldierCreateStruct"); diff --git a/Tactical/Items.h b/Tactical/Items.h index 78498912..fd7c0fba 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -494,6 +494,10 @@ OBJECTTYPE* GetExternalFeedingObject(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject BOOLEAN DeductBulletViaExternalFeeding(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject); +#define SECTORFLAG_RAIDPOSSIBLE_BLOODCAT 0x00000001 // bloodcats can attack this sector +#define SECTORFLAG_RAIDPOSSIBLE_ZOMBIE 0x00000002 // zombies can attack this sector +#define SECTORFLAG_RAIDPOSSIBLE_BANDIT 0x00000004 // bandits can attack this sector + // Flugente: additional xml data for sectors typedef struct { @@ -515,6 +519,8 @@ typedef struct UINT8 maxworkers; // maximum number of workers in this sector; + UINT32 usSectorFlagMask; // various flags + } SECTOR_EXT_DATA; // get dirt increase for object with attachments, fConsiderAmmo: with ammo diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index d7a823f5..979e723f 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -6856,7 +6856,8 @@ void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) { switch ( pTeamSoldier->ubSoldierClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: ++sNumPrisoner[PRISONER_ADMIN]; break; + case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: ++sNumPrisoner[PRISONER_ADMIN]; break; case SOLDIER_CLASS_ARMY: ++sNumPrisoner[PRISONER_REGULAR]; break; case SOLDIER_CLASS_ELITE: ++sNumPrisoner[PRISONER_ELITE]; break; default: @@ -6865,7 +6866,11 @@ void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) break; } } - else //if ( pTeamSoldier->bTeam == CIV_TEAM ) + else if ( pTeamSoldier->bTeam == CREATURE_TEAM && pTeamSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) + { + ++sNumPrisoner[PRISONER_ADMIN]; + } + else { ++sNumPrisoner[PRISONER_CIVILIAN]; } @@ -10508,9 +10513,9 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) // We justify this storywise by these soldiers being very determined leaders who don't allow surrender categorically. BOOLEAN fNoSurrender = FALSE; - // enemy team + // enemy team and creature team (bandits can be captured) firstid = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; - lastid = gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; + lastid = gTacticalStatus.Team[CREATURE_TEAM].bLastID; for ( uiCnt = firstid, pSoldier = MercPtrs[ uiCnt ]; uiCnt <= lastid; ++uiCnt, ++pSoldier) { if( pSoldier->bActive && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) ) @@ -10558,7 +10563,7 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) { // it is enough to simply set all soldiers to captured firstid = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; - lastid = gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; + lastid = gTacticalStatus.Team[ CIV_TEAM ].bLastID; for ( uiCnt = firstid, pSoldier = MercPtrs[ uiCnt ]; uiCnt <= lastid; ++uiCnt, ++pSoldier) { if( pSoldier->bActive && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) ) @@ -10577,28 +10582,7 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) } } } - - firstid = gTacticalStatus.Team[CIV_TEAM].bFirstID; - lastid = gTacticalStatus.Team[CIV_TEAM].bLastID; - for ( uiCnt = firstid, pSoldier = MercPtrs[uiCnt]; uiCnt <= lastid; ++uiCnt, ++pSoldier ) - { - if ( pSoldier->bActive && (pSoldier->sSectorX == gWorldSectorX) && (pSoldier->sSectorY == gWorldSectorY) && (pSoldier->bSectorZ == gbWorldSectorZ) ) - { - // can this guy be captured? - if ( !pSoldier->CanBeCaptured() ) - continue; - - // only if not dying - if ( pSoldier->stats.bLife >= OKLIFE ) - { - pSoldier->usSoldierFlagMask |= SOLDIER_POW; - - // Remove as target - RemoveManAsTarget( pSoldier ); - } - } - } - + // dynamic opinion: a merc caused the remaining enemies to give up if ( gusSelectedSoldier != NOBODY ) HandleDynamicOpinionChange( MercPtrs[gusSelectedSoldier], OPINIONEVENT_BATTLE_TOOK_PRISONER, TRUE, TRUE ); @@ -10626,6 +10610,14 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) // we offered to surrender OURSELVES TO the enemy else if ( ubExitValue == 2 ) { + // we cannot surrender to bandits, talk instead + if ( MercPtrs[prisonerdialoguetargetID]->bTeam != CREATURE_TEAM ) + { + // normal dialog + StartCivQuote( MercPtrs[prisonerdialoguetargetID] ); + return; + } + if ( !gGameExternalOptions.fPlayerCanAsktoSurrender ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szPrisonerTextStr[ SRT_PRISONER_INI_SETTING_OFF ] ); @@ -10763,14 +10755,16 @@ void HandleSurrenderOffer( SOLDIERTYPE* pSoldier ) // open a dialogue box and see wether we really want to offer this, or just talk wcscpy( gzUserDefinedButton[0], TacticalStr[ PRISONER_DEMAND_SURRENDER_STR ] ); - wcscpy( gzUserDefinedButton[1], TacticalStr[ PRISONER_OFFER_SURRENDER_STR ] ); - SOLDIERTYPE* pTargetSoldier = NULL; - if ( GetSoldier( &pTargetSoldier, prisonerdialoguetargetID ) && - pTargetSoldier->bTeam == ENEMY_TEAM && - pTargetSoldier->ubProfile == NO_PROFILE && - pTargetSoldier->aiData.bAlertStatus < STATUS_RED && - !pTargetSoldier->RecognizeAsCombatant( gusSelectedSoldier ) ) + if ( pSoldier->bTeam != CREATURE_TEAM ) + wcscpy( gzUserDefinedButton[1], TacticalStr[ PRISONER_OFFER_SURRENDER_STR ] ); + else + wcscpy( gzUserDefinedButton[1], TacticalStr[PRISONER_TALK_STR] ); + + if ( pSoldier->bTeam == ENEMY_TEAM && + pSoldier->ubProfile == NO_PROFILE && + pSoldier->aiData.bAlertStatus < STATUS_RED && + !pSoldier->RecognizeAsCombatant( gusSelectedSoldier ) ) { wcscpy( gzUserDefinedButton[2], TacticalStr[PRISONER_DISTRACT_STR] ); } diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 42f6ddf3..8cdfbf50 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -47,6 +47,8 @@ #include "Campaign Types.h" #include "text.h" // added by Flugente #include "Vehicles.h" // added by silversurfer + #include "ai.h" // added by Flugente + #include "PreBattle Interface.h" // added by Flugente #endif #include "Animation Control.h" @@ -2632,7 +2634,7 @@ void RaiseZombies( void ) for ( INT32 cnt = giNumRottingCorpse - 1; cnt >= 0; --cnt ) { - if ( pSector->ubNumCreatures < gGameExternalOptions.ubGameMaximumNumberOfCreatures ) // ... if there is still room for more zombies (zombies count as creatures until a separate ZOMBIE_TEAM is implemented)... + if ( pSector && pSector->ubNumCreatures < gGameExternalOptions.ubGameMaximumNumberOfCreatures ) // ... if there is still room for more zombies (zombies count as creatures until a separate ZOMBIE_TEAM is implemented)... { pCorpse = &(gRottingCorpse[ cnt ] ); @@ -2652,8 +2654,8 @@ void RaiseZombies( void ) zombieshaverisen = TRUE; CreateZombiefromCorpse( pCorpse, recanimstate ); - //++pSector->ubNumZombies; - //++pSector->ubZombiesInBattle; + pSector->ubNumCreatures++; + pSector->ubCreaturesInBattle++; RemoveCorpse( cnt ); } @@ -2680,13 +2682,32 @@ void RaiseZombies( void ) PlayJA2SampleFromFile( "Sounds\\zombie1.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); UseCreatureMusic(TRUE); // Madd: music when zombies rise - #ifdef NEWMUSIC +#ifdef NEWMUSIC GlobalSoundID = MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ]; if ( MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] != -1 ) SetMusicModeID( MUSIC_TACTICAL_ENEMYPRESENT, MusicSoundValues[ SECTOR( gWorldSectorX, gWorldSectorY ) ].SoundTacticalTensor[gbWorldSectorZ] ); else - #endif +#endif SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT ); + + // alert the creatures + HandleInitialRedAlert( CREATURE_TEAM, FALSE ); + + // we need this so that the battle is properly recognized in strategic + { + //Set up the location as well as turning on the blit flag. + gubPBSectorX = (UINT8)gWorldSectorX; + gubPBSectorY = (UINT8)gWorldSectorY; + gubPBSectorZ = (UINT8)gbWorldSectorZ; + gfBlitBattleSectorLocator = TRUE; + } + + if ( GetEnemyEncounterCode() == NO_ENCOUNTER_CODE ) + SetEnemyEncounterCode( ZOMBIE_ATTACK_CODE ); + + // idea... potentially scrap this part and use a simplified form of void CreatureAttackTown_OtherCreatures(...) ? + + //gubSectorIDOfCreatureAttack = SECTOR( gWorldSectorX, gWorldSectorY ); } } } diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 1aa8694f..1b85b748 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -64,6 +64,8 @@ #include "CampaignStats.h" // added by Flugente #include "DynamicDialogue.h" // added by Flugente #include "MilitiaIndividual.h" // added by Flugente +#include "Town Militia.h" // added by Flugente +#include "PreBattle Interface.h" // added by Flugente #endif // anv: for enemy taunts @@ -3945,6 +3947,9 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) case SOLDIER_CLASS_ZOMBIE : gMercProfiles[ MercPtrs[ ubAttacker ]->ubProfile ].records.usKillsZombies++; break; + case SOLDIER_CLASS_BANDIT: + gMercProfiles[MercPtrs[ubAttacker]->ubProfile].records.usKillsOthers++; + break; default : if ( CREATURE_OR_BLOODCAT( pSoldier ) ) gMercProfiles[ MercPtrs[ ubAttacker ]->ubProfile ].records.usKillsCreatures++; @@ -4049,6 +4054,14 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) // Flugente: campaign stats gCurrentIncident.AddStat( pSoldier, CAMPAIGNHISTORY_TYPE_KILL ); + // Flugente: for raids, we need to keep track of killed forces + if ( GetEnemyEncounterCode() == BLOODCAT_ATTACK_CODE || + GetEnemyEncounterCode() == ZOMBIE_ATTACK_CODE || + GetEnemyEncounterCode() == BANDIT_ATTACK_CODE ) + { + AddRaidPersonnel( -( pSoldier->ubBodyType == BLOODCAT ), -( pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE ), -( pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) ); + } + // Flugente: individual militia MILITIA militia; if ( GetMilitia( pSoldier->usIndividualMilitiaID, &militia ) && !(militia.flagmask & MILITIAFLAG_DEAD) ) diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 9f142672..ad95de26 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -14723,7 +14723,7 @@ INT32 SOLDIERTYPE::GetDamageResistance( BOOLEAN fAutoResolve, BOOLEAN fCalcBreat else if ( this->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.bVeteranMilitiaDamageResistance != 0 ) resistance += (INT32)(gGameExternalOptions.bVeteranMilitiaDamageResistance / breathmodifiermilitia); // bonus for enemy too - else if ( this->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminDamageResistance != 0 ) + else if ( (this->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || this->ubSoldierClass == SOLDIER_CLASS_BANDIT ) && gGameExternalOptions.sEnemyAdminDamageResistance != 0 ) resistance += gGameExternalOptions.sEnemyAdminDamageResistance; else if ( this->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularDamageResistance != 0 ) resistance += gGameExternalOptions.sEnemyRegularDamageResistance; @@ -16211,7 +16211,7 @@ UINT32 SOLDIERTYPE::GetSurrenderStrength( ) // adjust for type of soldier if ( this->ubSoldierClass == SOLDIER_CLASS_ELITE || this->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) value *= 1.5f; - else if ( this->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || this->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( this->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || this->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA || this->ubSoldierClass == SOLDIER_CLASS_BANDIT ) value *= 0.75f; // tanks won't surrender that easy @@ -16267,6 +16267,10 @@ BOOLEAN SOLDIERTYPE::CanBeCaptured( ) if ( this->bTeam == ENEMY_TEAM ) return TRUE; + // bandits can be captured + if ( this->bTeam == CREATURE_TEAM && this->ubSoldierClass == SOLDIER_CLASS_BANDIT ) + return TRUE; + // civilians can be captured if their faction can, and if they are hostile if ( this->bTeam == CIV_TEAM && zCivGroupName[this->ubCivilianGroup].fCanBeCaptured ) { diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 9ea3610d..14a49b74 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -331,6 +331,7 @@ enum SOLDIER_CLASS_ZOMBIE, SOLDIER_CLASS_TANK, SOLDIER_CLASS_JEEP, + SOLDIER_CLASS_BANDIT, SOLDIER_CLASS_MAX, }; diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 17c44af7..753d6007 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -1799,7 +1799,7 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT ////////////////////////////////////////////////////////////////////////////////////////////////// // Added by SANDRO - the enemy/militia has a decent chance to have some traits - if ( gGameExternalOptions.fAssignTraitsToEnemy && SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass ) ) + if ( gGameExternalOptions.fAssignTraitsToEnemy && (SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass ) || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT) ) AssignTraitsToSoldier( pSoldier, pCreateStruct ); if ( gGameExternalOptions.fAssignTraitsToMilitia && SOLDIER_CLASS_MILITIA( pSoldier->ubSoldierClass ) ) AssignTraitsToSoldier( pSoldier, pCreateStruct ); @@ -2010,7 +2010,7 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT } break; case CREATURE_TEAM: - if( pSoldier->ubBodyType == BLOODCAT ) + if ( pSoldier->ubBodyType == BLOODCAT ) { swprintf( pSoldier->name, gzLateLocalizedString[ 36 ] ); } @@ -2018,9 +2018,13 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT { swprintf( pSoldier->name, TacticalStr[ ZOMBIE_TEAM_MERC_NAME ] ); } + else if ( pSoldier->ubBodyType >= ADULTFEMALEMONSTER && pSoldier->ubBodyType <= QUEENMONSTER ) + { + swprintf( pSoldier->name, TacticalStr[CREATURE_TEAM_MERC_NAME] ); break; + } else { - swprintf( pSoldier->name, TacticalStr[ CREATURE_TEAM_MERC_NAME ] ); break; + swprintf( pSoldier->name, gpStrategicString[STR_PB_BANDIT] ); break; } break; } @@ -2296,7 +2300,6 @@ INT8 CalcDifficultyModifier( UINT8 ubSoldierClass ) return(bDiffModifier); } - //When the editor modifies the soldier's relative attribute level, //this function is called to update that information. //Used to generate a detailed placement from a basic placement. This assumes that the detailed placement @@ -2397,8 +2400,7 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B pp->bOrders = bp->bOrders; pp->bAttitude = bp->bAttitude; pp->ubDirection = bp->ubDirection; - - + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenBasicPlacementInfo: determine soldier's class")); // determine this soldier's soldier class if( bp->bTeam == CREATURE_TEAM ) @@ -2411,8 +2413,7 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B } ubDiffFactor = CalcDifficultyModifier( ubSoldierClass ); - - + // experience level is modified by game difficulty, player's progress, & proximity to Queen's Palace, etc. // This formula gives the following results: // DIFFICULTY FACTOR EXP. LEVEL MODIFIER LEVEL OF AVG REGULAR TROOP @@ -2457,6 +2458,19 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B break; } + UINT8 DiffLevel; + if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_EASY ) + DiffLevel = 1; + else if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_MEDIUM ) + DiffLevel = 2; + else if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD ) + DiffLevel = 3; + else if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) + DiffLevel = 4; + else + { + DiffLevel = 1 + Random ( 4 ); + } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenBasicPlacementInfo: set level based on soldier class")); //Set the experience level based on the soldier class and exp level modifier or relative attribute level @@ -2464,6 +2478,7 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B { case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_ZOMBIE: + case SOLDIER_CLASS_BANDIT: pp->bExpLevel = (INT8) 2 + bExpLevelModifier; break; case SOLDIER_CLASS_ARMY: @@ -2512,23 +2527,7 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B UINT8 PlacementType = gBloodcatPlacements[ SECTOR(gWorldSectorX, gWorldSectorY) ][0].PlacementType; if( PlacementType == BLOODCAT_PLACEMENT_LAIR ) { - - UINT8 DiffLevel; - if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_EASY ) - DiffLevel = 1; - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_MEDIUM ) - DiffLevel = 2; - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD ) - DiffLevel = 3; - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) - DiffLevel = 4; - else - { - DiffLevel = Random (4); - if (DiffLevel == 0) DiffLevel = 1; - } pp->bExpLevel += DiffLevel; - //pp->bExpLevel += gGameOptions.ubDifficultyLevel; } break; } @@ -2546,8 +2545,8 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenBasicPlacementInfo: set exp. level")); - pp->bExpLevel = max( gGameOptions.ubDifficultyLevel, pp->bExpLevel ); //minimum exp. level of 1 -- madd->= dif level + pp->bExpLevel = max( DiffLevel, pp->bExpLevel ); //minimum exp. level of 1 -- madd->= dif level if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) pp->bExpLevel = max( 6, pp->bExpLevel ); //minimum exp. level of 6 in insane - madd @@ -2555,23 +2554,7 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B ubStatsLevel = pp->bExpLevel + bStatsModifier; - UINT8 DiffLevel; - if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_EASY ) - DiffLevel = 1; - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_MEDIUM ) - DiffLevel = 2; - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD ) - DiffLevel = 3; - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) - DiffLevel = 4; - else - { - DiffLevel = Random (4); - if (DiffLevel == 0) DiffLevel = 1; - } - ubStatsLevel = max( DiffLevel, ubStatsLevel ); //minimum stats level of 0 -- madd->= dif level - //ubStatsLevel = max( gGameOptions.ubDifficultyLevel, ubStatsLevel ); //minimum stats level of 0 -- madd->= dif level - + ubStatsLevel = max( DiffLevel, ubStatsLevel ); //minimum stats level of 0 -- madd->= dif level if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) ubStatsLevel = max( 6, ubStatsLevel ); //minimum stats level of 6 in insane @@ -2618,7 +2601,11 @@ void CreateDetailedPlacementGivenBasicPlacementInfo( SOLDIERCREATE_STRUCT *pp, B //own equipment. DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CreateDetailedPlacementGivenBasicPlacementInfo: generate random equipment")); - if( !bp->fDetailedPlacement && ubSoldierClass != SOLDIER_CLASS_NONE && ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_MINER && ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) + if( !bp->fDetailedPlacement && + ubSoldierClass != SOLDIER_CLASS_NONE && + ubSoldierClass != SOLDIER_CLASS_CREATURE && + ubSoldierClass != SOLDIER_CLASS_MINER && + ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) { GenerateRandomEquipment( pp, ubSoldierClass, bp->bRelativeEquipmentLevel); @@ -2800,7 +2787,10 @@ void CreateDetailedPlacementGivenStaticDetailedPlacementAndBasicPlacementInfo( // ReplaceExtendedGuns( pp, bp->ubSoldierClass ); //} - if( bp->ubSoldierClass != SOLDIER_CLASS_NONE && bp->ubSoldierClass != SOLDIER_CLASS_CREATURE && bp->ubSoldierClass != SOLDIER_CLASS_MINER && bp->ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) + if( bp->ubSoldierClass != SOLDIER_CLASS_NONE && + bp->ubSoldierClass != SOLDIER_CLASS_CREATURE && + bp->ubSoldierClass != SOLDIER_CLASS_MINER && + bp->ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) { GenerateRandomEquipment( pp, bp->ubSoldierClass, bp->bRelativeEquipmentLevel); @@ -2841,6 +2831,7 @@ void UpdateSoldierWithStaticDetailedInformation( SOLDIERTYPE *s, SOLDIERCREATE_S case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_ARMY: case SOLDIER_CLASS_ELITE: + case SOLDIER_CLASS_BANDIT: GeneratePaletteForSoldier( s, spp->ubSoldierClass, spp->bTeam ); break; } @@ -3003,7 +2994,7 @@ SOLDIERTYPE* ReserveTacticalSoldierForAutoresolve( UINT8 ubSoldierClass ) //returns the pointer to that soldier. This is used when copying the exact status of //all remaining enemy troops (or creatures) to finish the battle in autoresolve. To //signify that the troop has already been reserved, we simply set their gridno to NOWHERE. - if( ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_ZOMBIE ) + if( ubSoldierClass != SOLDIER_CLASS_CREATURE && ubSoldierClass != SOLDIER_CLASS_ZOMBIE && ubSoldierClass != SOLDIER_CLASS_BANDIT ) { //use the enemy team iStart = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; @@ -3244,12 +3235,16 @@ SOLDIERTYPE* TacticalCreateZombie() memset( &bp, 0, sizeof( BASIC_SOLDIERCREATE_STRUCT ) ); RandomizeRelativeLevel( &( bp.bRelativeAttributeLevel ), SOLDIER_CLASS_ZOMBIE ); RandomizeRelativeLevel( &( bp.bRelativeEquipmentLevel ), SOLDIER_CLASS_ZOMBIE ); - bp.bTeam = CREATURE_TEAM;//ZOMBIE_TEAM; + bp.bTeam = CREATURE_TEAM; bp.bOrders = SEEKENEMY; bp.bAttitude = AGGRESSIVE; - bp.ubBodyType = -1; + bp.ubBodyType = Random( ADULTFEMALEMONSTER ); bp.ubSoldierClass = SOLDIER_CLASS_ZOMBIE; CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp ); + + // need to properly set this here + pp.ubSoldierClass = SOLDIER_CLASS_ZOMBIE; + pSoldier = TacticalCreateSoldier( &pp, &ubID ); if ( pSoldier ) { @@ -3398,10 +3393,13 @@ SOLDIERTYPE* TacticalCreateArmedCivilian( UINT8 usSoldierClass ) pSoldier->aiData.sNoiseGridno = (CENTRAL_GRIDNO + (Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS) + (Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS) * WORLD_COLS); pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION; - // random clothes - SetClothes( pSoldier, Random( NUMSHIRTS ), Random( NUMPANTS ) ); + if ( guiCurrentScreen != AUTORESOLVE_SCREEN ) + { + // random clothes + SetClothes( pSoldier, Random( NUMSHIRTS ), Random( NUMPANTS ) ); - pSoldier->CreateSoldierPalettes( ); + pSoldier->CreateSoldierPalettes(); + } } return(pSoldier); @@ -3481,25 +3479,28 @@ SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 pSoldier->aiData.bOrders = ONGUARD; } - // make him wear administrator uniform - UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); - - if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + if ( guiCurrentScreen != AUTORESOLVE_SCREEN ) { - SetClothes( pSoldier, aVest, aPants, aHair, aSkin ); + // make him wear administrator uniform + UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); - // Use palette from HVOBJECT, then use substitution for pants, etc - memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[usPaletteAnimSurface].hVideoObject->pPaletteEntry, sizeof(pSoldier->p8BPPPalette) * 256 ); + if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + { + SetClothes( pSoldier, aVest, aPants, aHair, aSkin ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal ); + // Use palette from HVOBJECT, then use substitution for pants, etc + memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[usPaletteAnimSurface].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 ); - pSoldier->CreateSoldierPalettes( ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal ); - // Dirty - fInterfacePanelDirty = DIRTYLEVEL2; + pSoldier->CreateSoldierPalettes(); + + // Dirty + fInterfacePanelDirty = DIRTYLEVEL2; + } } } @@ -3572,6 +3573,65 @@ SOLDIERTYPE* TacticalCreateEnemyAssassin(UINT8 disguisetype) return( pSoldier ); } +SOLDIERTYPE* TacticalCreateBandit() +{ + BASIC_SOLDIERCREATE_STRUCT bp; + SOLDIERCREATE_STRUCT pp; + UINT8 ubID; + SOLDIERTYPE * pSoldier; + + if ( guiCurrentScreen == AUTORESOLVE_SCREEN && !gfPersistantPBI ) + { + pSoldier = ReserveTacticalSoldierForAutoresolve( SOLDIER_CLASS_BANDIT ); + if ( pSoldier ) return pSoldier; + } + + memset( &bp, 0, sizeof( BASIC_SOLDIERCREATE_STRUCT ) ); + RandomizeRelativeLevel( &( bp.bRelativeAttributeLevel ), SOLDIER_CLASS_ADMINISTRATOR ); + RandomizeRelativeLevel( &( bp.bRelativeEquipmentLevel ), SOLDIER_CLASS_ADMINISTRATOR ); + bp.bTeam = ENEMY_TEAM; // set wrong team here, otherwise no bodytype/equipment will be set + bp.bOrders = SEEKENEMY; + bp.bAttitude = (INT8)Random( MAXATTITUDES ); + bp.ubBodyType = -1; + bp.ubSoldierClass = SOLDIER_CLASS_ADMINISTRATOR; + CreateDetailedPlacementGivenBasicPlacementInfo( &pp, &bp ); + + // need to properly set this here + pp.bTeam = CREATURE_TEAM; + pp.ubSoldierClass = SOLDIER_CLASS_BANDIT; + + pSoldier = TacticalCreateSoldier( &pp, &ubID ); + if ( pSoldier ) + { + // send soldier to centre of map, roughly + pSoldier->aiData.sNoiseGridno = ( CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS ); + pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION; + + if ( guiCurrentScreen != AUTORESOLVE_SCREEN ) + { + UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); + + if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + { + // random clothes + SetClothes( pSoldier, Random( NUMSHIRTS ), Random( NUMPANTS ) ); + + // Use palette from HVOBJECT, then use substitution for pants, etc + memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[usPaletteAnimSurface].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 ); + + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal ); + + pSoldier->CreateSoldierPalettes(); + } + } + } + + return( pSoldier ); +} + void CreateAssassin(UINT8 disguisetype) { SOLDIERTYPE* pSoldier = TacticalCreateEnemyAssassin( disguisetype ); @@ -3742,26 +3802,29 @@ void CreatePrisonerOfWar() swprintf( pSoldier->name, TacticalStr[ POW_TEAM_MERC_NAME ] ); - // make him wear 'prisoner garb' (just what Shank and Dynamo are wearing) - UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); - - if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + if ( guiCurrentScreen != AUTORESOLVE_SCREEN ) { - SET_PALETTEREP_ID( pSoldier->VestPal, "greyVEST" ); - SET_PALETTEREP_ID( pSoldier->PantsPal, "BLACKPANTS" ); + // make him wear 'prisoner garb' (just what Shank and Dynamo are wearing) + UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); - // Use palette from HVOBJECT, then use substitution for pants, etc - memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[usPaletteAnimSurface].hVideoObject->pPaletteEntry, sizeof(pSoldier->p8BPPPalette) * 256 ); + if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE ) + { + SET_PALETTEREP_ID( pSoldier->VestPal, "greyVEST" ); + SET_PALETTEREP_ID( pSoldier->PantsPal, "BLACKPANTS" ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal ); - SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal ); + // Use palette from HVOBJECT, then use substitution for pants, etc + memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[usPaletteAnimSurface].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 ); - pSoldier->CreateSoldierPalettes( ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal ); + SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal ); - // Dirty - fInterfacePanelDirty = DIRTYLEVEL2; + pSoldier->CreateSoldierPalettes(); + + // Dirty + fInterfacePanelDirty = DIRTYLEVEL2; + } } // So we can see them! @@ -4696,7 +4759,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre // 30-80% iChance = 30 + ubProgress / 2; } - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) { // 15-65% iChance = 15 + ubProgress / 2; @@ -4757,7 +4820,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre // 30-80% iChance = 30 + ubProgress / 2; } - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) { // 15-65% iChance = 15 + ubProgress / 2; @@ -4819,7 +4882,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre // 25-75% iChance = 25 + ubProgress / 2; } - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) { // 10-60% iChance = 10 + ubProgress / 2; @@ -4888,7 +4951,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre // 25-75% iChance = 25 + ubProgress / 2; } - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) { // 10-60% iChance = 10 + ubProgress / 2; @@ -4993,7 +5056,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre // 25-75% iChance = 25 + ubProgress / 2; } - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) { // 10-60% iChance = 10 + ubProgress / 2; @@ -5177,7 +5240,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 45 + ubProgress / 2; // 45-95% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 30 + ubProgress / 2; // 30-80% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = 15 + ubProgress / 2; // 15-65% chance // adust the chance by the type of heavy weapon @@ -5239,7 +5302,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 20 + ubProgress / 4; // 20-45% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 10 + ubProgress / 4; // 10-35% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = ubProgress / 4; // 0-25% chance else if ( ubSolClass == SOLDIER_CLASS_ZOMBIE ) iChance = 100; // 100% chance @@ -5284,7 +5347,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 40 + ubProgress * 2 / 5; // 40-80% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 25 + ubProgress * 2 / 5; // 25-65% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = 10 + ubProgress * 2 / 5; // 10-50% chance else if ( ubSolClass == SOLDIER_CLASS_ZOMBIE ) iChance = 100; // 100% chance @@ -5348,7 +5411,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 30 + ubProgress / 3 + 30; // 60-92% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 15 + ubProgress / 3 + 25; // 40-73% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = ubProgress / 3 + 20; // 20-53% chance // modify the chance by preset ini setting @@ -5411,7 +5474,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 20 + ubProgress / 3; // 20-53% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 10 + ubProgress / 3; // 10-43% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = ubProgress / 3; // 0-33% chance // modify the chance by preset ini setting @@ -5451,7 +5514,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 50 + ubProgress * 2 / 5; // 50-90% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 35 + ubProgress * 2 / 5; // 35-75% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = 15 + ubProgress * 2 / 5; // 15-55% chance // modify the chance by preset ini setting @@ -5514,7 +5577,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 40 + ubProgress * 2 / 5; // 40-80% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 25 + ubProgress * 2 / 5; // 25-65% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = 10 + ubProgress * 2 / 5; // 10-50% chance // modify the chance by preset ini setting @@ -5556,7 +5619,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 10 + ubProgress / 4; // 10-35% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = ubProgress / 5; // 0-20% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = 0; // 0% chance // modify the chance by preset ini setting @@ -5598,7 +5661,7 @@ BOOLEAN AssignTraitsToSoldier( SOLDIERTYPE *pSoldier, SOLDIERCREATE_STRUCT *pCre iChance = 30 + ubProgress / 2; // 30-90% chance else if ( ubSolClass == SOLDIER_CLASS_ARMY || ubSolClass == SOLDIER_CLASS_REG_MILITIA ) iChance = 10 + ubProgress / 2; // 10-60% chance - else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA ) + else if ( ubSolClass == SOLDIER_CLASS_ADMINISTRATOR || ubSolClass == SOLDIER_CLASS_GREEN_MILITIA || ubSolClass == SOLDIER_CLASS_BANDIT ) iChance = ubProgress / 3; // 0-33% chance // modify the chance by preset ini setting diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index 3a569629..369e64f4 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -481,6 +481,9 @@ SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 // Flugente: assassins are elite soldiers of the civ team that go hostile on a certain event, otherwise they just blend in SOLDIERTYPE* TacticalCreateEnemyAssassin(UINT8 disguisetype); + +SOLDIERTYPE* TacticalCreateBandit(); + void CreateAssassin(UINT8 disguisetype); // create a prisoner (in a prison cell) in the current sector diff --git a/Tactical/Soldier Init List.cpp b/Tactical/Soldier Init List.cpp index 17637cc1..97e020b0 100644 --- a/Tactical/Soldier Init List.cpp +++ b/Tactical/Soldier Init List.cpp @@ -59,6 +59,8 @@ #include "PreBattle Interface.h" // added by Flugente #include "LuaInitNPCs.h" // added by Flugente #include "Soldier macros.h" // added by Flugente +#include "Creature Spreading.h" // added by Flugente + BOOLEAN gfOriginalList = TRUE; @@ -1105,6 +1107,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr ubEliteBSlots++; break; case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) ubAdminPDSlots++; else if( curr->pBasicPlacement->fPriorityExistance ) @@ -1163,6 +1166,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr switch( ubCurrClass ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: pCurrSlots = &ubAdminPDSlots; pCurrTotal = &ubTotalAdmin; break; @@ -1229,6 +1233,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr switch( ubCurrClass ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: pCurrSlots = &ubAdminPSlots; pCurrTotal = &ubTotalAdmin; break; @@ -1285,6 +1290,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr switch( ubCurrClass ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: pCurrSlots = &ubAdminDSlots; pCurrTotal = &ubTotalAdmin; break; @@ -1389,12 +1395,13 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr //ADD REMAINING PLACEMENTS WITH BASIC PLACEMENT INFORMATION //we now have the numbers of available slots for each soldier class, so loop through three times //and randomly choose some (or all) of the matching slots to fill. This is done randomly. - for( ubCurrClass = SOLDIER_CLASS_ADMINISTRATOR; ubCurrClass <= SOLDIER_CLASS_ARMY; ubCurrClass++ ) + for( ubCurrClass = SOLDIER_CLASS_ADMINISTRATOR; ubCurrClass <= SOLDIER_CLASS_ARMY; ++ubCurrClass ) { //First, prepare the counters. switch( ubCurrClass ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: pCurrSlots = &ubAdminBSlots; pCurrTotal = &ubTotalAdmin; break; @@ -1672,7 +1679,8 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl switch( curr->pBasicPlacement->ubSoldierClass ) { case SOLDIER_CLASS_ELITE: ubEliteSlots++; break; - case SOLDIER_CLASS_ADMINISTRATOR: ubGreenSlots++; break; + case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: ubGreenSlots++; break; case SOLDIER_CLASS_ARMY: ubRegSlots++; break; } } @@ -1693,6 +1701,7 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl switch( reorder[i] ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: pCurrSlots = &ubGreenSlots; pCurrTotal = &ubNumGreen; break; @@ -1721,6 +1730,7 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl switch( reorder[i] ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_GREEN_MILITIA; break; case SOLDIER_CLASS_ARMY: @@ -2059,7 +2069,125 @@ void AddSoldierInitListCreatures( BOOLEAN fQueen, UINT8 ubNumLarvae, UINT8 ubNum } } +void AddSoldierInitListOtherCreatures( UINT8 usNum ) +{ + SOLDIERINITNODE *curr; + INT32 iRandom; + UINT8 ubFreeSlots; + BOOLEAN fDoPlacement; + UINT8 ubNumCreatures = usNum; + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "AddSoldierInitListBloodcats" ) ); + + SortSoldierInitList(); + + //First fill up only the priority existance slots (as long as the availability and bodytypes match) + curr = gSoldierInitHead; + while ( curr && curr->pBasicPlacement->fPriorityExistance && ubNumCreatures ) + { + fDoPlacement = TRUE; + + if ( curr->pBasicPlacement->bTeam == CREATURE_TEAM ) + { + //Matching team, now check the soldier class... + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_ZOMBIE: + if ( curr->pBasicPlacement->ubBodyType > REGFEMALE ) + fDoPlacement = FALSE; + break; + + case CREATURE_ATTACK_TYPE_BANDIT: + if ( curr->pBasicPlacement->ubBodyType > REGFEMALE ) + fDoPlacement = FALSE; + break; + + case CREATURE_ATTACK_TYPE_BLOODCAT: + if ( curr->pBasicPlacement->ubBodyType != BLOODCAT ) + fDoPlacement = FALSE; + break; + } + break; + + if ( fDoPlacement ) + { + if ( AddPlacementToWorld( curr ) ) + { + --ubNumCreatures; + } + else + return; + } + } + curr = curr->next; + } + + if ( !ubNumCreatures ) + return; + + //Count how many free creature slots are left. + curr = gSoldierInitHead; + ubFreeSlots = 0; + while ( curr ) + { + if ( !curr->pSoldier && curr->pBasicPlacement->bTeam == CREATURE_TEAM ) + ++ubFreeSlots; + + curr = curr->next; + } + + //Now, if we still have creatures to place, do so completely randomly, overriding priority + //placements, etc. + curr = gSoldierInitHead; + while ( curr && ubFreeSlots && ubNumCreatures ) + { + if ( !curr->pSoldier && curr->pBasicPlacement->bTeam == CREATURE_TEAM ) + { + //Randomly determine if we will use this slot; the more available slots in proportion to + //the number of enemies, the lower the chance of accepting the slot. + if ( ubFreeSlots <= ubNumCreatures || Random( ubFreeSlots ) < ubNumCreatures ) + { + switch ( guCreatureAttackType ) + { + case CREATURE_ATTACK_TYPE_ZOMBIE: + curr->pBasicPlacement->ubBodyType = Random( ADULTFEMALEMONSTER ); + break; + + case CREATURE_ATTACK_TYPE_BANDIT: + curr->pBasicPlacement->ubBodyType = Random( ADULTFEMALEMONSTER ); + break; + + case CREATURE_ATTACK_TYPE_BLOODCAT: + curr->pBasicPlacement->ubBodyType = BLOODCAT; + break; + } + break; + + if ( curr->pDetailedPlacement ) + { + //delete the detailed placement information. + MemFree( curr->pDetailedPlacement ); + curr->pDetailedPlacement = NULL; + curr->pBasicPlacement->fDetailedPlacement = FALSE; + } + + if ( AddPlacementToWorld( curr ) ) + { + --ubNumCreatures; + } + else + { + return; + } + } + + --ubFreeSlots; + //With the decrementing of the slot vars in this manner, the chances increase so that all slots + //will be full by the time the end of the list comes up. + } + curr = curr->next; + } +} SOLDIERINITNODE* FindSoldierInitNodeWithProfileID( UINT16 usProfile ); SOLDIERINITNODE* FindSoldierInitNodeWithProfileID( UINT16 usProfile ) diff --git a/Tactical/Soldier Init List.h b/Tactical/Soldier Init List.h index 832a9816..2b774fec 100644 --- a/Tactical/Soldier Init List.h +++ b/Tactical/Soldier Init List.h @@ -44,6 +44,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr void AddSoldierInitListCreatures( BOOLEAN fQueen, UINT8 ubNumLarvae, UINT8 ubNumInfants, UINT8 ubNumYoungMales, UINT8 ubNumYoungFemales, UINT8 ubNumAdultMales, UINT8 ubNumAdultFemales ); +void AddSoldierInitListOtherCreatures( UINT8 usNum ); void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumReg, UINT8 ubNumElites ); void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNumGreen, UINT8 ubNumReg, UINT8 ubNumElites ); diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 07327d63..fe0821f1 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -6477,7 +6477,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sVeteranMilitiaCtHBonusPercent) /100); // bonus for enemy - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) + else if ( (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT) && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sEnemyAdminCtHBonusPercent) /100); else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sEnemyRegularCtHBonusPercent) /100); @@ -9069,6 +9069,8 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO INT32 iImpact, iFluke, iBonus; + BOOLEAN autoresolve = IsAutoResolveActive(); + // Flugente: check for underbarrel weapons and use that object if necessary (think of bayonets) OBJECTTYPE* pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); @@ -9158,9 +9160,10 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO if ( HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT ) ) { iBonus += ( gSkillTraitValues.ubMABonusDamageHandToHand * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT ) ); - } + } + // add bonus for focused punch - if (pSoldier->usAnimState == NINJA_SPINKICK || pSoldier->usAnimState == FOCUSED_PUNCH || pSoldier->usAnimState == FOCUSED_HTH_KICK ) + if ( !autoresolve && (pSoldier->usAnimState == NINJA_SPINKICK || pSoldier->usAnimState == FOCUSED_PUNCH || pSoldier->usAnimState == FOCUSED_HTH_KICK) ) { iBonus += 50; @@ -9180,7 +9183,8 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO iBonus += ( gSkillTraitValues.ubMABonusDamageHandToHand * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT ) ); // The Spinning kicks or aimed punch bonus - SANDRO - if (pSoldier->usAnimState == NINJA_SPINKICK || (pSoldier->aiData.bAimTime >= (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedPunches : 6))) + if ( !autoresolve && + (pSoldier->usAnimState == NINJA_SPINKICK || (pSoldier->aiData.bAimTime >= (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedPunches : 6)) )) { //iImpact = (INT32)((iImpact * (100 + gSkillTraitValues.usMAAimedPunchDamageBonus * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT ) ) / 100) + 0.5); // +75% damage per trait iBonus += ( (gSkillTraitValues.ubMABonusDamageHandToHand + 25 )* NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT ) ); @@ -9196,28 +9200,31 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO iBonus += gSkillTraitValues.ubMEDamageBonusBlunt; // bonus for aimed attack - if ( pSoldier->aiData.bAimTime >= (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedBladedAttackes : 6) ) + if ( !autoresolve && pSoldier->aiData.bAimTime >= (gGameExternalOptions.fEnhancedCloseCombatSystem ? gSkillTraitValues.ubModifierForAPsAddedOnAimedBladedAttackes : 6) ) { - iBonus += gSkillTraitValues.usMEAimedMeleeAttackDamageBonus; // incresed damage if focused melee attack + iBonus += gSkillTraitValues.usMEAimedMeleeAttackDamageBonus; // incresed damage if focused melee attack } } } } else // original code { - if ( gGameExternalOptions.fEnhancedCloseCombatSystem && (pSoldier->usAnimState == NINJA_SPINKICK || pSoldier->usAnimState == FOCUSED_PUNCH || pSoldier->usAnimState == FOCUSED_HTH_KICK )) + if ( !autoresolve && gGameExternalOptions.fEnhancedCloseCombatSystem && (pSoldier->usAnimState == NINJA_SPINKICK || pSoldier->usAnimState == FOCUSED_PUNCH || pSoldier->usAnimState == FOCUSED_HTH_KICK )) { iBonus += 50; } + // add bonuses for hand-to-hand and martial arts if ( HAS_SKILL_TRAIT( pSoldier, MARTIALARTS_OT ) ) { iBonus += gbSkillTraitBonus[MARTIALARTS_OT] * NUM_SKILL_TRAITS( pSoldier, MARTIALARTS_OT ); - if (pSoldier->usAnimState == NINJA_SPINKICK) + + if ( !autoresolve && pSoldier->usAnimState == NINJA_SPINKICK) { iBonus += 100; } } + if ( HAS_SKILL_TRAIT( pSoldier, HANDTOHAND_OT ) ) { // SPECIAL - give TRIPLE bonus for damage for hand-to-hand trait @@ -9227,7 +9234,7 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO } // Enhanced Close Combat System - aiming at body parts makes difference - if (gGameExternalOptions.fEnhancedCloseCombatSystem) + if ( !autoresolve && gGameExternalOptions.fEnhancedCloseCombatSystem) { if ( pSoldier->bAimShotLocation == AIM_SHOT_HEAD ) { @@ -9251,22 +9258,6 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO iBonus += 50; // 50% incresed damage on suprising attacks } } - - // Flugente: power armour reduces damage taken - // SANDRO - huh?! - /*INT8 iSlot = VESTPOS; - switch( pSoldier->bAimShotLocation ) - { - case AIM_SHOT_HEAD: - iSlot = HELMETPOS; - break; - case AIM_SHOT_LEGS: - iSlot = LEGPOS; - break; - default: - iSlot = VESTPOS; - break; - }*/ } // DAMAGE BONUS TO KNIFE ATTACK WITH MELEE SKILL else @@ -9277,42 +9268,27 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO iBonus += gSkillTraitValues.ubMEDamageBonusBlades; // +30% damage // if this attack happens directly after running, the attack is slightly more powerful due to extra force - if (pSoldier->usSoldierFlagMask2 & SOLDIER_BAYONET_RUNBONUS) + if ( !autoresolve && pSoldier->usSoldierFlagMask2 & SOLDIER_BAYONET_RUNBONUS) iBonus += 20; } else if ( HAS_SKILL_TRAIT( pSoldier, MELEE_NT ) && ( gGameOptions.fNewTraitSystem )) { iBonus += gSkillTraitValues.ubMEDamageBonusBlades; // +30% damage - if (pSoldier->usAnimState == FOCUSED_STAB) + if ( !autoresolve && pSoldier->usAnimState == FOCUSED_STAB) { iBonus += gSkillTraitValues.usMEAimedMeleeAttackDamageBonus; // 50% increased damage if focused melee attack } } + // Enhanced Close Combat System - if (gGameExternalOptions.fEnhancedCloseCombatSystem) + if ( !autoresolve && gGameExternalOptions.fEnhancedCloseCombatSystem) { if (gAnimControl[ pTarget->usAnimState ].ubEndHeight == ANIM_PRONE) { iBonus += 30; // incresed damage to lying characters } } - - // Flugente: power armour reduces damage taken - // SANDRO - huh?! - /*INT8 iSlot = VESTPOS; - switch( pSoldier->bAimShotLocation ) - { - case AIM_SHOT_HEAD: - iSlot = HELMETPOS; - break; - case AIM_SHOT_LEGS: - iSlot = LEGPOS; - break; - default: - iSlot = VESTPOS; - break; - }*/ } // remove flag, regardless of whether it was used @@ -9326,7 +9302,7 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO // Flugente: if we are using a garotte, there is a chance that we score an instakill // our level in covert ops and wether the target is aware of us has a huge impact - if ( pObj && HasItemFlag(pObj->usItem, GAROTTE) ) + if ( !autoresolve && pObj && HasItemFlag(pObj->usItem, GAROTTE) ) { INT32 instakillchance = 0; INT32 resistchance = 20; @@ -9356,7 +9332,6 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO iImpact = (iImpact * (100 + iBonus) + 50) / 100; // round it properly // Flugente: moved the damage calculation into a separate function - BOOLEAN autoresolve = IsAutoResolveActive(); iImpact = max( 1, (INT32)(iImpact * (100 - pTarget->GetDamageResistance(autoresolve, FALSE)) / 100 ) ); // Flugente: if the target is a zombie, any melee attack, regardless of hit location, will set the headshot flag. Thus any zombie killed in melee will stay dead (if you play with that option) @@ -10068,7 +10043,7 @@ UINT32 CalcChanceHTH( SOLDIERTYPE * pAttacker,SOLDIERTYPE *pDefender, INT16 ubAi else if (pAttacker->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sVeteranMilitiaCtHBonusPercent) /100); // bonus for enemy - else if (pAttacker->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) + else if ( ( pAttacker->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pAttacker->ubSoldierClass == SOLDIER_CLASS_BANDIT ) && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sEnemyAdminCtHBonusPercent) /100); else if (pAttacker->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sEnemyRegularCtHBonusPercent) /100); @@ -10746,7 +10721,7 @@ UINT32 CalcThrownChanceToHit(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sVeteranMilitiaCtHBonusPercent) /100); // bonus for enemy - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) + else if ( ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sEnemyAdminCtHBonusPercent) /100); else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0) iChance += ((iChance * gGameExternalOptions.sEnemyRegularCtHBonusPercent) /100); @@ -11801,7 +11776,7 @@ FLOAT CalcNewChanceToHitBaseSpecialBonus(SOLDIERTYPE *pSoldier) else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0) fBaseModifier += gGameExternalOptions.sVeteranMilitiaCtHBonusPercent; // bonus for enemy - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) + else if ( ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) fBaseModifier += gGameExternalOptions.sEnemyAdminCtHBonusPercent; else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0) fBaseModifier += gGameExternalOptions.sEnemyRegularCtHBonusPercent; diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index e4182839..abcfc063 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -4870,18 +4870,19 @@ void DebugSoldierPage4( ) switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: gprintf( 320, LINE_HEIGHT * ubLine, L"(Administrator)" ); break; - case SOLDIER_CLASS_ELITE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Army Elite)" ); break; - case SOLDIER_CLASS_ARMY: gprintf( 320, LINE_HEIGHT * ubLine, L"(Army Troop)" ); break; - case SOLDIER_CLASS_CREATURE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Creature)" ); break; + case SOLDIER_CLASS_ELITE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Army Elite)" ); break; + case SOLDIER_CLASS_ARMY: gprintf( 320, LINE_HEIGHT * ubLine, L"(Army Troop)" ); break; + case SOLDIER_CLASS_CREATURE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Creature)" ); break; case SOLDIER_CLASS_GREEN_MILITIA: gprintf( 320, LINE_HEIGHT * ubLine, L"(Green Militia)" ); break; case SOLDIER_CLASS_REG_MILITIA: gprintf( 320, LINE_HEIGHT * ubLine, L"(Reg Militia)" ); break; case SOLDIER_CLASS_ELITE_MILITIA: gprintf( 320, LINE_HEIGHT * ubLine, L"(Elite Militia)" ); break; - case SOLDIER_CLASS_MINER: gprintf( 320, LINE_HEIGHT * ubLine, L"(Miner)" ); break; - case SOLDIER_CLASS_ZOMBIE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Zombie)" ); break; + case SOLDIER_CLASS_MINER: gprintf( 320, LINE_HEIGHT * ubLine, L"(Miner)" ); break; + case SOLDIER_CLASS_ZOMBIE: gprintf( 320, LINE_HEIGHT * ubLine, L"(Zombie)" ); break; + case SOLDIER_CLASS_BANDIT: gprintf( 320, LINE_HEIGHT * ubLine, L"(Bandit)" ); break; default: break; //don't care (don't write anything) } - ubLine++; + ++ubLine; if( pSoldier->bTeam != OUR_TEAM ) { @@ -7785,19 +7786,19 @@ void MakeBloodcatsHostile( void ) iLoop = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; - for (pSoldier = MercPtrs[iLoop]; iLoop <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; iLoop++,pSoldier++ ) + for (pSoldier = MercPtrs[iLoop]; iLoop <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; ++iLoop, ++pSoldier ) { if ( pSoldier->ubBodyType == BLOODCAT && pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 ) { - SetSoldierNonNeutral( pSoldier ); - RecalculateOppCntsDueToNoLongerNeutral( pSoldier ); - if ( ( gTacticalStatus.uiFlags & INCOMBAT ) ) - { - CheckForPotentialAddToBattleIncrement( pSoldier ); - } + SetSoldierNonNeutral( pSoldier ); + RecalculateOppCntsDueToNoLongerNeutral( pSoldier ); + + if ( ( gTacticalStatus.uiFlags & INCOMBAT ) ) + { + CheckForPotentialAddToBattleIncrement( pSoldier ); + } } } - } BOOLEAN SoldierHasLimitedVision(SOLDIERTYPE * pSoldier) diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index c3410eec..8c7da51f 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -2293,12 +2293,11 @@ INT8 CalcMorale(SOLDIERTYPE *pSoldier) case AGGRESSIVE: bMoraleCategory++; break; } - // make idiot administrators much more aggressive - if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) - { - bMoraleCategory += 2; - } - + // make idiot administrators much more aggressive + if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) + { + bMoraleCategory += 2; + } // if still full of energy if (pSoldier->bBreath > 75) @@ -2737,6 +2736,7 @@ UINT8 SoldierDifficultyLevel( SOLDIERTYPE * pSoldier ) switch( pSoldier->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: + case SOLDIER_CLASS_BANDIT: bDifficulty = bDifficultyBase - 1; break; @@ -3543,7 +3543,7 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier) } // make idiot administrators more aggressive - if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) + if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) { bMoraleCategory += 2; } diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index d466b43b..0b4dd692 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -1409,7 +1409,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) iChance = 25; if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA || pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE ) iChance += 15; - else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA || pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR ) + else if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA || pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT ) iChance -= 15; if ( Random(100) < iChance ) { diff --git a/Utils/Text.h b/Utils/Text.h index 4ff5e71a..b2d27431 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -1887,6 +1887,15 @@ enum STR_BREATH_REGEN_SLEEP, + STR_PB_ZOMBIES, + STR_PB_BANDITS, + STR_PB_BLOODCATRAID_HEADER, + STR_PB_ZOMBIERAID_HEADER, + STR_PB_BANDITRAID_HEADER, + STR_PB_ZOMBIE, + STR_PB_BANDIT, + STR_PB_BANDIT_KILLCIVS_IN_SECTOR, + TEXT_NUM_STRATEGIC_TEXT }; diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 34bfa7e1..b04ff21b 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -3514,6 +3514,15 @@ STR16 gpStrategicString[] = L"吉普", // L"Jeep", STR_AR_JEEP_NAME L"\n睡觉时每小时恢复精力: %d", //L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 9f2a0269..04803a12 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -3514,6 +3514,15 @@ STR16 gpStrategicString[] = L"Jeep", //STR_AR_JEEP_NAME // TODO.Translate L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index eb26c39d..35311b84 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -3514,6 +3514,15 @@ STR16 gpStrategicString[] = L"Jeep", //STR_AR_JEEP_NAME, L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 083fadb1..1d764b1e 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -3522,6 +3522,15 @@ STR16 gpStrategicString[] = L"Jeep", //STR_AR_JEEP_NAME // TODO.Translate L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 624e5767..05f3822c 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -3525,6 +3525,15 @@ STR16 gpStrategicString[] = L"Jeep", //STR_AR_JEEP_NAME // TODO.Translate L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index d258b7af..02307a63 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -3509,6 +3509,15 @@ STR16 gpStrategicString[] = L"Jeep", //STR_AR_JEEP_NAME // TODO.Translate L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index bc1687be..58f30366 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -3520,6 +3520,15 @@ STR16 gpStrategicString[] = L"Jeep", //STR_AR_JEEP_NAME // TODO.Translate L"\nBreath regeneration per hour: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] = diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 9689b687..20020fb1 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -3514,6 +3514,15 @@ STR16 gpStrategicString[] = L"Джип", //STR_AR_JEEP_NAME L"\nВосстановление дыхания в час: %d", // STR_BREATH_REGEN_SLEEP + + L"Zombies", // TODO.Translate + L"Bandits", + L"BLOODCAT ATTACK", + L"ZOMBIE ATTACK", + L"BANDIT ATTACK", + L"Zombie", + L"Bandit", + L"Bandits attack and kill %d civilians in sector %s.", }; STR16 gpGameClockString[] =