Files
source/Strategic/Queen Command.cpp
T
Wanne 7ca53fadc7 *** MERGED source code from development trunk revision 4890 up to latest current revision 5105 ***
Development Trunk (now obsolete): https://ja2svn.dyndns.org/source/ja2/branches/Wanne/JA2%201.13%20MP
A detailed list of all the features (changes) is visible in the history log of the development trunk:

INFO: There will be no further development in the development source trunk, so is obsolete from now on!

This commit contains the following features (bugfixes are not listed):

- New Feature: Show merc ranks in Strategy Screen before merc name
o Toggle on/off in the ingame options screen
o For each experience level (0-10) there are defined rank names in the language specific text files
o Additional language text that is not translated yet is marked with // TODO.Translate

- New Feature: Faster Bobby Ray Shipments
o This feature is selectable in the ja2_options.ini
o When activated Bobby Ray shipments will be really fast
-> Overnight Express: 5 - 7 hours
-> Air Service: 10, 12 or 14 hours
-> Standard Service: 15, 18 or 21 hours
o In a multiplayer game, this feature is disabled (because shipment arrive immediately in the destination sector)

- New Feature: AP cost for manipulating the merc inventory
o This feature is selectable in the start new game screen
o For example, putting a weapon from hand in the backpack costs APs
o The AP costs are defined in the APBPConstants.ini
o In a multiplayer game, this feature is disabled

- New Feature: Added possibility to set individual Gear Kit names
o Set individual names in the optional new tag <mGearKitName> inside the <GEARKIT> root element (File: TableData\MercStartingGear.xml)

- New Feature: Externalized the maximum purchase amount (JA2 vanilla: 10) for Bobby Rays purchases per shipment
o Define the max. amount in the ja2_options.ini: BOBBY_RAY_MAX_PURCHASE_AMOUNT, Range: 10 - 100)
o Nice scrolling bars to scroll through all the purchases

- New Feature: Now it is possible to walk on "water" tiles or handle those tiles like "normal" tiles 
o (instead of showing the swimming animation and counting the APs for swimming), when the soldier is located on the second level (level > 0)

- New Feature: Externalized AIM Veterans laptop site
o Add maximum of 4 pages
o Add  new AIM to veterans page, and remove AIM from veterans page.
o New XML file: OldAIMArchive.xml (language specific XML files: *.OldAIMArchive.xml)

- New Feature: Added possibility to choose from 254 IMP faces
o Modified IMPPortraits.xml
o Added new GameDir folder "IMPFaces" which contains the IMP face portraits
o Moved IMP faces in the new "IMPFaces" folder

- New Feature: ARSP - All Resolution Support Project
o See here: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=298698&page=1
o Support any kind of screen resolution. This works by choosing the matching full supported resolution (640x480, 800x600, 1024x768) and then centering the graphics
o See ja2.ini for defining the screen resolution

- New Feature: AI Turn Speed Up (FF-MOD)
o see here: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=298699#Post298699
o When this feature is activated it allows fast enemy turns
o New entry in ja2.ini to enable this feature: HIGHSPEED_TIMER = TRUE
o Turn on/off auto fast turns in ingame options
o New ja2_options.ini settings for fine tuning: [Clock Settings] ini block

- New Feature: "Lock" / "Release" mouse cursor in tactical windowed mode, so the mouse can stay within window boundary
o This is very helpful for doing scrolling with the mouse (by moving the mouse to the window edge) in windowed mode
o You can toggle on/off the feature by pressing CTRL + Z. It is also possible to press CTRL + Y. So the user can decide which fits better :)
o Whenever you toggle on/off a screen message appears on the tactical log to notify the player

- New Feature: Weapon overheating
o Basically, every shot fired from a gun increases its temperature. On higher temperatures, the gun jams more frequently
o On/off toggle in the ingame options
o Graphical thermometer indication on the weapon picture
o New ja2_options.ini section with a few options, Section: [Tactical Weapon Overheating Settings]
o XML changes (New Tags)
--> AmmoTypes.xml: <temperatureModificator>
--> Items.xml: <barrel>, <usOverheatingCooldownFactor>
--> Weapons.xml: <usOverheatingJamThreshold>, <usOverheatingDamageThreshold>, <usOverheatingSingleShotTemperature>
o Added a few barells to Items.xml (end of the file)
o more infos: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=301059&page=1

- New Feature: Resting weapons on a surfaces
o You can now rest your weapon on crates, low walls, rocks, debris, open windows etc. while you are crouched. Resting will give yu the superior gun handling modifiers from PRONE position.
o A little 'A' on your gun indicates if you are resting it or not.
o Both settings can be turned on and off in Ja2_Options.ini under 'Tactical Gameplay Settings'

- New Feature: Externalized option on whether mercs can exit sector using grid exit in turn based mode
o new ja2_options.ini property: GRID_EXIT_IN_TURNBASED
o This ini setting does not affect leaving sectors on map edges

- Rework/Update: Ammo boxes and Ammo crates
o Ammo crate items added for remaining calibers.  Tweaked prices to correspond to magazines, tweaked capacities and weights to be reasonable (~800 or less).
o Shift-R reload now looks at both boxes and crates in sector items and merc's inventory to reload, and tops off magazines in inventory as well.  Only possible outside of combat.
o Ctrl-Shift-A still creates crates as per usual, with some fixes to ensure all ammo is caught on the first press.
o Shift-A now creates ammo boxes in the same loop.  They mostly behave like crates, but fit in mercs inventories.
o Ammo boxes are available to purchase at BR and tony, sam, etc.  Individual magazines/drums/etc are no longer available.
o Removed ammo crate xml tag.  Added ubMagType tag to Magazines array.  This allows us to differentiate between magazines, bullets, boxes and crates.
o Changed ammo filtering in BR's to display boxes appropriately.  Added back the "Mag:" display (changed text to "Size:")
o Added ja2set.dat.xml file (WF map version in WF maps folder) to be used instead of ja2set.dat file (ja2.ini setting added) For now, if you want to play vanilla instead, just change the setting in the ini (see comments in file).
o Added P4-P9Items image files (up to 5k images each) and tileslots.  1.13 core items will continue to use Guns and P1-P3Items for the foreseeable future.  P4-P9 can be used by customizers/modders to avoid conflicts with Guns/P1-P3 items.  TODO: Need to discuss a strategy for this in the forums.
o Added in Ambient/0.bad and 32.bad to avoid an exception in the code.
o Updated the XML Editor
o Standardized the text on all the ammo items.

- New Multiplayer Feature: Sharing same FOV (field of view) in a coop/team-dm (for friendly teams) game
o You will also see enemies, that your teammate sees

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5108 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-03-23 18:46:54 +00:00

2423 lines
78 KiB
C++

//Queen Command.c
#ifdef PRECOMPILEDHEADERS
#include "Strategic All.h"
#include "GameSettings.h"
#else
#include "Queen Command.h"
#include "Strategic Event Handler.h"
#include "Overhead Types.h"
#include "strategicmap.h"
#include "Soldier Init List.h"
#include "debug.h"
#include "Random.h"
#include "Strategic Movement.h"
#include "Overhead.h"
#include "Strategic Pathing.h"
#include "strategic.h"
#include "message.h"
#include "Map Edgepoints.h"
#include "PreBattle Interface.h"
#include "strategic status.h"
#include "squads.h"
#include "assignments.h"
#include "items.h"
#include "tactical save.h"
#include "Soldier Ani.h"
#include "Strategic AI.h"
#include "GameSettings.h"
#include "MessageBoxScreen.h"
#include "screenids.h"
#include "Strategic Town Loyalty.h"
#include "Soldier Profile.h"
#include "Quests.h"
#include "jascreens.h"
#include "Auto Resolve.h"
#include "Game Event Hook.h"
#include "Animation Data.h"
#include "Game Clock.h"
#include "renderworld.h"
#include "Town Militia.h"
#include "Dialogue Control.h"
#include "Campaign Init.h"
#include "meanwhile.h"
#include "Soldier macros.h"
#include "Morale.h"
#endif
#ifdef JA2BETAVERSION
extern BOOLEAN gfClearCreatureQuest;
#endif
#include "connect.h"
#include "Reinforcement.h"
#include "MilitiaSquads.h"
#ifdef JA2UB
#include "Explosion Control.h"
#include "Ja25_Tactical.h"
#include "Ja25 Strategic Ai.h"
#include "MapScreen Quotes.h"
#include "email.h"
#include "interface Dialogue.h"
#include "Arms Dealer Init.h"
#endif
#include <vector>
//The sector information required for the strategic AI. Contains the number of enemy troops,
//as well as intentions, etc.
//SECTORINFO SectorInfo[256];
std::vector<SECTORINFO> SectorInfo (256);
UNDERGROUND_SECTORINFO *gpUndergroundSectorInfoHead = NULL;
extern UNDERGROUND_SECTORINFO* gpUndergroundSectorInfoTail;
BOOLEAN gfPendingEnemies = FALSE;
extern void BuildUndergroundSectorInfoList();
extern void EndCreatureQuest();
extern GARRISON_GROUP *gGarrisonGroup;
extern INT32 giGarrisonArraySize;
#ifdef JA2TESTVERSION
extern BOOLEAN gfOverrideSector;
#endif
INT32 gsInterrogationGridNo[3] = { 7756, 7757, 7758 };
#ifdef JA2UB
INT32 gsGridNoForMapEdgePointInfo=-1;
#endif
#ifdef JA2UB
void HandleBloodCatDeaths( SECTORINFO *pSector );
#endif
extern void Ensure_RepairedGarrisonGroup( GARRISON_GROUP **ppGarrison, INT32 *pGarraySize );
void ValidateEnemiesHaveWeapons()
{
#ifdef JA2BETAVERSION
SGPRect CenteringRect= {0 + xResOffset, 0, SCREEN_WIDTH - xResOffset, SCREEN_HEIGHT };
INT32 i, iErrorDialog;
SOLDIERTYPE *pSoldier;
INT32 iNumInvalid = 0;
for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
if( !pSoldier->bActive || !pSoldier->bInSector )
{
continue;
}
if( !pSoldier->inv[ HANDPOS ].usItem )
{
iNumInvalid++;
}
// WDS DEBUG
// Uncommenting the following two lines will cause all the PLACED soldiers to instantly drop dead,
// which is useful for debugging some things
//pSoldier->bBleeding = 10;
//pSoldier->stats.bLife = 1;
}
// do message box and return
if( iNumInvalid )
{
CHAR16 str[ 100 ];
swprintf( str, L"%d enemies have been added without any weapons! KM:0. Please note sector.", iNumInvalid );
iErrorDialog = DoMessageBox( MSG_BOX_BASIC_STYLE, str, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, &CenteringRect );
}
#endif
}
//Counts enemies and crepitus, but not bloodcats.
UINT8 NumHostilesInSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
UINT8 ubNumHostiles = 0;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
AssertGE( sSectorZ, MINIMUM_VALID_Z_COORDINATE );
AssertLE( sSectorZ, MAXIMUM_VALID_Z_COORDINATE );
if( sSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( sSectorX, sSectorY, (UINT8)sSectorZ );
if( pSector )
{
ubNumHostiles = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumCreatures);
}
}
else
{
SECTORINFO *pSector;
GROUP *pGroup;
//Count stationary hostiles
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
ubNumHostiles = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites + pSector->ubNumCreatures);
//Count mobile enemies
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY)
{
ubNumHostiles += pGroup->ubGroupSize;
}
pGroup = pGroup->next;
}
}
return ubNumHostiles;
}
UINT8 NumEnemiesInAnySector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
UINT8 ubNumEnemies = 0;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
AssertGE( sSectorZ, MINIMUM_VALID_Z_COORDINATE );
AssertLE( sSectorZ, MAXIMUM_VALID_Z_COORDINATE );
if( sSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( sSectorX, sSectorY, (UINT8)sSectorZ );
if( pSector )
{
ubNumEnemies = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites);
}
}
else
{
SECTORINFO *pSector;
GROUP *pGroup;
//Count stationary enemies
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
ubNumEnemies = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites);
//Count mobile enemies
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY )
{
ubNumEnemies += pGroup->ubGroupSize;
}
pGroup = pGroup->next;
}
}
return ubNumEnemies;
}
UINT8 NumEnemiesInSector( INT16 sSectorX, INT16 sSectorY )
{
SECTORINFO *pSector;
GROUP *pGroup;
UINT8 ubNumTroops;
// HEADROCK: This is a TEMPORARY fix to avoid the assertion error. Not sure this is the best solution,
// probably isn't. But I need this bit to work.
if (sSectorX < MINIMUM_VALID_X_COORDINATE ||
sSectorX > MAXIMUM_VALID_X_COORDINATE ||
sSectorY < MINIMUM_VALID_Y_COORDINATE ||
sSectorY > MAXIMUM_VALID_Y_COORDINATE)
{
return (0);
}
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
ubNumTroops = (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites);
if (is_networked)
ubNumTroops += numenemyLAN((UINT8)sSectorX,(UINT8)sSectorY ); //hayden
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY )
{
ubNumTroops += pGroup->ubGroupSize;
}
pGroup = pGroup->next;
}
return ubNumTroops;
}
UINT8 NumStationaryEnemiesInSector( INT16 sSectorX, INT16 sSectorY )
{
SECTORINFO *pSector;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
if( pSector->ubGarrisonID == NO_GARRISON )
{ //If no garrison, no stationary.
return( 0 );
}
Ensure_RepairedGarrisonGroup( &gGarrisonGroup, &giGarrisonArraySize ); /* added NULL fix, 2007-03-03, Sgt. Kolja */
// don't count roadblocks as stationary garrison, we want to see how many enemies are in them, not question marks
if ( gGarrisonGroup[ pSector->ubGarrisonID ].ubComposition == ROADBLOCK )
{
// pretend they're not stationary
return( 0 );
}
return (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites);
}
UINT8 NumMobileEnemiesInSector( INT16 sSectorX, INT16 sSectorY )
{
GROUP *pGroup;
SECTORINFO *pSector;
UINT8 ubNumTroops;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
ubNumTroops = 0;
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY )
{
ubNumTroops += pGroup->ubGroupSize;
}
pGroup = pGroup->next;
}
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
if( pSector->ubGarrisonID == ROADBLOCK )
{ //consider these troops as mobile troops even though they are in a garrison
ubNumTroops += (UINT8)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites);
}
return ubNumTroops;
}
void GetNumberOfMobileEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites )
{
GROUP *pGroup;
SECTORINFO *pSector;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
//Now count the number of mobile groups in the sector.
*pubNumTroops = *pubNumElites = *pubNumAdmins = 0;
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY )
{
*pubNumTroops += pGroup->pEnemyGroup->ubNumTroops;
*pubNumElites += pGroup->pEnemyGroup->ubNumElites;
*pubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins;
}
pGroup = pGroup->next;
}
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
if( pSector->ubGarrisonID == ROADBLOCK )
{ //consider these troops as mobile troops even though they are in a garrison
*pubNumAdmins += pSector->ubNumAdmins;
*pubNumTroops += pSector->ubNumTroops;
*pubNumElites += pSector->ubNumElites;
}
}
void GetNumberOfMobileEnemiesInSectorWithoutRoadBlock( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites )
{
GROUP *pGroup;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
//Now count the number of mobile groups in the sector.
*pubNumTroops = *pubNumElites = *pubNumAdmins = 0;
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY )
{
*pubNumTroops += pGroup->pEnemyGroup->ubNumTroops;
*pubNumElites += pGroup->pEnemyGroup->ubNumElites;
*pubNumAdmins += pGroup->pEnemyGroup->ubNumAdmins;
}
pGroup = pGroup->next;
}
}
void GetNumberOfStationaryEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites )
{
SECTORINFO *pSector;
AssertGE( sSectorX, MINIMUM_VALID_X_COORDINATE);
AssertLE( sSectorX, MAXIMUM_VALID_X_COORDINATE );
AssertGE( sSectorY, MINIMUM_VALID_Y_COORDINATE);
AssertLE( sSectorY, MAXIMUM_VALID_Y_COORDINATE );
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
//grab the number of each type in the stationary sector
*pubNumAdmins = pSector->ubNumAdmins;
*pubNumTroops = pSector->ubNumTroops;
*pubNumElites = pSector->ubNumElites;
}
void GetNumberOfEnemiesInSector( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites )
{
UINT8 ubNumAdmins, ubNumTroops, ubNumElites;
GetNumberOfStationaryEnemiesInSector( sSectorX, sSectorY, pubNumAdmins, pubNumTroops, pubNumElites );
GetNumberOfMobileEnemiesInSector( sSectorX, sSectorY, &ubNumAdmins, &ubNumTroops, &ubNumElites );
*pubNumAdmins += ubNumAdmins;
*pubNumTroops += ubNumTroops;
*pubNumElites += ubNumElites;
}
void EndTacticalBattleForEnemy()
{
GROUP *pGroup;
INT32 i;
//Clear enemies in battle for all stationary groups in the sector.
if( gbWorldSectorZ > 0 )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
pSector->ubAdminsInBattle = 0;
pSector->ubTroopsInBattle = 0;
pSector->ubElitesInBattle = 0;
}
else if( !gbWorldSectorZ )
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
//grab the number of each type in the stationary sector
pSector->ubAdminsInBattle = 0;
pSector->ubTroopsInBattle = 0;
pSector->ubElitesInBattle = 0;
pSector->ubNumCreatures = 0;
pSector->ubCreaturesInBattle = 0;
}
else // negative
return;
//Clear this value so that profiled enemies can be added into battles in the future.
gfProfiledEnemyAdded = FALSE;
//Clear enemies in battle for all mobile groups in the sector.
pGroup = gpGroupList;
while( pGroup )
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY )
{
pGroup->pEnemyGroup->ubTroopsInBattle = 0;
pGroup->pEnemyGroup->ubElitesInBattle = 0;
pGroup->pEnemyGroup->ubAdminsInBattle = 0;
}
pGroup = pGroup->next;
}
//Check to see if any of our mercs have abandoned the militia during a battle. This is cause for a rather
//severe loyalty blow.
for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ )
{
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->bInSector && MercPtrs[ i ]->stats.bLife >= OKLIFE )
{ //found one live militia, so look for any enemies/creatures.
// NOTE: this is relying on ENEMY_TEAM being immediately followed by CREATURE_TEAM
for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ )
{
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->bInSector && MercPtrs[ i ]->stats.bLife >= OKLIFE )
{ //confirmed at least one enemy here, so do the loyalty penalty.
HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ABANDON_MILITIA, gWorldSectorX, gWorldSectorY, 0 );
break;
}
}
break;
}
}
}
UINT8 NumFreeEnemySlots()
{
UINT8 ubNumFreeSlots = 0;
INT32 i;
SOLDIERTYPE *pSoldier;
//Count the number of free enemy slots. It is possible to have multiple groups exceed the maximum.
for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM].bLastID; i++ )
{
pSoldier = &Menptr[ i ];
if( !pSoldier->bActive )
ubNumFreeSlots++;
}
return ubNumFreeSlots;
}
//Called when entering a sector so the campaign AI can automatically insert the
//correct number of troops of each type based on the current number in the sector
//in global focus (gWorldSectorX/Y)
BOOLEAN PrepareEnemyForSectorBattle()
{
SECTORINFO *pSector;
GROUP *pGroup;
SOLDIERTYPE *pSoldier;
unsigned ubNumAdmins, ubNumTroops, ubNumElites;
unsigned ubTotalAdmins, ubTotalElites, ubTotalTroops;
unsigned totalCountOfStationaryEnemies = 0;
unsigned totalCountOfMobileEnemies = 0;
int sNumSlots;
gfPendingEnemies = FALSE;
if( gbWorldSectorZ > 0 )
return PrepareEnemyForUndergroundBattle();
// Add the invading group
if (gpBattleGroup && !gpBattleGroup->fPlayer )
{
//The enemy has instigated the battle which means they are the ones entering the conflict.
//The player was actually in the sector first, and the enemy doesn't use reinforced placements
HandleArrivalOfReinforcements( gpBattleGroup );
// Reinforcement groups? Bring it on!
if( gGameExternalOptions.gfAllowReinforcements &&
!( (GetTownIdForSector( gWorldSectorX, gWorldSectorY ) == OMERTA )&&( gGameOptions.ubDifficultyLevel != DIF_LEVEL_INSANE ) ) )
{
UINT16 pusMoveDir[4][3];
UINT8 ubDirNumber = 0;
GROUP *pGroup;
SECTORINFO *pThisSector;
// They arrived in multiple groups, so here they come
pThisSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
GenerateDirectionInfos( gWorldSectorX, gWorldSectorY, &ubDirNumber, pusMoveDir, FALSE, TRUE );
for( unsigned ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ )
{
while ( NumMobileEnemiesInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) && GetEnemyGroupInSector( SECTORX( pusMoveDir[ ubIndex][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) )
{
pGroup = GetEnemyGroupInSector( SECTORX( pusMoveDir[ ubIndex][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) );
pGroup->ubPrevX = pGroup->ubSectorX;
pGroup->ubPrevY = pGroup->ubSectorY;
pGroup->ubSectorX = pGroup->ubNextX = (UINT8)gWorldSectorX;
pGroup->ubSectorY = pGroup->ubNextY = (UINT8)gWorldSectorY;
}
}
//It is now possible that other enemy groups have also arrived. Add them in the same manner.
pGroup = gpGroupList;
while( pGroup )
{
if( pGroup != gpBattleGroup && !pGroup->fPlayer && !pGroup->fVehicle &&
pGroup->ubSectorX == gpBattleGroup->ubSectorX &&
pGroup->ubSectorY == gpBattleGroup->ubSectorY &&
!pGroup->pEnemyGroup->ubAdminsInBattle &&
!pGroup->pEnemyGroup->ubTroopsInBattle &&
!pGroup->pEnemyGroup->ubElitesInBattle )
{
HandleArrivalOfReinforcements( pGroup );
}
pGroup = pGroup->next;
}
}
ValidateEnemiesHaveWeapons();
return ( ( BOOLEAN) ( gpBattleGroup->ubGroupSize > 0 ) );
}
// WDS Count the number of placements. This will limit the maximum number of enemies we can place on the map
SOLDIERINITNODE *curr = gSoldierInitHead;
unsigned mapMaximumNumberOfEnemies = 0;
while( curr ) {
if( curr->pBasicPlacement->bTeam == ENEMY_TEAM ) {
++mapMaximumNumberOfEnemies;
}
curr = curr->next;
}
AssertGE (mapMaximumNumberOfEnemies, 0);
if (mapMaximumNumberOfEnemies > gGameExternalOptions.ubGameMaximumNumberOfEnemies)
mapMaximumNumberOfEnemies = gGameExternalOptions.ubGameMaximumNumberOfEnemies;
gfPendingEnemies = (NumEnemiesInSector( gWorldSectorX, gWorldSectorY ) > mapMaximumNumberOfEnemies );
pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
// OJW - 20090403 - override max ai in co-op
if (is_networked && is_server && gMaxEnemiesEnabled == 1)
{
float totalEnemies = (float)(pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites);
ubTotalAdmins = (unsigned int)(((float)pSector->ubNumAdmins / totalEnemies) * mapMaximumNumberOfEnemies);
ubTotalTroops = (unsigned int)(((float)pSector->ubNumTroops / totalEnemies) * mapMaximumNumberOfEnemies);
ubTotalElites = (unsigned int)(((float)pSector->ubNumElites / totalEnemies) * mapMaximumNumberOfEnemies);
totalEnemies = (float)(ubTotalAdmins + ubTotalTroops + ubTotalElites);
// take care of any rounding losses
while (totalEnemies < mapMaximumNumberOfEnemies)
{
// just fill out with standard troops
ubTotalTroops++;
totalEnemies++;
}
pSector->ubNumAdmins = ubTotalAdmins;
pSector->ubNumTroops = ubTotalTroops;
pSector->ubNumElites = ubTotalElites;
pSector->ubAdminsInBattle = 0;
pSector->ubTroopsInBattle = 0;
pSector->ubElitesInBattle = 0;
}
else
{
if( pSector->uiFlags & SF_USE_MAP_SETTINGS )
{ //count the number of enemy placements in a map and use those
SOLDIERINITNODE *curr = gSoldierInitHead;
ubTotalAdmins = ubTotalTroops = ubTotalElites = 0;
while( curr )
{
if( curr->pBasicPlacement->bTeam == ENEMY_TEAM )
{
switch( curr->pBasicPlacement->ubSoldierClass )
{
case SOLDIER_CLASS_ADMINISTRATOR: ubTotalAdmins++; break;
case SOLDIER_CLASS_ARMY: ubTotalTroops++; break;
case SOLDIER_CLASS_ELITE: ubTotalElites++; break;
}
}
curr = curr->next;
}
pSector->ubNumAdmins = ubTotalAdmins;
pSector->ubNumTroops = ubTotalTroops;
pSector->ubNumElites = ubTotalElites;
pSector->ubAdminsInBattle = 0;
pSector->ubTroopsInBattle = 0;
pSector->ubElitesInBattle = 0;
}
else
{
ubTotalAdmins = pSector->ubNumAdmins - pSector->ubAdminsInBattle;
ubTotalTroops = pSector->ubNumTroops - pSector->ubTroopsInBattle;
ubTotalElites = pSector->ubNumElites - pSector->ubElitesInBattle;
}
}
totalCountOfStationaryEnemies = ubTotalAdmins + ubTotalTroops + ubTotalElites;
if( totalCountOfStationaryEnemies > mapMaximumNumberOfEnemies )
{
/*
#ifdef JA2BETAVERSION
ScreenMsg( FONT_RED, MSG_ERROR, L"The total stationary enemy forces in sector %c%d is %d. (max %d)",
gWorldSectorY + 'A' - 1, gWorldSectorX, ubTotalAdmins + ubTotalTroops + ubTotalElites, mapMaximumNumberOfEnemies );
#endif
*/
ubTotalAdmins = min( mapMaximumNumberOfEnemies, ubTotalAdmins );
ubTotalTroops = min( mapMaximumNumberOfEnemies-ubTotalAdmins, ubTotalTroops );
ubTotalElites = min( mapMaximumNumberOfEnemies-ubTotalAdmins-ubTotalTroops, ubTotalElites );
}
pSector->ubAdminsInBattle += ubTotalAdmins;
pSector->ubTroopsInBattle += ubTotalTroops;
pSector->ubElitesInBattle += ubTotalElites;
#ifdef JA2TESTVERSION
if( gfOverrideSector )
{
//if there are no troops in the current groups, then we're done.
if( !ubTotalAdmins && !ubTotalTroops && !ubTotalElites )
return FALSE;
AddSoldierInitListEnemyDefenceSoldiers( ubTotalAdmins, ubTotalTroops, ubTotalElites );
ValidateEnemiesHaveWeapons();
return TRUE;
}
#endif
//Search for movement groups that happen to be in the sector.
sNumSlots = NumFreeEnemySlots();
if (sNumSlots > (int)mapMaximumNumberOfEnemies)
sNumSlots = mapMaximumNumberOfEnemies;
/*
* WDS - this test no longer valid with the above change
//Test: All slots should be free at this point!
if( sNumSlots != gTacticalStatus.Team[ENEMY_TEAM].bLastID - gTacticalStatus.Team[ENEMY_TEAM].bFirstID + 1 )
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_RED, MSG_ERROR, L"All enemy slots should be free at this point. Only %d of %d are available.\nTrying to add %d admins, %d troops, and %d elites.",
sNumSlots, gTacticalStatus.Team[ENEMY_TEAM].bLastID - gTacticalStatus.Team[ENEMY_TEAM].bFirstID + 1 ,
ubTotalAdmins, ubTotalTroops, ubTotalElites );
#endif
}
*/
//Subtract the total number of stationary enemies from the available slots, as stationary forces take
//precendence in combat. The mobile forces that could also be in the same sector are considered later if
//all the slots fill up.
sNumSlots -= ubTotalAdmins + ubTotalTroops + ubTotalElites;
//Now, process all of the groups and search for both enemy and player groups in the sector.
//For enemy groups, we fill up the slots until we have none left or all of the groups have been
//processed.
for( pGroup = gpGroupList;
pGroup;
pGroup = pGroup->next)
{
if( !pGroup->fPlayer && !pGroup->fVehicle &&
pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ )
{ //Process enemy group in sector.
if( sNumSlots > 0 )
{
AssertGE(pGroup->pEnemyGroup->ubNumAdmins, pGroup->pEnemyGroup->ubAdminsInBattle);
ubNumAdmins = pGroup->pEnemyGroup->ubNumAdmins - pGroup->pEnemyGroup->ubAdminsInBattle;
sNumSlots -= ubNumAdmins;
if( sNumSlots < 0 )
{ //adjust the value to zero
ubNumAdmins += sNumSlots;
sNumSlots = 0;
gfPendingEnemies = TRUE;
}
pGroup->pEnemyGroup->ubAdminsInBattle += ubNumAdmins;
ubTotalAdmins += ubNumAdmins;
}
if( sNumSlots > 0 )
{ //Add regular army forces.
AssertGE(pGroup->pEnemyGroup->ubNumTroops, pGroup->pEnemyGroup->ubTroopsInBattle);
ubNumTroops = pGroup->pEnemyGroup->ubNumTroops - pGroup->pEnemyGroup->ubTroopsInBattle;
sNumSlots -= ubNumTroops;
if( sNumSlots < 0 )
{ //adjust the value to zero
ubNumTroops += sNumSlots;
sNumSlots = 0;
gfPendingEnemies = TRUE;
}
pGroup->pEnemyGroup->ubTroopsInBattle += ubNumTroops;
ubTotalTroops += ubNumTroops;
}
if( sNumSlots > 0 )
{ //Add elite troops
AssertGE(pGroup->pEnemyGroup->ubNumElites, pGroup->pEnemyGroup->ubElitesInBattle);
ubNumElites = pGroup->pEnemyGroup->ubNumElites - pGroup->pEnemyGroup->ubElitesInBattle;
sNumSlots -= ubNumElites;
if( sNumSlots < 0 )
{ //adjust the value to zero
ubNumElites += sNumSlots;
sNumSlots = 0;
gfPendingEnemies = TRUE;
}
pGroup->pEnemyGroup->ubElitesInBattle += ubNumElites;
ubTotalElites += ubNumElites;
}
//NOTE:
//no provisions for profile troop leader or retreat groups yet.
}
if( pGroup->fPlayer && !pGroup->fVehicle && !pGroup->fBetweenSectors &&
pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ )
{ //TEMP: The player path needs to get destroyed, otherwise, it'll be impossible to move the
// group after the battle is resolved.
// no one in the group any more continue loop
if( pGroup->pPlayerList == NULL )
{
continue;
}
// clear the movt for this grunt and his buddies
RemoveGroupWaypoints( pGroup->ubGroupID );
}
}
//if there are no troops in the current groups, then we're done.
if( !ubTotalAdmins && !ubTotalTroops && !ubTotalElites )
{
return FALSE;
}
AddSoldierInitListEnemyDefenceSoldiers( ubTotalAdmins, ubTotalTroops, ubTotalElites );
//Now, we have to go through all of the enemies in the new map, and assign their respective groups if
//in a mobile group, but only for the ones that were assigned from the
sNumSlots = mapMaximumNumberOfEnemies - totalCountOfStationaryEnemies;
pGroup = gpGroupList;
while( pGroup && sNumSlots > 0 )
{
if( !pGroup->fPlayer && !pGroup->fVehicle &&
pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ )
{
ubNumAdmins = pGroup->pEnemyGroup->ubAdminsInBattle;
ubNumTroops = pGroup->pEnemyGroup->ubTroopsInBattle;
ubNumElites = pGroup->pEnemyGroup->ubElitesInBattle;
unsigned num = ubNumAdmins + ubNumTroops + ubNumElites;
unsigned firstSlot = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
unsigned lastSlot = gTacticalStatus.Team[ ENEMY_TEAM ].bLastID;
unsigned slotsAvailable = lastSlot-firstSlot+1;
AssertGE((int)slotsAvailable, sNumSlots);
for (unsigned slot = firstSlot;
(slot <= lastSlot) && (num > 0);
++slot)
{
pSoldier = &Menptr[ slot ];
// Skip inactive and already grouped soldiers
if (!pSoldier->bActive || pSoldier->ubGroupID)
{
continue;
}
// At this point we should not have added more soldiers than are in slots
AssertGT( sNumSlots, 0 );
switch( pSoldier->ubSoldierClass )
{
case SOLDIER_CLASS_ADMINISTRATOR:
if( ubNumAdmins )
{
num--;
sNumSlots--;
ubNumAdmins--;
pSoldier->ubGroupID = pGroup->ubGroupID;
}
break;
case SOLDIER_CLASS_ARMY:
if( ubNumTroops )
{
num--;
sNumSlots--;
ubNumTroops--;
pSoldier->ubGroupID = pGroup->ubGroupID;
}
break;
case SOLDIER_CLASS_ELITE:
if( ubNumElites )
{
num--;
sNumSlots--;
ubNumElites--;
pSoldier->ubGroupID = pGroup->ubGroupID;
}
break;
}
}
AssertEQ( ubNumElites , 0);
AssertEQ( ubNumTroops , 0);
AssertEQ( ubNumAdmins , 0);
AssertEQ( num , 0);
}
pGroup = pGroup->next;
}
ValidateEnemiesHaveWeapons();
return TRUE;
}
BOOLEAN PrepareEnemyForUndergroundBattle()
{
UNDERGROUND_SECTORINFO *pUnderground;
unsigned ubTotalAdmins, ubTotalTroops, ubTotalElites;
pUnderground = gpUndergroundSectorInfoHead;
while( pUnderground )
{
if( pUnderground->ubSectorX == gWorldSectorX &&
pUnderground->ubSectorY == gWorldSectorY &&
pUnderground->ubSectorZ == gbWorldSectorZ )
{ //This is the sector we are going to be fighting in.
if( pUnderground->ubNumAdmins || pUnderground->ubNumTroops || pUnderground->ubNumElites )
{
ubTotalAdmins = (UINT8)(pUnderground->ubNumAdmins - pUnderground->ubAdminsInBattle);
ubTotalTroops = (UINT8)(pUnderground->ubNumTroops - pUnderground->ubTroopsInBattle);
ubTotalElites = (UINT8)(pUnderground->ubNumElites - pUnderground->ubElitesInBattle);
pUnderground->ubAdminsInBattle += ubTotalAdmins;
pUnderground->ubTroopsInBattle += ubTotalTroops;
pUnderground->ubElitesInBattle += ubTotalElites;
AddSoldierInitListEnemyDefenceSoldiers( pUnderground->ubNumAdmins, pUnderground->ubNumTroops, pUnderground->ubNumElites );
ValidateEnemiesHaveWeapons();
}
return ( ( BOOLEAN) ( pUnderground->ubNumAdmins + pUnderground->ubNumTroops + pUnderground->ubNumElites > 0 ) );
}
pUnderground = pUnderground->next;
}
// underground sector not found in list
Assert( FALSE );
return FALSE;
}
//The queen AI layer must process the event by subtracting forces, etc.
void ProcessQueenCmdImplicationsOfDeath( SOLDIERTYPE *pSoldier )
{
INT32 iNumEnemiesInSector;
SECTORINFO *pSector;
INT32 iMaxEnemyGroupSize = gGameExternalOptions.iMaxEnemyGroupSize;
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"QueenCommand");
AssertNotNIL (pSoldier);
EvaluateDeathEffectsToSoldierInitList( pSoldier );
switch( pSoldier->ubProfile )
{
#ifdef JA2UB
case 75://MORRIS:
if( !pSoldier->bSectorZ )
{
pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ];
if( pSector->ubNumElites )
{
pSector->ubNumElites--;
}
if( pSector->ubElitesInBattle )
{
pSector->ubElitesInBattle--;
}
}
else
{
UNDERGROUND_SECTORINFO *pUnderground;
pUnderground = FindUnderGroundSector( (UINT8)pSoldier->sSectorX, (UINT8)pSoldier->sSectorY, (UINT8)pSoldier->bSectorZ );
Assert( pUnderground );
if( pUnderground->ubNumElites )
{
pUnderground->ubNumElites--;
}
if( pUnderground->ubElitesInBattle )
{
pUnderground->ubElitesInBattle--;
}
}
break;
#endif
case MIKE:
case IGGY:
if( pSoldier->ubProfile == IGGY && !gubFact[ FACT_IGGY_AVAILABLE_TO_ARMY ] )
{ //Iggy is on our team!
break;
}
if( !pSoldier->bSectorZ )
{
pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ];
if( pSector->ubNumElites )
{
pSector->ubNumElites--;
}
if( pSector->ubElitesInBattle )
{
pSector->ubElitesInBattle--;
}
}
else
{
UNDERGROUND_SECTORINFO *pUnderground;
pUnderground = FindUnderGroundSector( (UINT8)pSoldier->sSectorX, (UINT8)pSoldier->sSectorY, (UINT8)pSoldier->bSectorZ );
Assert( pUnderground );
if( pUnderground->ubNumElites )
{
pUnderground->ubNumElites--;
}
if( pUnderground->ubElitesInBattle )
{
pUnderground->ubElitesInBattle--;
}
}
break;
}
if( pSoldier->aiData.bNeutral || pSoldier->bTeam != ENEMY_TEAM && pSoldier->bTeam != CREATURE_TEAM )
return;
//we are recording an enemy death
if( pSoldier->ubGroupID )
{ //The enemy was in a mobile group
GROUP *pGroup;
pGroup = GetGroup( pSoldier->ubGroupID );
if( !pGroup )
{
#ifdef JA2BETAVERSION
CHAR16 str[256];
swprintf( str, L"Enemy soldier killed with ubGroupID of %d, and the group doesn't exist!", pSoldier->ubGroupID );
DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL );
#endif
return;
}
if( pGroup->fPlayer )
{
#ifdef JA2BETAVERSION
CHAR16 str[256];
swprintf( str, L"Attempting to process player group thinking it's an enemy group in ProcessQueenCmdImplicationsOfDeath()", pSoldier->ubGroupID );
DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL );
#endif
return;
}
switch( pSoldier->ubSoldierClass )
{
case SOLDIER_CLASS_ELITE:
#ifdef JA2BETAVERSION
if( !pGroup->pEnemyGroup->ubNumElites )
{
//swprintf( str, L"Enemy elite killed with ubGroupID of %d, but the group doesn't contain elites!", pGroup->ubGroupID );
//DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL );
break;
}
if( guiCurrentScreen == GAME_SCREEN )
{
if( pGroup->ubGroupSize <= iMaxEnemyGroupSize && pGroup->pEnemyGroup->ubNumElites != pGroup->pEnemyGroup->ubElitesInBattle && !gfPendingEnemies ||
pGroup->ubGroupSize > iMaxEnemyGroupSize /* || pGroup->pEnemyGroup->ubNumElites > 50 || pGroup->pEnemyGroup->ubElitesInBattle > 50*/ )
{
DoScreenIndependantMessageBox( L"Group elite 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
if( pGroup->pEnemyGroup->ubNumElites )
{
pGroup->pEnemyGroup->ubNumElites--;
}
if( pGroup->pEnemyGroup->ubElitesInBattle )
{
pGroup->pEnemyGroup->ubElitesInBattle--;
}
break;
case SOLDIER_CLASS_ARMY:
#ifdef JA2BETAVERSION
if( !pGroup->pEnemyGroup->ubNumTroops )
{
//swprintf( str, L"Enemy troop killed with ubGroupID of %d, but the group doesn't contain elites!", pGroup->ubGroupID );
//DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL );
break;
}
if( guiCurrentScreen == GAME_SCREEN )
{
if( pGroup->ubGroupSize <= iMaxEnemyGroupSize && pGroup->pEnemyGroup->ubNumTroops != pGroup->pEnemyGroup->ubTroopsInBattle && !gfPendingEnemies ||
pGroup->ubGroupSize > iMaxEnemyGroupSize /*|| pGroup->pEnemyGroup->ubNumTroops > 50 || pGroup->pEnemyGroup->ubTroopsInBattle > 50*/ )
{
// haydent
if (!is_client)
{
DoScreenIndependantMessageBox( L"Group troop 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
if( pGroup->pEnemyGroup->ubNumTroops )
{
pGroup->pEnemyGroup->ubNumTroops--;
}
if( pGroup->pEnemyGroup->ubTroopsInBattle )
{
pGroup->pEnemyGroup->ubTroopsInBattle--;
}
break;
case SOLDIER_CLASS_ADMINISTRATOR:
#ifdef JA2BETAVERSION
if( !pGroup->pEnemyGroup->ubNumAdmins )
{
//swprintf( str, L"Enemy administrator killed with ubGroupID of %d, but the group doesn't contain elites!", pGroup->ubGroupID );
//DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL );
break;
}
if( guiCurrentScreen == GAME_SCREEN )
{
if( pGroup->ubGroupSize <= iMaxEnemyGroupSize && pGroup->pEnemyGroup->ubNumAdmins != pGroup->pEnemyGroup->ubAdminsInBattle && !gfPendingEnemies ||
pGroup->ubGroupSize > iMaxEnemyGroupSize /*|| pGroup->pEnemyGroup->ubNumAdmins > 50 || pGroup->pEnemyGroup->ubAdminsInBattle > 50*/ )
{
DoScreenIndependantMessageBox( L"Group admin 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
if( pGroup->pEnemyGroup->ubNumAdmins )
{
pGroup->pEnemyGroup->ubNumAdmins--;
}
if( pGroup->pEnemyGroup->ubAdminsInBattle )
{
pGroup->pEnemyGroup->ubAdminsInBattle--;
}
break;
}
if( pGroup->ubGroupSize )
pGroup->ubGroupSize--;
RecalculateGroupWeight( pGroup );
if( !pGroup->ubGroupSize )
{
RemovePGroup( pGroup );
}
}
else
{ //The enemy was in a stationary defence group
if( !gbWorldSectorZ || IsAutoResolveActive() )
{ //ground level (SECTORINFO)
SECTORINFO *pSector;
#ifdef JA2BETAVERSION
UINT32 ubTotalEnemies;
#endif
// if( !IsAutoResolveActive() )
// {
pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ];
// }
// else
// {
// pSector = &SectorInfo[ GetAutoResolveSectorID() ];
// }
#ifdef JA2BETAVERSION
ubTotalEnemies = pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites;
#endif
switch( pSoldier->ubSoldierClass )
{
case SOLDIER_CLASS_ADMINISTRATOR:
#ifdef JA2BETAVERSION
if( guiCurrentScreen == GAME_SCREEN )
{
if( ubTotalEnemies <= gGameExternalOptions.ubGameMaximumNumberOfEnemies && pSector->ubNumAdmins != pSector->ubAdminsInBattle ||
!pSector->ubNumAdmins || !pSector->ubAdminsInBattle ||
pSector->ubNumAdmins > gGameExternalOptions.ubGameMaximumNumberOfEnemies || pSector->ubAdminsInBattle > gGameExternalOptions.ubGameMaximumNumberOfEnemies )
{
DoScreenIndependantMessageBox( L"Sector admin 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
if( pSector->ubNumAdmins )
{
pSector->ubNumAdmins--;
}
if( pSector->ubAdminsInBattle )
{
pSector->ubAdminsInBattle--;
}
break;
case SOLDIER_CLASS_ARMY:
#ifdef JA2BETAVERSION
if( guiCurrentScreen == GAME_SCREEN )
{
if( ubTotalEnemies <= gGameExternalOptions.ubGameMaximumNumberOfEnemies && pSector->ubNumTroops != pSector->ubTroopsInBattle ||
!pSector->ubNumTroops || !pSector->ubTroopsInBattle ||
pSector->ubNumTroops > gGameExternalOptions.ubGameMaximumNumberOfEnemies || pSector->ubTroopsInBattle > gGameExternalOptions.ubGameMaximumNumberOfEnemies )
{
if(!is_client)DoScreenIndependantMessageBox( L"Sector troop 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 );
//disabled: hayden.
}
}
#endif
if( pSector->ubNumTroops )
{
pSector->ubNumTroops--;
}
if( pSector->ubTroopsInBattle )
{
pSector->ubTroopsInBattle--;
}
break;
case SOLDIER_CLASS_ELITE:
#ifdef JA2BETAVERSION
if( guiCurrentScreen == GAME_SCREEN )
{
if( ubTotalEnemies <= gGameExternalOptions.ubGameMaximumNumberOfEnemies && pSector->ubNumElites != pSector->ubElitesInBattle ||
!pSector->ubNumElites || !pSector->ubElitesInBattle ||
pSector->ubNumElites > gGameExternalOptions.ubGameMaximumNumberOfEnemies || pSector->ubElitesInBattle > gGameExternalOptions.ubGameMaximumNumberOfEnemies )
{
DoScreenIndependantMessageBox( L"Sector elite 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
if( pSector->ubNumElites )
{
pSector->ubNumElites--;
}
if( pSector->ubElitesInBattle )
{
pSector->ubElitesInBattle--;
}
break;
case SOLDIER_CLASS_CREATURE:
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 )
{
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 );
}
}
#endif
if( pSector->ubNumCreatures )
{
pSector->ubNumCreatures--;
}
if( pSector->ubCreaturesInBattle )
{
pSector->ubCreaturesInBattle--;
}
}
else
{
if( pSector->bBloodCats )
{
pSector->bBloodCats--;
}
#ifdef JA2UB
//JA25 UB
//handle anything important when bloodcats die
HandleBloodCatDeaths( pSector );
#endif
}
break;
}
RecalculateSectorWeight( (UINT8)SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) );
}
else
{ //basement level (UNDERGROUND_SECTORINFO)
UNDERGROUND_SECTORINFO *pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
#ifdef JA2BETAVERSION
UINT32 ubTotalEnemies = pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites;
#endif
if( pSector )
{
switch( pSoldier->ubSoldierClass )
{
case SOLDIER_CLASS_ADMINISTRATOR:
#ifdef JA2BETAVERSION
if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumAdmins != pSector->ubAdminsInBattle ||
!pSector->ubNumAdmins || !pSector->ubAdminsInBattle ||
pSector->ubNumAdmins > 100 || pSector->ubAdminsInBattle > iMaxEnemyGroupSize )
{
DoScreenIndependantMessageBox( L"Underground sector admin 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
if( pSector->ubNumAdmins )
{
pSector->ubNumAdmins--;
}
if( pSector->ubAdminsInBattle )
{
pSector->ubAdminsInBattle--;
}
break;
case SOLDIER_CLASS_ARMY:
#ifdef JA2BETAVERSION
if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumTroops != pSector->ubTroopsInBattle ||
!pSector->ubNumTroops || !pSector->ubTroopsInBattle ||
pSector->ubNumTroops > 100 || pSector->ubTroopsInBattle > iMaxEnemyGroupSize )
{
DoScreenIndependantMessageBox( L"Underground sector troop 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
if( pSector->ubNumTroops )
{
pSector->ubNumTroops--;
}
if( pSector->ubTroopsInBattle )
{
pSector->ubTroopsInBattle--;
}
break;
case SOLDIER_CLASS_ELITE:
#ifdef JA2BETAVERSION
if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumElites != pSector->ubElitesInBattle ||
!pSector->ubNumElites || !pSector->ubElitesInBattle ||
pSector->ubNumElites > 100 || pSector->ubElitesInBattle > iMaxEnemyGroupSize )
{
DoScreenIndependantMessageBox( L"Underground sector elite 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
if( pSector->ubNumElites )
{
pSector->ubNumElites--;
}
if( pSector->ubElitesInBattle )
{
pSector->ubElitesInBattle--;
}
break;
case SOLDIER_CLASS_CREATURE:
if (pSoldier->ubBodyType == BLOODCAT)
{
if( pSector->ubNumBloodcats > 0 )
pSector->ubNumBloodcats--;
}
else
{
#ifdef JA2BETAVERSION
if( ubTotalEnemies <= (UINT32)iMaxEnemyGroupSize && pSector->ubNumCreatures != pSector->ubCreaturesInBattle ||
!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
if( pSector->ubNumCreatures )
{
pSector->ubNumCreatures--;
}
if( pSector->ubCreaturesInBattle )
{
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 )
{
//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;
}
}
}
}
if( !pSoldier->bSectorZ )
{
pSector = &SectorInfo[ SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ) ];
iNumEnemiesInSector = NumEnemiesInSector( pSoldier->sSectorX, pSoldier->sSectorY );
if( iNumEnemiesInSector )
{
if( pSector->bLastKnownEnemies >= 0 )
{
pSector->bLastKnownEnemies = (INT8)iNumEnemiesInSector;
}
}
else
{
pSector->bLastKnownEnemies = 0;
}
}
}
//Well, not so rarely with mobile reinforcements!
//Rarely, there will be more enemies than supported by the engine. In this case, these
//soldiers are waiting for a slot to be free so that they can enter the battle. This
//essentially allows for an infinite number of troops, though only gGameExternalOptions.ubGameMaximumNumberOfEnemies at a time can fight.
//This is also called whenever an enemy group's reinforcements arrive because the code is
//identical, though it is highly likely that they will all be successfully added on the first call.
void AddPossiblePendingEnemiesToBattle()
{
// Check if no world is loaded, and is not underground level
if(!(gWorldSectorX > 0 && gWorldSectorY > 0 && gbWorldSectorZ == 0))//dnl ch57 161009
return;
#ifdef JA2UB
BOOLEAN fMagicallyAppeared=FALSE;
#endif
UINT8 ubSlots, ubNumAvailable;
UINT8 ubNumElites, ubNumTroops, ubNumAdmins;
UINT8 ubNumGroupsInSector;
UINT8 ubGroupIndex;
GROUP *pGroup;
SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
GROUP **pGroupInSectorList;
static UINT8 ubPredefinedInsertionCode = 255;
// haydent
if (is_networked)
{
if ((is_client && !is_server) || gEnemyEnabled == 0)
{
return;
}
}
if( ( !PlayerMercsInSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, 0 ) && !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY ) )
|| !NumEnemiesInSector( gWorldSectorX, gWorldSectorY ) ) return;
ubSlots = NumFreeEnemySlots();
if( !ubSlots )
{ //no available slots to add enemies to. Try again later...
return;
}
if( !gfPendingEnemies )
{
//Optimization. No point in checking for group reinforcements if we know that there aren't any more enemies that can
//be added to this battle. This changes whenever a new enemy group arrives at the scene.
while (ubSlots)
{
UINT8 ubInsertionCode = 255;
if( gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition == TRUE )
ubInsertionCode = DoReinforcementAsPendingEnemy( gWorldSectorX, gWorldSectorY );
if (ubInsertionCode == 255)
{
break;
}
// Assume we added one since there are supposedly more available and room for them
ubSlots--;
}
// It's probable that the "pending enemies" flag isn't working right. So we'll go ahead and continue into
// the group insertion code from here.
//return;
}
if( pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins )
{
ubNumElites = ubNumTroops = ubNumAdmins = 0;
ubNumAvailable = pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins - pSector->ubElitesInBattle - pSector->ubTroopsInBattle - pSector->ubAdminsInBattle;
while( ubNumAvailable && ubSlots )
{
// So they just magically appear out of nowhere from the edge?
//This group has enemies waiting for a chance to enter the battle.
if( pSector->ubElitesInBattle < pSector->ubNumElites )
{ //Add an elite troop
pSector->ubElitesInBattle++;
ubNumAvailable--;
ubSlots--;
ubNumElites++;
}
else if( pSector->ubTroopsInBattle < pSector->ubNumTroops )
{ //Add a regular troop.
pSector->ubTroopsInBattle++;
ubNumAvailable--;
ubSlots--;
ubNumTroops++;
}
else if( pSector->ubAdminsInBattle < pSector->ubNumAdmins )
{ //Add an elite troop
pSector->ubAdminsInBattle++;
ubNumAvailable--;
ubSlots--;
ubNumAdmins++;
}
else
{
AssertMsg( 0, "AddPossiblePendingEnemiesToBattle(): Logic Error -- by Kris" );
}
}
if( ubNumAdmins || ubNumTroops || ubNumElites )
{ //This group has contributed forces, then add them now, because different
//groups appear on different sides of the map.
UINT8 ubStrategicInsertionCode = ubPredefinedInsertionCode;
//First, determine which entrypoint to use, based on the travel direction of the group.
if( ubStrategicInsertionCode == 255 )
{
if( NumEnemiesInSector( gWorldSectorX + 1, gWorldSectorY ) )
ubStrategicInsertionCode = INSERTION_CODE_EAST;
else if( NumEnemiesInSector( gWorldSectorX - 1, gWorldSectorY ) )
ubStrategicInsertionCode = INSERTION_CODE_WEST;
else if( NumEnemiesInSector( gWorldSectorX, gWorldSectorY + 1 ) )
ubStrategicInsertionCode = INSERTION_CODE_SOUTH;
else if( NumEnemiesInSector( gWorldSectorX, gWorldSectorY - 1 ) )
ubStrategicInsertionCode = INSERTION_CODE_NORTH;
#ifdef JA2UB
else if( gsGridNoForMapEdgePointInfo != -1 )
{
//Ja25: it doesnt matter the entry point at this point, it will become GRIDNO at a later point
ubStrategicInsertionCode = INSERTION_CODE_NORTH;
fMagicallyAppeared = FALSE;
}
#endif
}
if( ubStrategicInsertionCode == 255 )
{
if( !CountAllMilitiaInSector( gWorldSectorX + 1, gWorldSectorY ) )
ubStrategicInsertionCode = INSERTION_CODE_EAST;
else if( !CountAllMilitiaInSector( gWorldSectorX - 1, gWorldSectorY ) )
ubStrategicInsertionCode = INSERTION_CODE_WEST;
else if( !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY + 1 ) )
ubStrategicInsertionCode = INSERTION_CODE_SOUTH;
else if( !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY - 1 ) )
ubStrategicInsertionCode = INSERTION_CODE_NORTH;
}
if( ubStrategicInsertionCode < INSERTION_CODE_NORTH ||ubStrategicInsertionCode > INSERTION_CODE_WEST )
ubStrategicInsertionCode = INSERTION_CODE_NORTH + Random( 4 );
AddEnemiesToBattle( 0, ubStrategicInsertionCode, ubNumAdmins, ubNumTroops, ubNumElites, FALSE );
gfPendingEnemies = TRUE;
}
}
ubPredefinedInsertionCode = 255;
// Figure out which groups are in the sector, so we can have reinforcements arrive at random
for (ubNumGroupsInSector = 0, pGroup = gpGroupList; pGroup; pGroup = pGroup->next)
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ )
ubNumGroupsInSector++;
}
pGroupInSectorList = (GROUP**) MemAlloc( ubNumGroupsInSector * sizeof( GROUP*));
for (ubNumGroupsInSector = 0, pGroup = gpGroupList; pGroup; pGroup = pGroup->next)
{
if( !pGroup->fPlayer && !pGroup->fVehicle && pGroup->ubSectorX == gWorldSectorX && pGroup->ubSectorY == gWorldSectorY && !gbWorldSectorZ )
{
pGroupInSectorList[ ubNumGroupsInSector++] = pGroup;
}
}
while( ubSlots && ubNumGroupsInSector )
{
UINT8 ubInsertionCode = 255;
ubGroupIndex = Random( ubNumGroupsInSector);
pGroup = pGroupInSectorList[ ubGroupIndex];
ubNumAvailable = pGroup->ubGroupSize - pGroup->pEnemyGroup->ubElitesInBattle - pGroup->pEnemyGroup->ubTroopsInBattle - pGroup->pEnemyGroup->ubAdminsInBattle;
if (!ubNumAvailable)
{
// Looks like we picked an empty group. Make a note of it
pGroupInSectorList[ ubGroupIndex] = pGroupInSectorList[ --ubNumGroupsInSector ];
continue;
}
if( pGroup->ubPrevX && pGroup->ubPrevY )
{
if( pGroup->ubSectorX < pGroup->ubPrevX )
ubInsertionCode = INSERTION_CODE_EAST;
else if( pGroup->ubSectorX > pGroup->ubPrevX )
ubInsertionCode = INSERTION_CODE_WEST;
else if( pGroup->ubSectorY < pGroup->ubPrevY )
ubInsertionCode = INSERTION_CODE_SOUTH;
else if( pGroup->ubSectorY > pGroup->ubPrevY )
ubInsertionCode = INSERTION_CODE_NORTH;
else
{
// WANNE: Hack: If no valid insertion is found, get random insertion instead of Assert() error
UINT32 rndInsertionCode = GetRndNum(3);
ubInsertionCode = rndInsertionCode;
//Assert(0);
}
}
else if( pGroup->ubNextX && pGroup->ubNextY )
{
if( pGroup->ubSectorX < pGroup->ubNextX )
ubInsertionCode = INSERTION_CODE_EAST;
else if( pGroup->ubSectorX > pGroup->ubNextX )
ubInsertionCode = INSERTION_CODE_WEST;
else if( pGroup->ubSectorY < pGroup->ubNextY )
ubInsertionCode = INSERTION_CODE_SOUTH;
else if( pGroup->ubSectorY > pGroup->ubNextY )
ubInsertionCode = INSERTION_CODE_NORTH;
else
{
// WANNE: Hack: If no valid insertion is found, get random insertion instead of Assert() error
UINT32 rndInsertionCode = GetRndNum(3);
ubInsertionCode = rndInsertionCode;
//Assert(0);
}
}
else
{
// The group has no movement orders. Where did it come from?
Assert(0);
}
if( pGroup->pEnemyGroup->ubElitesInBattle < pGroup->pEnemyGroup->ubNumElites )
{ //Add an elite troop
pGroup->pEnemyGroup->ubElitesInBattle++;
ubSlots--;
AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 0, 1, FALSE );
}
else if( pGroup->pEnemyGroup->ubTroopsInBattle < pGroup->pEnemyGroup->ubNumTroops )
{ //Add a regular troop.
pGroup->pEnemyGroup->ubTroopsInBattle++;
ubSlots--;
AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 1, 0, FALSE );
}
else if( pGroup->pEnemyGroup->ubAdminsInBattle < pGroup->pEnemyGroup->ubNumAdmins )
{ //Add an admin troop
pGroup->pEnemyGroup->ubAdminsInBattle++;
ubSlots--;
AddEnemiesToBattle( pGroup, ubInsertionCode, 1, 0, 0, FALSE );
}
else
{
// WANNE: Instead of asserting, just add a regular troop
//AssertMsg( 0, "AddPossiblePendingEnemiesToBattle(): Logic Error -- by Kris" );
pGroup->pEnemyGroup->ubTroopsInBattle++;
ubSlots--;
AddEnemiesToBattle( pGroup, ubInsertionCode, 0, 1, 0, FALSE );
}
}
MemFree( pGroupInSectorList);
if( ubSlots )
{ //After going through the process, we have finished with some free slots and no more enemies to add.
//So, we can turn off the flag, as this check is no longer needed.
gfPendingEnemies = FALSE;
}
}
void NotifyPlayersOfNewEnemies()
{
INT32 iSoldiers, iChosenSoldier, i;
SOLDIERTYPE *pSoldier;
BOOLEAN fIgnoreBreath = FALSE;
iSoldiers = 0;
for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ )
{ //find a merc that is aware.
pSoldier = MercPtrs[ i ];
if( pSoldier->bInSector && pSoldier->bActive && pSoldier->stats.bLife >= OKLIFE && pSoldier->bBreath >= OKBREATH )
{
iSoldiers++;
}
}
if( !iSoldiers )
{ // look for an out of breath merc.
fIgnoreBreath = TRUE;
for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ )
{ //find a merc that is aware.
pSoldier = MercPtrs[ i ];
if( pSoldier->bInSector && pSoldier->bActive && pSoldier->stats.bLife >= OKLIFE )
{
iSoldiers++;
}
}
}
if( iSoldiers )
{
iChosenSoldier = Random( iSoldiers );
for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ )
{ //find a merc that is aware.
pSoldier = MercPtrs[ i ];
if( pSoldier->bInSector && pSoldier->bActive && pSoldier->stats.bLife >= OKLIFE &&
( ( pSoldier->bBreath >= OKBREATH ) || fIgnoreBreath ) )
{
if( !iChosenSoldier )
{
// ATE: This is to allow special handling of initial heli drop
if ( !DidGameJustStart() )
{
TacticalCharacterDialogueWithSpecialEvent( pSoldier, QUOTE_ENEMY_PRESENCE, 0, 0, 0 );
}
return;
}
iChosenSoldier--;
}
}
}
else
{ //There is either nobody here or our mercs can't talk
}
}
void AddEnemiesToBattle( GROUP *pGroup, UINT8 ubStrategicInsertionCode, UINT8 ubNumAdmins, UINT8 ubNumTroops, UINT8 ubNumElites, BOOLEAN fMagicallyAppeared )
{
SOLDIERTYPE *pSoldier;
MAPEDGEPOINTINFO MapEdgepointInfo;
UINT8 ubCurrSlot;
UINT8 ubTotalSoldiers;
UINT8 bDesiredDirection=0;
#ifdef JA2UB
UINT8 ubCnt;
#endif
switch( ubStrategicInsertionCode )
{
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;
default: AssertMsg( 0, "Illegal direction passed to AddEnemiesToBattle()" ); break;
}
#ifdef JA2TESTVERSION
ScreenMsg( FONT_RED, MSG_INTERFACE, L"Enemy reinforcements have arrived! (%d admins, %d troops, %d elite)", ubNumAdmins, ubNumTroops, ubNumElites );
#endif
if( fMagicallyAppeared )
{ //update the strategic counters
if( !gbWorldSectorZ )
{
SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
pSector->ubNumAdmins += ubNumAdmins;
pSector->ubAdminsInBattle += ubNumAdmins;
pSector->ubNumTroops += ubNumTroops;
pSector->ubTroopsInBattle += ubNumTroops;
pSector->ubNumElites += ubNumElites;
pSector->ubElitesInBattle += ubNumElites;
}
else
{
UNDERGROUND_SECTORINFO *pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
if( pSector )
{
pSector->ubNumAdmins += ubNumAdmins;
pSector->ubAdminsInBattle += ubNumAdmins;
pSector->ubNumTroops += ubNumTroops;
pSector->ubTroopsInBattle += ubNumTroops;
pSector->ubNumElites += ubNumElites;
pSector->ubElitesInBattle += ubNumElites;
}
}
//Because the enemies magically appeared, have one of our soldiers say something...
NotifyPlayersOfNewEnemies();
}
ubTotalSoldiers = ubNumAdmins + ubNumTroops + ubNumElites;
#ifdef JA2UB
if( gsGridNoForMapEdgePointInfo != -1 )
{
ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
}
#endif
#ifdef JA2UB
if( ubStrategicInsertionCode == INSERTION_CODE_GRIDNO )
{
if( gsGridNoForMapEdgePointInfo == -1 )
{
Assert( 0 );
gsGridNoForMapEdgePointInfo=0;
}
for( ubCnt=0; ubCnt<32;ubCnt++)
{
MapEdgepointInfo.sGridNo[ ubCnt ] = gsGridNoForMapEdgePointInfo;
}
MapEdgepointInfo.ubNumPoints = 32;
MapEdgepointInfo.ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
}
else
{
ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, (UINT8)(ubNumAdmins+ubNumElites+ubNumTroops) );
}
#else
ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, (UINT8)(ubNumAdmins+ubNumElites+ubNumTroops) );
#endif
ubCurrSlot = 0;
while( ubTotalSoldiers )
{
if( ubNumElites && Random( ubTotalSoldiers ) < ubNumElites )
{
ubNumElites--;
ubTotalSoldiers--;
Assert(pSoldier = TacticalCreateEliteEnemy());
if( pGroup )
{
pSoldier->ubGroupID = pGroup->ubGroupID;
}
pSoldier->ubInsertionDirection = bDesiredDirection;
//Setup the position
if( ubCurrSlot < MapEdgepointInfo.ubNumPoints )
{ //using an edgepoint
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ];
}
else
{ //no edgepoints left, so put him at the entrypoint.
pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode;
}
UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 );
}
else if( ubNumTroops && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumTroops) )
{
ubNumTroops--;
ubTotalSoldiers--;
Assert(pSoldier = TacticalCreateArmyTroop());
if( pGroup )
{
pSoldier->ubGroupID = pGroup->ubGroupID;
}
pSoldier->ubInsertionDirection = bDesiredDirection;
//Setup the position
if( ubCurrSlot < MapEdgepointInfo.ubNumPoints )
{ //using an edgepoint
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ];
}
else
{ //no edgepoints left, so put him at the entrypoint.
pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode;
}
UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 );
}
else if( ubNumAdmins && (UINT8)Random( ubTotalSoldiers ) < (UINT8)(ubNumElites + ubNumTroops + ubNumAdmins) )
{
ubNumAdmins--;
ubTotalSoldiers--;
Assert(pSoldier = TacticalCreateAdministrator());
if( pGroup )
{
pSoldier->ubGroupID = pGroup->ubGroupID;
}
pSoldier->ubInsertionDirection = bDesiredDirection;
//Setup the position
if( ubCurrSlot < MapEdgepointInfo.ubNumPoints )
{ //using an edgepoint
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
pSoldier->usStrategicInsertionData = MapEdgepointInfo.sGridNo[ ubCurrSlot++ ];
}
else
{ //no edgepoints left, so put him at the entrypoint.
pSoldier->ubStrategicInsertionCode = ubStrategicInsertionCode;
}
UpdateMercInSector( pSoldier, gWorldSectorX, gWorldSectorY, 0 );
}
// HEADROCK HAM 3.2: enemy reinforcements arrive with 0 APs.
if (gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 1 || gGameExternalOptions.ubReinforcementsFirstTurnFreeze == 2)
{
pSoldier->bActionPoints = 0;
}
}
#ifdef JA2UB
gsGridNoForMapEdgePointInfo = -1;
#endif
}
BOOLEAN SaveUnderGroundSectorInfoToSaveGame( HWFILE hFile )
{
UINT32 uiNumBytesWritten;
UINT32 uiNumOfRecords=0;
UNDERGROUND_SECTORINFO *TempNode = gpUndergroundSectorInfoHead;
//Loop through all the nodes to count how many there are
while( TempNode )
{
uiNumOfRecords++;
TempNode = TempNode->next;
}
//Write how many nodes there are
FileWrite( hFile, &uiNumOfRecords, sizeof( UINT32 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT32 ) )
{
return(FALSE);
}
TempNode = gpUndergroundSectorInfoHead;
//Go through each node and save it.
while( TempNode )
{
FileWrite( hFile, TempNode, sizeof( UNDERGROUND_SECTORINFO ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UNDERGROUND_SECTORINFO ) )
{
return(FALSE);
}
TempNode = TempNode->next;
}
return( TRUE );
}
BOOLEAN LoadUnderGroundSectorInfoFromSavedGame( HWFILE hFile )
{
UINT32 uiNumBytesRead;
UINT32 uiNumOfRecords=0;
UINT32 cnt=0;
UNDERGROUND_SECTORINFO *TempNode = NULL;
UNDERGROUND_SECTORINFO *TempSpot = NULL;
//Clear the current LL
TrashUndergroundSectorInfo();
//Read in the number of nodes stored
FileRead( hFile, &uiNumOfRecords, sizeof( UINT32 ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT32 ) )
{
return(FALSE);
}
for( cnt = 0; cnt< uiNumOfRecords; cnt ++)
{
//Malloc space for the new node
TempNode = (UNDERGROUND_SECTORINFO *) MemAlloc( sizeof( UNDERGROUND_SECTORINFO ) );
if( TempNode == NULL )
return( FALSE );
//read in the new node
FileRead( hFile, TempNode, sizeof( UNDERGROUND_SECTORINFO ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UNDERGROUND_SECTORINFO ) )
{
return(FALSE);
}
//If its the first time in, assign the node to the list
if( cnt == 0 )
{
gpUndergroundSectorInfoHead = TempNode;
TempSpot = gpUndergroundSectorInfoHead;
TempSpot->next = NULL;
}
else
{
//assign the new node to the LL
TempSpot->next = TempNode;
//advance to the next node
TempSpot = TempSpot->next;
TempSpot->next = NULL;
gpUndergroundSectorInfoTail = TempSpot;
}
}
#ifdef JA2BETAVERSION
if( !uiNumOfRecords )
{
BuildUndergroundSectorInfoList();
gfClearCreatureQuest = TRUE;
}
#endif
return( TRUE );
}
UNDERGROUND_SECTORINFO* FindUnderGroundSector( INT16 sMapX, INT16 sMapY, UINT8 bMapZ )
{
UNDERGROUND_SECTORINFO *pUnderground;
pUnderground = gpUndergroundSectorInfoHead;
//Loop through all the underground sectors looking for specified sector
while( pUnderground )
{
//If the sector is the right one
if( pUnderground->ubSectorX == sMapX &&
pUnderground->ubSectorY == sMapY &&
pUnderground->ubSectorZ == bMapZ )
{
return( pUnderground );
}
pUnderground = pUnderground->next;
}
return( NULL );
}
void BeginCaptureSquence( )
{
if( !( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE ) || !( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE ) )
{
gStrategicStatus.ubNumCapturedForRescue = 0;
}
}
void EndCaptureSequence( )
{
#ifdef JA2UB
// no UB
#else
// Set flag...
if( !( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE ) || !(gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE) )
{
// CJC Dec 1 2002: fixing multiple captures:
//gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE;
if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED )
{
// CJC Dec 1 2002: fixing multiple captures:
gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE;
StartQuest( QUEST_HELD_IN_ALMA, gWorldSectorX, gWorldSectorY );
}
// CJC Dec 1 2002: fixing multiple captures:
//else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE )
else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE && gubQuest[ QUEST_INTERROGATION ] == QUESTNOTSTARTED )
{
StartQuest( QUEST_INTERROGATION, gWorldSectorX, gWorldSectorY );
// CJC Dec 1 2002: fixing multiple captures:
gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE;
// OK! - Schedule Meanwhile now!
{
MEANWHILE_DEFINITION MeanwhileDef;
MeanwhileDef.sSectorX = 7;
MeanwhileDef.sSectorY = 14;
MeanwhileDef.ubNPCNumber = QUEEN;
MeanwhileDef.usTriggerEvent = 0;
MeanwhileDef.ubMeanwhileID = INTERROGATION;
ScheduleMeanwhileEvent( &MeanwhileDef, 10 );
}
}
// CJC Dec 1 2002: fixing multiple captures
else
{
// !?!? set both flags
gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE;
gStrategicStatus.uiFlags |= STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE;
}
}
#endif
}
void EnemyCapturesPlayerSoldier( SOLDIERTYPE *pSoldier )
{
UINT32 i;
BOOLEAN fMadeCorpse;
INT32 iNumEnemiesInSector;
// TODO.WANNE: Hardcoded grid number
static INT32 sAlmaCaptureGridNos[] = { 9208, 9688, 9215 }; //dnl!!!
static INT32 sAlmaCaptureItemsGridNo[] = { 12246, 12406, 13046 }; //dnl!!!
static INT32 sInterrogationItemGridNo[] = { 12089, 12089, 12089 }; //dnl!!!
AssertNotNIL(pSoldier);
// ATE: Check first if ! in player captured sequence already
// CJC Dec 1 2002: fixing multiple captures
if ( ( gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_RESCUE ) && (gStrategicStatus.uiFlags & STRATEGIC_PLAYER_CAPTURED_FOR_ESCAPE) )
{
return;
}
// ATE: If maximum prisoners captured, return!
if ( gStrategicStatus.ubNumCapturedForRescue > 3 )
{
return;
}
// If this is an EPC , just kill them...
if ( AM_AN_EPC( pSoldier ) )
{
pSoldier->stats.bLife = 0;
pSoldier->iHealableInjury = 0; // added by SANDRO
HandleSoldierDeath( pSoldier, &fMadeCorpse );
return;
}
if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE )
{
return;
}
// ATE: Patch fix If in a vehicle, remove from vehicle...
TakeSoldierOutOfVehicle( pSoldier );
// Are there anemies in ALMA? ( I13 )
iNumEnemiesInSector = NumEnemiesInSector( 13, 9 );
// IF there are no enemies, and we need to do alma, skip!
if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED && iNumEnemiesInSector == 0 )
{
InternalStartQuest( QUEST_HELD_IN_ALMA, gWorldSectorX, gWorldSectorY, FALSE );
InternalEndQuest( QUEST_HELD_IN_ALMA, gWorldSectorX, gWorldSectorY, FALSE );
}
HandleMoraleEvent( pSoldier, MORALE_MERC_CAPTURED, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
// Change to POW....
//-add him to a POW assignment/group
if( ( pSoldier->bAssignment != ASSIGNMENT_POW ) )
{
SetTimeOfAssignmentChangeForMerc( pSoldier );
}
ChangeSoldiersAssignment( pSoldier, ASSIGNMENT_POW );
// ATE: Make them neutral!
if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED )
{
pSoldier->aiData.bNeutral = TRUE;
}
RemoveCharacterFromSquads( pSoldier );
WORLDITEM WorldItem;
// Is this the first one..?
if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTNOTSTARTED )
{
//-teleport him to NE Alma sector (not Tixa as originally planned)
pSoldier->sSectorX = 13;
pSoldier->sSectorY = 9;
pSoldier->bSectorZ = 0;
// put him on the floor!!
pSoldier->pathing.bLevel = 0;
// OK, drop all items!
for ( i = 0; i < pSoldier->inv.size(); i++ )
{
if( pSoldier->inv[ i ].exists() == true )
{
WorldItem.fExists = TRUE;
WorldItem.sGridNo = sAlmaCaptureItemsGridNo[ gStrategicStatus.ubNumCapturedForRescue ];
WorldItem.ubLevel = 0;
WorldItem.usFlags = 0;
WorldItem.bVisible = FALSE;
WorldItem.bRenderZHeightAboveLevel = 0;
pSoldier->inv[ i ].MoveThisObjectTo(WorldItem.object);
AddWorldItemsToUnLoadedSector( 13, 9, 0, sAlmaCaptureItemsGridNo[ gStrategicStatus.ubNumCapturedForRescue ], 1, &WorldItem, FALSE );
}
}
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
pSoldier->usStrategicInsertionData = sAlmaCaptureGridNos[ gStrategicStatus.ubNumCapturedForRescue ];
gStrategicStatus.ubNumCapturedForRescue++;
}
else if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTDONE )
{
//-teleport him to N7
pSoldier->sSectorX = 7;
pSoldier->sSectorY = 14;
pSoldier->bSectorZ = 0;
// put him on the floor!!
pSoldier->pathing.bLevel = 0;
// OK, drop all items!
for ( i = 0; i < pSoldier->inv.size(); i++ )
{
if( pSoldier->inv[ i ].exists() == true )
{
WorldItem.fExists = TRUE;
WorldItem.sGridNo = sInterrogationItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ];
WorldItem.ubLevel = 0;
WorldItem.usFlags = 0;
WorldItem.bVisible = FALSE;
WorldItem.bRenderZHeightAboveLevel = 0;
pSoldier->inv[ i ].MoveThisObjectTo(WorldItem.object);
AddWorldItemsToUnLoadedSector( 7, 14, 0, sInterrogationItemGridNo[ gStrategicStatus.ubNumCapturedForRescue ], 1, &WorldItem, FALSE );
}
}
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
pSoldier->usStrategicInsertionData = gsInterrogationGridNo[ gStrategicStatus.ubNumCapturedForRescue ];
gStrategicStatus.ubNumCapturedForRescue++;
}
//Bandaging him would prevent him from dying (due to low HP)
pSoldier->bBleeding = 0;
// wake him up
if ( pSoldier->flags.fMercAsleep )
{
PutMercInAwakeState( pSoldier );
pSoldier->flags.fForcedToStayAwake = FALSE;
}
//Set his life to 50% + or - 10 HP.
pSoldier->stats.bLife = pSoldier->stats.bLifeMax / 2;
if ( pSoldier->stats.bLife <= 35 )
{
pSoldier->stats.bLife = 35;
}
else if ( pSoldier->stats.bLife >= 45 )
{
pSoldier->stats.bLife += (INT8)(10 - Random( 21 ) );
}
// SANDRO - make the lost life insta-healable
pSoldier->iHealableInjury = ((pSoldier->stats.bLifeMax - pSoldier->stats.bLife) * 100);
// make him quite exhausted when found
pSoldier->bBreath = pSoldier->bBreathMax = 50;
pSoldier->sBreathRed = 0;
pSoldier->flags.fMercCollapsedFlag = FALSE;
}
void HandleEnemyStatusInCurrentMapBeforeLoadingNewMap()
{
INT32 i;
BOOLEAN fMadeCorpse;
INT8 bKilledEnemies = 0, bKilledCreatures = 0, bKilledRebels = 0, bKilledCivilians = 0;
return;
//If any of the soldiers are dying, kill them now.
for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; i++ )
{
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife )
{
MercPtrs[ i ]->stats.bLife = 0;
MercPtrs[ i ]->iHealableInjury = 0; // added just for sure - SANDRO
HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse );
bKilledEnemies++;
}
}
//Do the same for the creatures.
for( i = gTacticalStatus.Team[ CREATURE_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ )
{
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife )
{
MercPtrs[ i ]->stats.bLife = 0;
HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse );
bKilledCreatures++;
}
}
//Militia
for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ )
{
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife )
{
MercPtrs[ i ]->stats.bLife = 0;
MercPtrs[ i ]->iHealableInjury = 0; // added just for sure - SANDRO
HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse );
bKilledRebels++;
}
}
//Civilians
for( i = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; i++ )
{
if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife < OKLIFE && MercPtrs[ i ]->stats.bLife )
{
MercPtrs[ i ]->stats.bLife = 0;
MercPtrs[ i ]->iHealableInjury = 0; // added just for sure - SANDRO
HandleSoldierDeath( MercPtrs[ i ], &fMadeCorpse );
bKilledCivilians++;
}
}
// TEST MESSAGES ONLY!
if( bKilledCivilians )
ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d civilians died after you left the sector.", bKilledCivilians );
if( bKilledRebels )
ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d militia died after you left the sector.", bKilledRebels );
if( bKilledEnemies )
ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d enemies died after you left the sector.", bKilledEnemies );
if( bKilledCreatures )
ScreenMsg( FONT_BLUE, MSG_TESTVERSION, L"%d creatures died after you left the sector.", bKilledCreatures );
if( !gbWorldSectorZ )
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR(gWorldSectorX, gWorldSectorY) ];
pSector->ubAdminsInBattle = 0;
pSector->ubTroopsInBattle = 0;
pSector->ubElitesInBattle = 0;
pSector->ubCreaturesInBattle = 0;
//RecalculateSectorWeight(
}
else if( gbWorldSectorZ > 0 )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
if( !pSector )
return;
pSector->ubAdminsInBattle = 0;
pSector->ubTroopsInBattle = 0;
pSector->ubElitesInBattle = 0;
pSector->ubCreaturesInBattle = 0;
}
}
BOOLEAN PlayerSectorDefended( UINT8 ubSectorID )
{
SECTORINFO *pSector;
pSector = &SectorInfo[ ubSectorID ];
if( pSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] +
pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] +
pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] )
{ //militia in sector
return TRUE;
}
if( FindMovementGroupInSector( (UINT8)SECTORX( ubSectorID ), (UINT8)SECTORY( ubSectorID ), TRUE ) )
{ //player in sector
return TRUE;
}
return FALSE;
}
//Assumes gTacticalStatus.fEnemyInSector
BOOLEAN OnlyHostileCivsInSector()
{
SOLDIERTYPE *pSoldier;
INT32 i;
BOOLEAN fHostileCivs = FALSE;
//Look for any hostile civs.
for( i = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife )
{
if( !pSoldier->aiData.bNeutral )
{
fHostileCivs = TRUE;
break;
}
}
}
if( !fHostileCivs )
{ //No hostile civs, so return FALSE
return FALSE;
}
//Look for anybody else hostile. If found, return FALSE immediately.
for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife )
{
if( !pSoldier->aiData.bNeutral )
{
return FALSE;
}
}
}
for( 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->aiData.bNeutral )
{
return FALSE;
}
}
}
for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
if( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife )
{
if( !pSoldier->aiData.bNeutral )
{
return FALSE;
}
}
}
//We only have hostile civilians, don't allow time compression.
return TRUE;
}
BOOLEAN CheckPendingEnemies()
{
if( gbWorldSectorZ ) return FALSE;
SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
if( (pSector->ubNumElites + pSector->ubNumTroops + pSector->ubNumAdmins) > ( pSector->ubElitesInBattle + pSector->ubTroopsInBattle + pSector->ubAdminsInBattle) )
return TRUE;
for (GROUP *pGroup = gpGroupList; pGroup; pGroup = pGroup->next)
{
if( !pGroup->fPlayer
&& !pGroup->fVehicle
&& pGroup->ubSectorX == gWorldSectorX
&& pGroup->ubSectorY == gWorldSectorY
&& pGroup->ubGroupSize > pGroup->pEnemyGroup->ubElitesInBattle + pGroup->pEnemyGroup->ubTroopsInBattle + pGroup->pEnemyGroup->ubAdminsInBattle)
return TRUE;
}
return FALSE;
}
#ifdef JA2UB
void HandleBloodCatDeaths( SECTORINFO *pSector )
{
//if the current sector is the first part of the town
if( gWorldSectorX == 10 && gWorldSectorY == 9 && gbWorldSectorZ == 0 )
{
//if ALL the bloodcats are killed
if( pSector->bBloodCats == 0 )
{
UINT8 bId1=-1;
UINT8 bId2=-1;
UINT8 bNum=0;
SetFactTrue( FACT_PLAYER_KILLED_ALL_BETTYS_BLOODCATS );
//Instantly have betties missing items show up
//DailyCheckOnItemQuantities( TRUE );
// Now have a merc say the killed bloodcat quote
bNum = Get3RandomQualifiedMercs( &bId1, &bId2, NULL );
//if there are some qualified mercs
if( bNum != 0 )
{
//must make sure TEX doesnt say the quote
if( bId1 != NOBODY && Menptr[ bId1 ].ubProfile != 64 )
{
TacticalCharacterDialogue( &Menptr[ bId1 ], QUOTE_RENEW_REFUSAL_DUE_TO_LACK_OF_FUNDS );
}
else if( bId2 != NOBODY && Menptr[ bId2 ].ubProfile != 64 )
{
TacticalCharacterDialogue( &Menptr[ bId2 ], QUOTE_RENEW_REFUSAL_DUE_TO_LACK_OF_FUNDS );
}
}
}
}
}
#endif