Files
source/Tactical/Enemy Soldier Save.cpp
T
Overhaul 7a27e7105d New attack busy system
Added missing #includes

Added LUA scripting and console

Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain

Fixed signedness of many grid variables used in GetMouseMapPos calls

Fixed enemy weapon choosing:  Sometimes a mortar is chosen but later rejected, but the grenade class was not reset.  Caused assertion failure

Added checks so enemies don't try to chuck RPG grenades with their hands

Now possible to shoot a someone in the head if he's in water

Fixed InitSightArrays to also clear soldier interrupt duel points.  This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called.

Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have

Removed early setting of muzzle flash.  This would allow enemies to get an interrupt before you even fired.

Fixed item dropping by AI.  If AI tried to drop something while standing it would cause deadlock

Change to greatly speed up closing the sector inventory window in an unloaded sector

Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting


Check builddefines.h for the conditional flags.  Of greatest interest might be LUA_CONSOLE.  This will cause the game to bring up a command console when run.  However this console is severely lacking in many areas.  If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated.  The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus.

LUA scripting is very limited, basically just proof of concept.  There is one global variable, the Soldiers array.  An array index gives you the soldier in that slot in the currently loaded sector.  The soldier has a few things that can be accessed:  name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid)


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2007-06-09 09:01:04 +00:00

2179 lines
71 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#include "Enemy Soldier Save.h"
#else
#include "builddefines.h"
#include <stdio.h>
#include <memory.h>
#include "types.h"
#include "strategicmap.h"
#include "overhead.h"
#include "isometric utils.h"
#include "soldier add.h"
#include "soldier create.h"
#include "Soldier Init List.h"
#include "debug.h"
#include "Random.h"
#include "items.h"
#include "cursors.h"
#include "Map Information.h"
#include "soldier profile.h"
#include "Sys Globals.h"
#include "EditorMercs.h"
#include "Animation Data.h"
#include "message.h"
#include "Font Control.h"
#include "Campaign Types.h"
#include "Tactical Save.h"
#include "Game Clock.h"
#include "Queen Command.h"
#include "Scheduling.h"
#endif
BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *pNode );
BOOLEAN CountNumberOfElitesRegularsAdminsAndCreaturesFromEnemySoldiersTempFile( UINT8 *pubNumElites, UINT8 *pubNumRegulars, UINT8 *pubNumAdmins, UINT8 *pubNumCreatures );
BOOLEAN gfRestoringEnemySoldiersFromTempFile = FALSE;
BOOLEAN gfRestoringCiviliansFromTempFile = FALSE;
void RemoveCivilianTempFile( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ );
void RemoveEnemySoldierTempFile( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
CHAR8 zMapName[ 128 ];
if( GetSectorFlagStatus( sSectorX, sSectorY, bSectorZ, SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS ) )
{
//Delete any temp file that is here and toast the flag that say's one exists.
ReSetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS );
// GetMapFileName( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, zTempName, FALSE );
//add the 'e' for 'Enemy preserved' to the front of the map name
// sprintf( zMapName, "%s\\e_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, zMapName, sSectorX, sSectorY, bSectorZ );
//Delete the temp file.
FileDelete( zMapName );
}
}
void RemoveCivilianTempFile( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
//CHAR8 zTempName[ 128 ];
CHAR8 zMapName[ 128 ];
if( GetSectorFlagStatus( sSectorX, sSectorY, bSectorZ, SF_CIV_PRESERVED_TEMP_FILE_EXISTS ) )
{
//Delete any temp file that is here and toast the flag that say's one exists.
ReSetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_CIV_PRESERVED_TEMP_FILE_EXISTS );
//GetMapFileName( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, zTempName, FALSE );
GetMapTempFileName( SF_CIV_PRESERVED_TEMP_FILE_EXISTS, zMapName, sSectorX, sSectorY, bSectorZ );
//Delete the temp file.
FileDelete( zMapName );
}
}
//OLD SAVE METHOD: This is the old way of loading the enemies and civilians
BOOLEAN LoadEnemySoldiersFromTempFile()
{
SOLDIERINITNODE *curr;
SOLDIERCREATE_STRUCT tempDetailedPlacement;
INT32 i;
INT32 slots = 0;
UINT32 uiNumBytesRead;
UINT32 uiTimeStamp;
HWFILE hfile;
INT16 sSectorX, sSectorY;
UINT16 usCheckSum, usFileCheckSum;
CHAR8 zMapName[ 128 ];
#ifdef JA2TESTVERSION
CHAR8 zReason[256];
#endif
INT8 bSectorZ;
UINT8 ubSectorID;
UINT8 ubNumElites = 0, ubNumTroops = 0, ubNumAdmins = 0, ubNumCreatures = 0;
UINT8 ubStrategicElites, ubStrategicTroops, ubStrategicAdmins, ubStrategicCreatures;
gfRestoringEnemySoldiersFromTempFile = TRUE;
//STEP ONE: Set up the temp file to read from.
//Convert the current sector location into a file name
// GetMapFileName( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, zTempName, FALSE );
//add the 'e' for 'Enemy preserved' to the front of the map name
// sprintf( zMapName, "%s\\e_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, zMapName, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
//Open the file for reading
hfile = FileOpen( zMapName, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
//STEP TWO: determine whether or not we should use this data.
//because it is the demo, it is automatically used.
FileRead( hfile, &sSectorY, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading sSectorY. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorY != sSectorY )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- sSectorY mismatch. KM" );
#endif
goto FAIL_LOAD;
}
LoadSoldierInitListLinks( hfile );
//STEP THREE: read the data
FileRead( hfile, &sSectorX, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading sSectorX. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorX != sSectorX )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- sSectorX mismatch. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &slots, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading slots. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &uiTimeStamp, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading uiTimeStamp. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &bSectorZ, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading bSectorZ. KM" );
#endif
goto FAIL_LOAD;
}
if( GetWorldTotalMin() > uiTimeStamp + 300 )
{ //the file has aged. Use the regular method for adding soldiers.
FileClose( hfile );
RemoveEnemySoldierTempFile( sSectorX, sSectorY, bSectorZ );
gfRestoringEnemySoldiersFromTempFile = FALSE;
return TRUE;
}
if( gbWorldSectorZ != bSectorZ )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- bSectorZ mismatch. KM" );
#endif
goto FAIL_LOAD;
}
if( !slots )
{ //no need to restore the enemy's to the map. This means we are restoring a saved game.
gfRestoringEnemySoldiersFromTempFile = FALSE;
FileClose( hfile );
return TRUE;
}
if( slots < 0 || slots >= 64 )
{ //bad IO!
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- illegal slot value of %d. KM", slots );
#endif
goto FAIL_LOAD;
}
//For all the enemy slots (enemy/creature), clear the fPriorityExistance flag. We will use these flags
//to determine which slots have been modified as we load the data into the map pristine soldier init list.
curr = gSoldierInitHead;
while( curr )
{
if( curr->pBasicPlacement->fPriorityExistance )
{
if( curr->pBasicPlacement->bTeam == ENEMY_TEAM || curr->pBasicPlacement->bTeam == CREATURE_TEAM || curr->pBasicPlacement->bTeam == CIV_TEAM )
{
curr->pBasicPlacement->fPriorityExistance = FALSE;
}
}
curr = curr->next;
}
//get the number of enemies in this sector.
if( bSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ );
if( !pSector )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- Couldn't find underground sector info for (%d,%d,%d) KM", sSectorX, sSectorY, bSectorZ );
#endif
goto FAIL_LOAD;
}
ubStrategicElites = pSector->ubNumElites;
ubStrategicTroops = pSector->ubNumTroops;
ubStrategicAdmins = pSector->ubNumAdmins;
ubStrategicCreatures = pSector->ubNumCreatures;
}
else
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
ubStrategicCreatures = pSector->ubNumCreatures;
GetNumberOfEnemiesInSector( sSectorX, sSectorY, &ubStrategicAdmins, &ubStrategicTroops, &ubStrategicElites );
}
for( i = 0; i < slots; i++ )
{
FileRead( hfile, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SOLDIERCREATE_STRUCT ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading tempDetailedPlacment %d. KM", i );
#endif
goto FAIL_LOAD;
}
curr = gSoldierInitHead;
while( curr )
{
if( !curr->pBasicPlacement->fPriorityExistance )
{
if( !curr->pDetailedPlacement || curr->pDetailedPlacement && curr->pDetailedPlacement->ubProfile == NO_PROFILE )
{
if( curr->pBasicPlacement->bTeam == tempDetailedPlacement.bTeam )
{
curr->pBasicPlacement->fPriorityExistance = TRUE;
if( !curr->pDetailedPlacement )
{ //need to upgrade the placement to detailed placement
curr->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
}
//now replace the map pristine placement info with the temp map file version..
memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ) );
curr->pBasicPlacement->fPriorityExistance = TRUE;
curr->pBasicPlacement->bDirection = curr->pDetailedPlacement->bDirection;
curr->pBasicPlacement->bOrders = curr->pDetailedPlacement->bOrders;
curr->pBasicPlacement->bAttitude = curr->pDetailedPlacement->bAttitude;
curr->pBasicPlacement->bBodyType = curr->pDetailedPlacement->bBodyType;
curr->pBasicPlacement->fOnRoof = curr->pDetailedPlacement->fOnRoof;
curr->pBasicPlacement->ubSoldierClass = curr->pDetailedPlacement->ubSoldierClass;
curr->pBasicPlacement->ubCivilianGroup = curr->pDetailedPlacement->ubCivilianGroup;
curr->pBasicPlacement->fHasKeys = curr->pDetailedPlacement->fHasKeys;
curr->pBasicPlacement->usStartingGridNo = curr->pDetailedPlacement->sInsertionGridNo;
curr->pBasicPlacement->bPatrolCnt = curr->pDetailedPlacement->bPatrolCnt;
memcpy( curr->pBasicPlacement->sPatrolGrid, curr->pDetailedPlacement->sPatrolGrid,
sizeof( INT16 ) * curr->pBasicPlacement->bPatrolCnt );
FileRead( hfile, &usCheckSum, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading usCheckSum %d. KM", i );
#endif
goto FAIL_LOAD;
}
//verify the checksum equation (anti-hack) -- see save
usFileCheckSum =
curr->pDetailedPlacement->bLife * 7 +
curr->pDetailedPlacement->bLifeMax * 8 -
curr->pDetailedPlacement->bAgility * 2 +
curr->pDetailedPlacement->bDexterity * 1 +
curr->pDetailedPlacement->bExpLevel * 5 -
curr->pDetailedPlacement->bMarksmanship * 9 +
curr->pDetailedPlacement->bMedical * 10 +
curr->pDetailedPlacement->bMechanical * 3 +
curr->pDetailedPlacement->bExplosive * 4 +
curr->pDetailedPlacement->bLeadership * 5 +
curr->pDetailedPlacement->bStrength * 7 +
curr->pDetailedPlacement->bWisdom * 11 +
curr->pDetailedPlacement->bMorale * 7 +
curr->pDetailedPlacement->bAIMorale * 3 -
curr->pDetailedPlacement->bBodyType * 7 +
4 * 6 +
curr->pDetailedPlacement->sSectorX * 7 -
curr->pDetailedPlacement->ubSoldierClass * 4 +
curr->pDetailedPlacement->bTeam * 7 +
curr->pDetailedPlacement->bDirection * 5 +
curr->pDetailedPlacement->fOnRoof * 17 +
curr->pDetailedPlacement->sInsertionGridNo * 1 +
3;
if( usCheckSum != usFileCheckSum )
{ //Hacker has modified the stats on the enemy placements.
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- checksum for placement %d failed. KM", i );
#endif
goto FAIL_LOAD;
}
if( curr->pBasicPlacement->bTeam == CIV_TEAM )
{
AddPlacementToWorld( curr );
break;
}
else
{
//Add preserved placements as long as they don't exceed the actual population.
switch( curr->pBasicPlacement->ubSoldierClass )
{
case SOLDIER_CLASS_ELITE:
ubNumElites++;
if( ubNumElites < ubStrategicElites )
{
AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_ARMY:
ubNumTroops++;
if( ubNumTroops < ubStrategicTroops )
{
AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_ADMINISTRATOR:
ubNumAdmins++;
if( ubNumAdmins < ubStrategicAdmins )
{
AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_CREATURE:
ubNumCreatures++;
if( ubNumCreatures < ubStrategicCreatures )
{
AddPlacementToWorld( curr );
}
break;
}
break;
}
}
}
}
curr = curr->next;
}
}
FileRead( hfile, &ubSectorID, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading ubSectorID. KM" );
#endif
goto FAIL_LOAD;
}
if( ubSectorID != SECTOR( sSectorX, sSectorY ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- ubSectorID mismatch. KM" );
#endif
goto FAIL_LOAD;
}
//now add any extra enemies that have arrived since the temp file was made.
if( ubStrategicTroops > ubNumTroops || ubStrategicElites > ubNumElites || ubStrategicAdmins > ubNumAdmins )
{
ubStrategicTroops = ( ubStrategicTroops > ubNumTroops ) ? ubStrategicTroops - ubNumTroops : 0;
ubStrategicElites = ( ubStrategicElites > ubNumElites ) ? ubStrategicElites - ubNumElites : 0;
ubStrategicAdmins = ( ubStrategicAdmins > ubNumAdmins ) ? ubStrategicAdmins - ubNumAdmins : 0;
AddSoldierInitListEnemyDefenceSoldiers( ubStrategicAdmins, ubStrategicTroops, ubStrategicElites );
}
if( ubStrategicCreatures > ubNumCreatures )
{
ubStrategicCreatures; //not sure if this wil ever happen. If so, needs to be handled.
}
//successful
FileClose( hfile );
return TRUE;
FAIL_LOAD:
//The temp file load failed either because of IO problems related to hacking/logic, or
//various checks failed for hacker validation. If we reach this point, the "error: exit game"
//dialog would appear in a non-testversion.
FileClose( hfile );
#ifdef JA2TESTVERSION
AssertMsg( 0, zReason );
#endif
return FALSE;
}
//OLD SAVE METHOD: This is the older way of saving the civilian and the enemies placement into a temp file
BOOLEAN SaveEnemySoldiersToTempFile( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubFirstIdTeam, UINT8 ubLastIdTeam, BOOLEAN fAppendToFile )
{
SOLDIERINITNODE *curr;
SOLDIERTYPE *pSoldier;
INT32 i;
INT32 slots = 0;
INT32 iSlotsAlreadyInUse=0;
UINT32 uiNumBytesWritten;
UINT32 uiTimeStamp;
HWFILE hfile;
SCHEDULENODE *pSchedule;
UINT16 usCheckSum;
CHAR8 zMapName[ 128 ];
UINT8 ubSectorID;
//STEP ONE: Prep the soldiers for saving...
//modify the map's soldier init list to reflect the changes to the member's still alive...
for( i = gTacticalStatus.Team[ ubFirstIdTeam ].bFirstID; i <= gTacticalStatus.Team[ ubLastIdTeam ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
if( pSoldier->bActive /*&& pSoldier->bInSector*/ && pSoldier->bLife )
{ //soldier is valid, so find the matching soldier init list entry for modification.
curr = gSoldierInitHead;
while( curr && curr->pSoldier != pSoldier )
{
curr = curr->next;
}
if( curr && curr->pSoldier == pSoldier && pSoldier->ubProfile == NO_PROFILE )
{ //found a match.
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME) )
{
if( !curr->pDetailedPlacement )
{ //need to upgrade the placement to detailed placement
curr->pBasicPlacement->fDetailedPlacement = TRUE;
curr->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
memset( curr->pDetailedPlacement, 0, sizeof( SOLDIERCREATE_STRUCT ) );
}
//Copy over the data of the soldier.
curr->pDetailedPlacement->ubProfile = NO_PROFILE;
curr->pDetailedPlacement->bLife = pSoldier->bLife;
curr->pDetailedPlacement->bLifeMax = pSoldier->bLifeMax;
curr->pDetailedPlacement->bAgility = pSoldier->bAgility;
curr->pDetailedPlacement->bDexterity = pSoldier->bDexterity;
curr->pDetailedPlacement->bExpLevel = pSoldier->bExpLevel;
curr->pDetailedPlacement->bMarksmanship = pSoldier->bMarksmanship;
curr->pDetailedPlacement->bMedical = pSoldier->bMedical;
curr->pDetailedPlacement->bMechanical = pSoldier->bMechanical;
curr->pDetailedPlacement->bExplosive = pSoldier->bExplosive;
curr->pDetailedPlacement->bLeadership = pSoldier->bLeadership;
curr->pDetailedPlacement->bStrength = pSoldier->bStrength;
curr->pDetailedPlacement->bWisdom = pSoldier->bWisdom;
curr->pDetailedPlacement->bAttitude = pSoldier->bAttitude;
curr->pDetailedPlacement->bOrders = pSoldier->bOrders;
curr->pDetailedPlacement->bMorale = pSoldier->bMorale;
curr->pDetailedPlacement->bAIMorale = pSoldier->bAIMorale;
curr->pDetailedPlacement->bBodyType = pSoldier->ubBodyType;
curr->pDetailedPlacement->ubCivilianGroup = pSoldier->ubCivilianGroup;
//If the soldier has a real schedule (not a default schedule), then store it.
//All other cases should be 0.
curr->pDetailedPlacement->ubScheduleID = 0;
if( pSoldier->ubScheduleID )
{
pSchedule = GetSchedule( pSoldier->ubScheduleID );
if( pSchedule && !(pSchedule->usFlags & SCHEDULE_FLAGS_TEMPORARY) )
{
curr->pDetailedPlacement->ubScheduleID = pSoldier->ubScheduleID;
}
}
curr->pDetailedPlacement->fHasKeys = pSoldier->bHasKeys;
curr->pDetailedPlacement->sSectorX = pSoldier->sSectorX;
curr->pDetailedPlacement->sSectorY = pSoldier->sSectorY;
curr->pDetailedPlacement->bSectorZ = pSoldier->bSectorZ;
curr->pDetailedPlacement->ubSoldierClass = pSoldier->ubSoldierClass;
curr->pDetailedPlacement->bTeam = pSoldier->bTeam;
curr->pDetailedPlacement->bDirection = pSoldier->bDirection;
//we don't want the player to think that all the enemies start in the exact position when we
//left the map, so randomize the start locations either current position or original position.
if( PreRandom( 2 ) )
{ //use current position
curr->pDetailedPlacement->fOnRoof = pSoldier->bLevel;
curr->pDetailedPlacement->sInsertionGridNo = pSoldier->sGridNo;
}
else
{ //use original position
curr->pDetailedPlacement->fOnRoof = curr->pBasicPlacement->fOnRoof;
curr->pDetailedPlacement->sInsertionGridNo = curr->pBasicPlacement->usStartingGridNo;
}
swprintf( curr->pDetailedPlacement->name, pSoldier->name );
//Copy patrol points
curr->pDetailedPlacement->bPatrolCnt = pSoldier->bPatrolCnt;
memcpy( curr->pDetailedPlacement->sPatrolGrid, pSoldier->usPatrolGrid, sizeof( INT16 ) * MAXPATROLGRIDS );
//copy colors for soldier based on the body type.
sprintf( curr->pDetailedPlacement->HeadPal, pSoldier->HeadPal );
sprintf( curr->pDetailedPlacement->VestPal, pSoldier->VestPal );
sprintf( curr->pDetailedPlacement->SkinPal, pSoldier->SkinPal );
sprintf( curr->pDetailedPlacement->PantsPal, pSoldier->PantsPal );
sprintf( curr->pDetailedPlacement->MiscPal, pSoldier->MiscPal );
//copy soldier's inventory
memcpy( curr->pDetailedPlacement->Inv, pSoldier->inv, sizeof( OBJECTTYPE ) * NUM_INV_SLOTS );
}
//DONE, now increment the counter, so we know how many there are.
slots++;
}
}
}
if( !slots )
{ //No need to save anything, so return successfully
RemoveEnemySoldierTempFile( sSectorX, sSectorY, bSectorZ );
return TRUE;
}
//STEP TWO: Set up the temp file to write to.
//Convert the current sector location into a file name
// GetMapFileName( sSectorX, sSectorY, bSectorZ, zTempName, FALSE );
//add the 'e' for 'Enemy preserved' to the front of the map name
// sprintf( zMapName, "%s\\e_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, zMapName, sSectorX, sSectorY, bSectorZ );
//if the file doesnt exist
if( FileSize( zMapName ) == 0 )
{
//set it so we are not appending
fAppendToFile = FALSE;
}
//if we are to append to the file
if( fAppendToFile )
{
//Open the file for writing, Create it if it doesnt exist
hfile = FileOpen( zMapName, FILE_ACCESS_READ | FILE_OPEN_ALWAYS, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
//advance for bytes and read the #of slots already used
FileSeek( hfile, 4, FILE_SEEK_FROM_START );
FileRead( hfile, &iSlotsAlreadyInUse, 4, &uiNumBytesWritten );
if( uiNumBytesWritten != 4 )
{
goto FAIL_SAVE;
}
FileClose( hfile );
//Open the file for writing, Create it if it doesnt exist
hfile = FileOpen( zMapName, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
slots += iSlotsAlreadyInUse;
//advance for bytes and read the #of slots already used
FileSeek( hfile, 4, FILE_SEEK_FROM_START );
FileWrite( hfile, &slots, 4, &uiNumBytesWritten );
if( uiNumBytesWritten != 4 )
{
goto FAIL_SAVE;
}
FileSeek( hfile, 0, FILE_SEEK_FROM_END );
}
else
{
//Open the file for writing, Create it if it doesnt exist
hfile = FileOpen( zMapName, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
}
//if we are to append to the file
if( !fAppendToFile )
{
FileWrite( hfile, &sSectorY, 2, &uiNumBytesWritten );
if( uiNumBytesWritten != 2 )
{
goto FAIL_SAVE;
}
//STEP THREE: Save the data
SaveSoldierInitListLinks( hfile );
FileWrite( hfile, &sSectorX, 2, &uiNumBytesWritten );
if( uiNumBytesWritten != 2 )
{
goto FAIL_SAVE;
}
//This check may appear confusing. It is intended to abort if the player is saving the game. It is only
//supposed to preserve the links to the placement list, so when we finally do leave the level with enemies remaining,
//we will need the links that are only added when the map is loaded, and are normally lost when restoring a save.
if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME )
{
slots = 0;
}
FileWrite( hfile, &slots, 4, &uiNumBytesWritten );
if( uiNumBytesWritten != 4 )
{
goto FAIL_SAVE;
}
uiTimeStamp = GetWorldTotalMin();
FileWrite( hfile, &uiTimeStamp, 4, &uiNumBytesWritten );
if( uiNumBytesWritten != 4 )
{
goto FAIL_SAVE;
}
FileWrite( hfile, &bSectorZ, 1, &uiNumBytesWritten );
if( uiNumBytesWritten != 1 )
{
goto FAIL_SAVE;
}
}
if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME )
{
//if we are saving the game, we don't need to preserve the soldier information, just
//preserve the links to the placement list.
slots = 0;
FileClose( hfile );
SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS );
return TRUE;
}
for( i = gTacticalStatus.Team[ ubFirstIdTeam ].bFirstID; i <= gTacticalStatus.Team[ ubLastIdTeam ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
if( pSoldier->bActive /*&& pSoldier->bInSector*/ && pSoldier->bLife )
{ //soldier is valid, so find the matching soldier init list entry for modification.
curr = gSoldierInitHead;
while( curr && curr->pSoldier != pSoldier )
{
curr = curr->next;
}
if( curr && curr->pSoldier == pSoldier && pSoldier->ubProfile == NO_PROFILE )
{ //found a match.
FileWrite( hfile, curr->pDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( SOLDIERCREATE_STRUCT ) )
{
goto FAIL_SAVE;
}
//insert a checksum equation (anti-hack)
usCheckSum =
curr->pDetailedPlacement->bLife * 7 +
curr->pDetailedPlacement->bLifeMax * 8 -
curr->pDetailedPlacement->bAgility * 2 +
curr->pDetailedPlacement->bDexterity * 1 +
curr->pDetailedPlacement->bExpLevel * 5 -
curr->pDetailedPlacement->bMarksmanship * 9 +
curr->pDetailedPlacement->bMedical * 10 +
curr->pDetailedPlacement->bMechanical * 3 +
curr->pDetailedPlacement->bExplosive * 4 +
curr->pDetailedPlacement->bLeadership * 5 +
curr->pDetailedPlacement->bStrength * 7 +
curr->pDetailedPlacement->bWisdom * 11 +
curr->pDetailedPlacement->bMorale * 7 +
curr->pDetailedPlacement->bAIMorale * 3 -
curr->pDetailedPlacement->bBodyType * 7 +
4 * 6 +
curr->pDetailedPlacement->sSectorX * 7 -
curr->pDetailedPlacement->ubSoldierClass * 4 +
curr->pDetailedPlacement->bTeam * 7 +
curr->pDetailedPlacement->bDirection * 5 +
curr->pDetailedPlacement->fOnRoof * 17 +
curr->pDetailedPlacement->sInsertionGridNo * 1 +
3;
FileWrite( hfile, &usCheckSum, 2, &uiNumBytesWritten );
if( uiNumBytesWritten != 2 )
{
goto FAIL_SAVE;
}
}
}
}
//if we are to append to the file
if( !fAppendToFile )
{
ubSectorID = SECTOR( sSectorX, sSectorY );
FileWrite( hfile, &ubSectorID, 1, &uiNumBytesWritten );
if( uiNumBytesWritten != 1 )
{
goto FAIL_SAVE;
}
}
FileClose( hfile );
SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS );
return TRUE;
FAIL_SAVE:
FileClose( hfile );
return FALSE;
}
BOOLEAN NewWayOfLoadingEnemySoldiersFromTempFile()
{
SOLDIERINITNODE *curr;
SOLDIERCREATE_STRUCT tempDetailedPlacement;
INT32 i;
INT32 slots = 0;
UINT32 uiNumBytesRead;
UINT32 uiTimeStamp;
HWFILE hfile;
INT16 sSectorX, sSectorY;
UINT16 usCheckSum, usFileCheckSum;
CHAR8 zMapName[ 128 ];
#ifdef JA2TESTVERSION
CHAR8 zReason[256];
#endif
INT8 bSectorZ;
UINT8 ubSectorID;
UINT8 ubNumElites = 0, ubNumTroops = 0, ubNumAdmins = 0, ubNumCreatures = 0;
UINT8 ubStrategicElites, ubStrategicTroops, ubStrategicAdmins, ubStrategicCreatures;
gfRestoringEnemySoldiersFromTempFile = TRUE;
//STEP ONE: Set up the temp file to read from.
//Convert the current sector location into a file name
// GetMapFileName( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, zTempName, FALSE );
//add the 'e' for 'Enemy preserved' to the front of the map name
// sprintf( zMapName, "%s\\e_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, zMapName, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
//Count the number of enemies ( elites, regulars, admins and creatures ) that are in the temp file.
if( gbWorldSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
if( !pSector )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- Couldn't find underground sector info for (%d,%d,%d) KM", gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
#endif
goto FAIL_LOAD;
}
}
else
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
ubNumElites = pSector->ubNumElites;
ubNumTroops = pSector->ubNumTroops;
ubNumAdmins = pSector->ubNumAdmins;
ubNumCreatures = pSector->ubNumCreatures;
}
if( !( gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
{
// Get the number of enemies form the temp file
CountNumberOfElitesRegularsAdminsAndCreaturesFromEnemySoldiersTempFile( &ubStrategicElites, &ubStrategicTroops, &ubStrategicAdmins, &ubStrategicCreatures );
//If any of the counts differ from what is in memory
if( ubStrategicElites != ubNumElites || ubStrategicTroops != ubNumTroops || ubStrategicAdmins != ubNumAdmins || ubStrategicCreatures != ubNumCreatures )
{
//remove the file
RemoveEnemySoldierTempFile( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
return( TRUE );
}
}
//reset
ubNumElites = ubNumTroops = ubNumAdmins = ubNumCreatures = 0;
//Open the file for reading
hfile = FileOpen( zMapName, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
//STEP TWO: determine whether or not we should use this data.
//because it is the demo, it is automatically used.
FileRead( hfile, &sSectorY, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading sSectorY. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorY != sSectorY )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- sSectorY mismatch. KM" );
#endif
goto FAIL_LOAD;
}
// LoadSoldierInitListLinks( hfile );
NewWayOfLoadingEnemySoldierInitListLinks( hfile );
//STEP THREE: read the data
FileRead( hfile, &sSectorX, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading sSectorX. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorX != sSectorX )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- sSectorX mismatch. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &slots, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading slots. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &uiTimeStamp, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading uiTimeStamp. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &bSectorZ, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading bSectorZ. KM" );
#endif
goto FAIL_LOAD;
}
if( gbWorldSectorZ != bSectorZ )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- bSectorZ mismatch. KM" );
#endif
goto FAIL_LOAD;
}
if( GetWorldTotalMin() > uiTimeStamp + 300 )
{ //the file has aged. Use the regular method for adding soldiers.
FileClose( hfile );
RemoveEnemySoldierTempFile( sSectorX, sSectorY, bSectorZ );
gfRestoringEnemySoldiersFromTempFile = FALSE;
return TRUE;
}
if( !slots )
{ //no need to restore the enemy's to the map. This means we are restoring a saved game.
gfRestoringEnemySoldiersFromTempFile = FALSE;
FileClose( hfile );
return TRUE;
}
if( slots < 0 || slots >= 64 )
{ //bad IO!
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- illegal slot value of %d. KM", slots );
#endif
goto FAIL_LOAD;
}
//For all the enemy slots (enemy/creature), clear the fPriorityExistance flag. We will use these flags
//to determine which slots have been modified as we load the data into the map pristine soldier init list.
curr = gSoldierInitHead;
while( curr )
{
if( curr->pBasicPlacement->fPriorityExistance )
{
if( curr->pBasicPlacement->bTeam == ENEMY_TEAM || curr->pBasicPlacement->bTeam == CREATURE_TEAM )
{
curr->pBasicPlacement->fPriorityExistance = FALSE;
}
}
curr = curr->next;
}
//get the number of enemies in this sector.
if( bSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ );
if( !pSector )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- Couldn't find underground sector info for (%d,%d,%d) KM", sSectorX, sSectorY, bSectorZ );
#endif
goto FAIL_LOAD;
}
ubStrategicElites = pSector->ubNumElites;
ubStrategicTroops = pSector->ubNumTroops;
ubStrategicAdmins = pSector->ubNumAdmins;
ubStrategicCreatures = pSector->ubNumCreatures;
}
else
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
ubStrategicCreatures = pSector->ubNumCreatures;
GetNumberOfEnemiesInSector( sSectorX, sSectorY, &ubStrategicAdmins, &ubStrategicTroops, &ubStrategicElites );
}
for( i = 0; i < slots; i++ )
{
FileRead( hfile, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SOLDIERCREATE_STRUCT ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading tempDetailedPlacment %d. KM", i );
#endif
goto FAIL_LOAD;
}
curr = gSoldierInitHead;
while( curr )
{
if( !curr->pBasicPlacement->fPriorityExistance )
{
if( curr->pBasicPlacement->bTeam == tempDetailedPlacement.bTeam )
{
curr->pBasicPlacement->fPriorityExistance = TRUE;
if( !curr->pDetailedPlacement )
{ //need to upgrade the placement to detailed placement
curr->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
}
//now replace the map pristine placement info with the temp map file version..
memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ) );
curr->pBasicPlacement->fPriorityExistance = TRUE;
curr->pBasicPlacement->bDirection = curr->pDetailedPlacement->bDirection;
curr->pBasicPlacement->bOrders = curr->pDetailedPlacement->bOrders;
curr->pBasicPlacement->bAttitude = curr->pDetailedPlacement->bAttitude;
curr->pBasicPlacement->bBodyType = curr->pDetailedPlacement->bBodyType;
curr->pBasicPlacement->fOnRoof = curr->pDetailedPlacement->fOnRoof;
curr->pBasicPlacement->ubSoldierClass = curr->pDetailedPlacement->ubSoldierClass;
curr->pBasicPlacement->ubCivilianGroup = curr->pDetailedPlacement->ubCivilianGroup;
curr->pBasicPlacement->fHasKeys = curr->pDetailedPlacement->fHasKeys;
curr->pBasicPlacement->usStartingGridNo = curr->pDetailedPlacement->sInsertionGridNo;
curr->pBasicPlacement->bPatrolCnt = curr->pDetailedPlacement->bPatrolCnt;
memcpy( curr->pBasicPlacement->sPatrolGrid, curr->pDetailedPlacement->sPatrolGrid,
sizeof( INT16 ) * curr->pBasicPlacement->bPatrolCnt );
FileRead( hfile, &usCheckSum, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading usCheckSum %d. KM", i );
#endif
goto FAIL_LOAD;
}
//verify the checksum equation (anti-hack) -- see save
usFileCheckSum =
curr->pDetailedPlacement->bLife * 7 +
curr->pDetailedPlacement->bLifeMax * 8 -
curr->pDetailedPlacement->bAgility * 2 +
curr->pDetailedPlacement->bDexterity * 1 +
curr->pDetailedPlacement->bExpLevel * 5 -
curr->pDetailedPlacement->bMarksmanship * 9 +
curr->pDetailedPlacement->bMedical * 10 +
curr->pDetailedPlacement->bMechanical * 3 +
curr->pDetailedPlacement->bExplosive * 4 +
curr->pDetailedPlacement->bLeadership * 5 +
curr->pDetailedPlacement->bStrength * 7 +
curr->pDetailedPlacement->bWisdom * 11 +
curr->pDetailedPlacement->bMorale * 7 +
curr->pDetailedPlacement->bAIMorale * 3 -
curr->pDetailedPlacement->bBodyType * 7 +
4 * 6 +
curr->pDetailedPlacement->sSectorX * 7 -
curr->pDetailedPlacement->ubSoldierClass * 4 +
curr->pDetailedPlacement->bTeam * 7 +
curr->pDetailedPlacement->bDirection * 5 +
curr->pDetailedPlacement->fOnRoof * 17 +
curr->pDetailedPlacement->sInsertionGridNo * 1 +
3;
if( usCheckSum != usFileCheckSum )
{ //Hacker has modified the stats on the enemy placements.
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- checksum for placement %d failed. KM", i );
#endif
goto FAIL_LOAD;
}
//Add preserved placements as long as they don't exceed the actual population.
switch( curr->pBasicPlacement->ubSoldierClass )
{
case SOLDIER_CLASS_ELITE:
ubNumElites++;
if( ubNumElites <= ubStrategicElites )
{
//def: AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_ARMY:
ubNumTroops++;
if( ubNumTroops <= ubStrategicTroops )
{
//def: AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_ADMINISTRATOR:
ubNumAdmins++;
if( ubNumAdmins <= ubStrategicAdmins )
{
//def: AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_CREATURE:
ubNumCreatures++;
if( ubNumCreatures <= ubStrategicCreatures )
{
//def: AddPlacementToWorld( curr );
}
break;
}
break;
}
}
curr = curr->next;
}
}
FileRead( hfile, &ubSectorID, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading ubSectorID. KM" );
#endif
goto FAIL_LOAD;
}
if( ubSectorID != SECTOR( sSectorX, sSectorY ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- ubSectorID mismatch. KM" );
#endif
goto FAIL_LOAD;
}
//now add any extra enemies that have arrived since the temp file was made.
if( ubStrategicTroops > ubNumTroops || ubStrategicElites > ubNumElites || ubStrategicAdmins > ubNumAdmins )
{
ubStrategicTroops = ( ubStrategicTroops > ubNumTroops ) ? ubStrategicTroops - ubNumTroops : 0;
ubStrategicElites = ( ubStrategicElites > ubNumElites ) ? ubStrategicElites - ubNumElites : 0;
ubStrategicAdmins = ( ubStrategicAdmins > ubNumAdmins ) ? ubStrategicAdmins - ubNumAdmins : 0;
AddSoldierInitListEnemyDefenceSoldiers( ubStrategicAdmins, ubStrategicTroops, ubStrategicElites );
}
if( ubStrategicCreatures > ubNumCreatures )
{
ubStrategicCreatures; //not sure if this wil ever happen. If so, needs to be handled.
}
//set the number of enemies in the sector
if( bSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
if( !pSector )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- Couldn't find underground sector info for (%d,%d,%d) KM", sSectorX, sSectorY, bSectorZ );
#endif
goto FAIL_LOAD;
/*
pSector->ubElitesInBattle = ubStrategicElites;
pSector->ubTroopsInBattle = ubStrategicTroops;
pSector->ubAdminsInBattle = ubStrategicAdmins;
pSector->ubCreaturesInBattle = ubStrategicCreatures;
*/
}
}
else
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
/*
pSector->ubElitesInBattle = ubStrategicElites;
pSector->ubTroopsInBattle = ubStrategicTroops;
pSector->ubAdminsInBattle = ubStrategicAdmins;
pSector->ubCreaturesInBattle = ubStrategicCreatures;
*/
}
//if in battle, what about the ubNumInBAttle
//successful
FileClose( hfile );
return TRUE;
FAIL_LOAD:
//The temp file load failed either because of IO problems related to hacking/logic, or
//various checks failed for hacker validation. If we reach this point, the "error: exit game"
//dialog would appear in a non-testversion.
FileClose( hfile );
#ifdef JA2TESTVERSION
AssertMsg( 0, zReason );
#endif
return FALSE;
}
BOOLEAN NewWayOfLoadingCiviliansFromTempFile()
{
SOLDIERINITNODE *curr, *temp;
SOLDIERCREATE_STRUCT tempDetailedPlacement;
INT32 i;
INT32 slots = 0;
UINT32 uiNumBytesRead;
UINT32 uiTimeStamp;
UINT32 uiTimeSinceLastLoaded;
HWFILE hfile;
INT16 sSectorX, sSectorY;
UINT16 usCheckSum, usFileCheckSum;
// CHAR8 zTempName[ 128 ];
CHAR8 zMapName[ 128 ];
#ifdef JA2TESTVERSION
CHAR8 zReason[256];
#endif
INT8 bSectorZ;
UINT8 ubSectorID;
UINT8 ubNumElites = 0, ubNumTroops = 0, ubNumAdmins = 0, ubNumCreatures = 0;
BOOLEAN fDeleted;
// UINT8 ubStrategicElites, ubStrategicTroops, ubStrategicAdmins, ubStrategicCreatures;
gfRestoringCiviliansFromTempFile = TRUE;
//STEP ONE: Set up the temp file to read from.
//Convert the current sector location into a file name
//GetMapFileName( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, zTempName, FALSE );
//add the 'e' for 'Enemy preserved' to the front of the map name
//sprintf( zMapName, "%s\\c_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_CIV_PRESERVED_TEMP_FILE_EXISTS, zMapName, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
//Open the file for reading
hfile = FileOpen( zMapName, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
//STEP TWO: determine whether or not we should use this data.
//because it is the demo, it is automatically used.
FileRead( hfile, &sSectorY, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading sSectorY. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorY != sSectorY )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- sSectorY mismatch. KM" );
#endif
goto FAIL_LOAD;
}
//LoadSoldierInitListLinks( hfile );
NewWayOfLoadingCivilianInitListLinks( hfile );
//STEP THREE: read the data
FileRead( hfile, &sSectorX, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading sSectorX. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorX != sSectorX )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- sSectorX mismatch. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &slots, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading slots. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &uiTimeStamp, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading uiTimeStamp. KM" );
#endif
goto FAIL_LOAD;
}
uiTimeSinceLastLoaded = GetWorldTotalMin() - uiTimeStamp;
FileRead( hfile, &bSectorZ, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading bSectorZ. KM" );
#endif
goto FAIL_LOAD;
}
if( gbWorldSectorZ != bSectorZ )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- bSectorZ mismatch. KM" );
#endif
goto FAIL_LOAD;
}
if( !slots )
{
//no need to restore the enemy's to the map. This means we are restoring a saved game.
gfRestoringCiviliansFromTempFile = FALSE;
FileClose( hfile );
return TRUE;
}
if( slots < 0 || slots >= 64 )
{
//bad IO!
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- illegal slot value of %d. KM", slots );
#endif
goto FAIL_LOAD;
}
//For all the enemy slots (enemy/creature), clear the fPriorityExistance flag. We will use these flags
//to determine which slots have been modified as we load the data into the map pristine soldier init list.
curr = gSoldierInitHead;
while( curr )
{
if( curr->pBasicPlacement->fPriorityExistance )
{
if( curr->pBasicPlacement->bTeam == CIV_TEAM )
{
curr->pBasicPlacement->fPriorityExistance = FALSE;
}
}
curr = curr->next;
}
for( i = 0; i < slots; i++ )
{
FileRead( hfile, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SOLDIERCREATE_STRUCT ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading tempDetailedPlacment %d. KM", i );
#endif
goto FAIL_LOAD;
}
curr = gSoldierInitHead;
while( curr )
{
if( !curr->pBasicPlacement->fPriorityExistance )
{
if( !curr->pDetailedPlacement || curr->pDetailedPlacement && curr->pDetailedPlacement->ubProfile == NO_PROFILE )
{
if( curr->pBasicPlacement->bTeam == tempDetailedPlacement.bTeam )
{
curr->pBasicPlacement->fPriorityExistance = TRUE;
if( !curr->pDetailedPlacement )
{
//need to upgrade the placement to detailed placement
curr->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
}
//now replace the map pristine placement info with the temp map file version..
memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ) );
curr->pBasicPlacement->fPriorityExistance = TRUE;
curr->pBasicPlacement->bDirection = curr->pDetailedPlacement->bDirection;
curr->pBasicPlacement->bOrders = curr->pDetailedPlacement->bOrders;
curr->pBasicPlacement->bAttitude = curr->pDetailedPlacement->bAttitude;
curr->pBasicPlacement->bBodyType = curr->pDetailedPlacement->bBodyType;
curr->pBasicPlacement->fOnRoof = curr->pDetailedPlacement->fOnRoof;
curr->pBasicPlacement->ubSoldierClass = curr->pDetailedPlacement->ubSoldierClass;
curr->pBasicPlacement->ubCivilianGroup = curr->pDetailedPlacement->ubCivilianGroup;
curr->pBasicPlacement->fHasKeys = curr->pDetailedPlacement->fHasKeys;
curr->pBasicPlacement->usStartingGridNo = curr->pDetailedPlacement->sInsertionGridNo;
curr->pBasicPlacement->bPatrolCnt = curr->pDetailedPlacement->bPatrolCnt;
memcpy( curr->pBasicPlacement->sPatrolGrid, curr->pDetailedPlacement->sPatrolGrid,
sizeof( INT16 ) * curr->pBasicPlacement->bPatrolCnt );
FileRead( hfile, &usCheckSum, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading usCheckSum %d. KM", i );
#endif
goto FAIL_LOAD;
}
//verify the checksum equation (anti-hack) -- see save
usFileCheckSum =
curr->pDetailedPlacement->bLife * 7 +
curr->pDetailedPlacement->bLifeMax * 8 -
curr->pDetailedPlacement->bAgility * 2 +
curr->pDetailedPlacement->bDexterity * 1 +
curr->pDetailedPlacement->bExpLevel * 5 -
curr->pDetailedPlacement->bMarksmanship * 9 +
curr->pDetailedPlacement->bMedical * 10 +
curr->pDetailedPlacement->bMechanical * 3 +
curr->pDetailedPlacement->bExplosive * 4 +
curr->pDetailedPlacement->bLeadership * 5 +
curr->pDetailedPlacement->bStrength * 7 +
curr->pDetailedPlacement->bWisdom * 11 +
curr->pDetailedPlacement->bMorale * 7 +
curr->pDetailedPlacement->bAIMorale * 3 -
curr->pDetailedPlacement->bBodyType * 7 +
4 * 6 +
curr->pDetailedPlacement->sSectorX * 7 -
curr->pDetailedPlacement->ubSoldierClass * 4 +
curr->pDetailedPlacement->bTeam * 7 +
curr->pDetailedPlacement->bDirection * 5 +
curr->pDetailedPlacement->fOnRoof * 17 +
curr->pDetailedPlacement->sInsertionGridNo * 1 +
3;
if( usCheckSum != usFileCheckSum )
{
//Hacker has modified the stats on the enemy placements.
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- checksum for placement %d failed. KM", i );
#endif
goto FAIL_LOAD;
}
if( curr->pDetailedPlacement->bLife < curr->pDetailedPlacement->bLifeMax )
{ //Add 4 life for every hour that passes.
INT32 iNewLife;
iNewLife = curr->pDetailedPlacement->bLife + uiTimeSinceLastLoaded / 15;
iNewLife = min( curr->pDetailedPlacement->bLifeMax, iNewLife );
curr->pDetailedPlacement->bLife = (INT8)iNewLife;
}
if( curr->pBasicPlacement->bTeam == CIV_TEAM )
{
//def: AddPlacementToWorld( curr );
break;
}
}
}
}
curr = curr->next;
}
}
// now remove any non-priority placement which matches the conditions!
curr = gSoldierInitHead;
fDeleted = FALSE;
while( curr )
{
if( !curr->pBasicPlacement->fPriorityExistance )
{
if( !curr->pDetailedPlacement || curr->pDetailedPlacement && curr->pDetailedPlacement->ubProfile == NO_PROFILE )
{
if( curr->pBasicPlacement->bTeam == tempDetailedPlacement.bTeam )
{
// Save pointer to the next guy in the list
// and after deleting, set the 'curr' to that guy
temp = curr->next;
RemoveSoldierNodeFromInitList( curr );
curr = temp;
fDeleted = TRUE;
}
}
}
if ( fDeleted )
{
// we've already done our pointer update so don't advance the pointer
fDeleted = FALSE;
}
else
{
curr = curr->next;
}
}
FileRead( hfile, &ubSectorID, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- EOF while reading ubSectorID. KM" );
#endif
goto FAIL_LOAD;
}
/*
if( ubSectorID != SECTOR( sSectorX, sSectorY ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Civilian -- ubSectorID mismatch. KM" );
#endif
goto FAIL_LOAD;
}
*/
//successful
FileClose( hfile );
return TRUE;
FAIL_LOAD:
//The temp file load failed either because of IO problems related to hacking/logic, or
//various checks failed for hacker validation. If we reach this point, the "error: exit game"
//dialog would appear in a non-testversion.
FileClose( hfile );
#ifdef JA2TESTVERSION
AssertMsg( 0, zReason );
#endif
return FALSE;
}
//If we are saving a game and we are in the sector, we will need to preserve the links between the
//soldiers and the soldier init list. Otherwise, the temp file will be deleted.
BOOLEAN NewWayOfSavingEnemyAndCivliansToTempFile( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, BOOLEAN fEnemy, BOOLEAN fValidateOnly )
{
SOLDIERINITNODE *curr;
SOLDIERTYPE *pSoldier;
INT32 i;
INT32 slots = 0;
UINT32 uiNumBytesWritten;
UINT32 uiTimeStamp;
HWFILE hfile;
// CHAR8 zTempName[ 128 ];
CHAR8 zMapName[ 128 ];
UINT8 ubSectorID;
UINT16 usCheckSum;
UINT8 ubStartID=0;
UINT8 ubEndID = 0;
//if we are saving the enemy info to the enemy temp file
if( fEnemy )
{
ubStartID = ENEMY_TEAM;
ubEndID = CREATURE_TEAM;
}
//else its the civilian team
else
{
ubStartID = CIV_TEAM;
ubEndID = CIV_TEAM;
}
//STEP ONE: Prep the soldiers for saving...
//modify the map's soldier init list to reflect the changes to the member's still alive...
for( i = gTacticalStatus.Team[ ubStartID ].bFirstID; i <= gTacticalStatus.Team[ ubEndID ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
//make sure the person is active, alive, in the sector, and is not a profiled person
if( pSoldier->bActive /*&& pSoldier->bInSector*/ && pSoldier->bLife && pSoldier->ubProfile == NO_PROFILE )
{ //soldier is valid, so find the matching soldier init list entry for modification.
curr = gSoldierInitHead;
while( curr && curr->pSoldier != pSoldier )
{
curr = curr->next;
}
if( curr && curr->pSoldier == pSoldier && pSoldier->ubProfile == NO_PROFILE )
{ //found a match.
if( !fValidateOnly )
{
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME) )
{
if( !curr->pDetailedPlacement )
{ //need to upgrade the placement to detailed placement
curr->pBasicPlacement->fDetailedPlacement = TRUE;
curr->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
memset( curr->pDetailedPlacement, 0, sizeof( SOLDIERCREATE_STRUCT ) );
}
//Copy over the data of the soldier.
curr->pDetailedPlacement->ubProfile = NO_PROFILE;
curr->pDetailedPlacement->bLife = pSoldier->bLife;
curr->pDetailedPlacement->bLifeMax = pSoldier->bLifeMax;
curr->pDetailedPlacement->bAgility = pSoldier->bAgility;
curr->pDetailedPlacement->bDexterity = pSoldier->bDexterity;
curr->pDetailedPlacement->bExpLevel = pSoldier->bExpLevel;
curr->pDetailedPlacement->bMarksmanship = pSoldier->bMarksmanship;
curr->pDetailedPlacement->bMedical = pSoldier->bMedical;
curr->pDetailedPlacement->bMechanical = pSoldier->bMechanical;
curr->pDetailedPlacement->bExplosive = pSoldier->bExplosive;
curr->pDetailedPlacement->bLeadership = pSoldier->bLeadership;
curr->pDetailedPlacement->bStrength = pSoldier->bStrength;
curr->pDetailedPlacement->bWisdom = pSoldier->bWisdom;
curr->pDetailedPlacement->bAttitude = pSoldier->bAttitude;
curr->pDetailedPlacement->bOrders = pSoldier->bOrders;
curr->pDetailedPlacement->bMorale = pSoldier->bMorale;
curr->pDetailedPlacement->bAIMorale = pSoldier->bAIMorale;
curr->pDetailedPlacement->bBodyType = pSoldier->ubBodyType;
curr->pDetailedPlacement->ubCivilianGroup = pSoldier->ubCivilianGroup;
curr->pDetailedPlacement->ubScheduleID = pSoldier->ubScheduleID;
curr->pDetailedPlacement->fHasKeys = pSoldier->bHasKeys;
curr->pDetailedPlacement->sSectorX = pSoldier->sSectorX;
curr->pDetailedPlacement->sSectorY = pSoldier->sSectorY;
curr->pDetailedPlacement->bSectorZ = pSoldier->bSectorZ;
curr->pDetailedPlacement->ubSoldierClass = pSoldier->ubSoldierClass;
curr->pDetailedPlacement->bTeam = pSoldier->bTeam;
curr->pDetailedPlacement->bDirection = pSoldier->bDirection;
//we don't want the player to think that all the enemies start in the exact position when we
//left the map, so randomize the start locations either current position or original position.
if( PreRandom( 2 ) )
{ //use current position
curr->pDetailedPlacement->fOnRoof = pSoldier->bLevel;
curr->pDetailedPlacement->sInsertionGridNo = pSoldier->sGridNo;
}
else
{ //use original position
curr->pDetailedPlacement->fOnRoof = curr->pBasicPlacement->fOnRoof;
curr->pDetailedPlacement->sInsertionGridNo = curr->pBasicPlacement->usStartingGridNo;
}
swprintf( curr->pDetailedPlacement->name, pSoldier->name );
//Copy patrol points
curr->pDetailedPlacement->bPatrolCnt = pSoldier->bPatrolCnt;
memcpy( curr->pDetailedPlacement->sPatrolGrid, pSoldier->usPatrolGrid, sizeof( INT16 ) * MAXPATROLGRIDS );
//copy colors for soldier based on the body type.
sprintf( curr->pDetailedPlacement->HeadPal, pSoldier->HeadPal );
sprintf( curr->pDetailedPlacement->VestPal, pSoldier->VestPal );
sprintf( curr->pDetailedPlacement->SkinPal, pSoldier->SkinPal );
sprintf( curr->pDetailedPlacement->PantsPal, pSoldier->PantsPal );
sprintf( curr->pDetailedPlacement->MiscPal, pSoldier->MiscPal );
//copy soldier's inventory
memcpy( curr->pDetailedPlacement->Inv, pSoldier->inv, sizeof( OBJECTTYPE ) * NUM_INV_SLOTS );
}
}
//DONE, now increment the counter, so we know how many there are.
slots++;
}
}
}
if( !slots )
{
if( fEnemy )
{
//No need to save anything, so return successfully
RemoveEnemySoldierTempFile( sSectorX, sSectorY, bSectorZ );
return TRUE;
}
else
{
//No need to save anything, so return successfully
RemoveCivilianTempFile( sSectorX, sSectorY, bSectorZ );
return( TRUE );
}
}
if( fValidateOnly )
{
return( TRUE );
}
//STEP TWO: Set up the temp file to write to.
//Convert the current sector location into a file name
//GetMapFileName( sSectorX, sSectorY, bSectorZ, zTempName, FALSE );
if( fEnemy )
{
//add the 'e' for 'Enemy preserved' to the front of the map name
//sprintf( zMapName, "%s\\e_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, zMapName, sSectorX, sSectorY, bSectorZ );
}
else
{
//add the 'e' for 'Enemy preserved' to the front of the map name
//sprintf( zMapName, "%s\\c_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_CIV_PRESERVED_TEMP_FILE_EXISTS, zMapName, sSectorX, sSectorY, bSectorZ );
}
//Open the file for writing, Create it if it doesnt exist
hfile = FileOpen( zMapName, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
FileWrite( hfile, &sSectorY, 2, &uiNumBytesWritten );
if( uiNumBytesWritten != 2 )
{
goto FAIL_SAVE;
}
//STEP THREE: Save the data
//this works for both civs and enemies
SaveSoldierInitListLinks( hfile );
FileWrite( hfile, &sSectorX, 2, &uiNumBytesWritten );
if( uiNumBytesWritten != 2 )
{
goto FAIL_SAVE;
}
//This check may appear confusing. It is intended to abort if the player is saving the game. It is only
//supposed to preserve the links to the placement list, so when we finally do leave the level with enemies remaining,
//we will need the links that are only added when the map is loaded, and are normally lost when restoring a save.
if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME )
{
slots = 0;
}
FileWrite( hfile, &slots, 4, &uiNumBytesWritten );
if( uiNumBytesWritten != 4 )
{
goto FAIL_SAVE;
}
uiTimeStamp = GetWorldTotalMin();
FileWrite( hfile, &uiTimeStamp, 4, &uiNumBytesWritten );
if( uiNumBytesWritten != 4 )
{
goto FAIL_SAVE;
}
FileWrite( hfile, &bSectorZ, 1, &uiNumBytesWritten );
if( uiNumBytesWritten != 1 )
{
goto FAIL_SAVE;
}
if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME )
{
//if we are saving the game, we don't need to preserve the soldier information, just
//preserve the links to the placement list.
slots = 0;
FileClose( hfile );
if( fEnemy )
{
SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS );
}
else
{
SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_CIV_PRESERVED_TEMP_FILE_EXISTS );
}
return TRUE;
}
for( i = gTacticalStatus.Team[ ubStartID ].bFirstID; i <= gTacticalStatus.Team[ ubEndID ].bLastID; i++ )
{
pSoldier = MercPtrs[ i ];
// CJC: note that bInSector is not required; the civ could be offmap!
if( pSoldier->bActive /*&& pSoldier->bInSector*/ && pSoldier->bLife )
{
//soldier is valid, so find the matching soldier init list entry for modification.
curr = gSoldierInitHead;
while( curr && curr->pSoldier != pSoldier )
{
curr = curr->next;
}
if( curr && curr->pSoldier == pSoldier && pSoldier->ubProfile == NO_PROFILE )
{
//found a match.
FileWrite( hfile, curr->pDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( SOLDIERCREATE_STRUCT ) )
{
goto FAIL_SAVE;
}
//insert a checksum equation (anti-hack)
usCheckSum =
curr->pDetailedPlacement->bLife * 7 +
curr->pDetailedPlacement->bLifeMax * 8 -
curr->pDetailedPlacement->bAgility * 2 +
curr->pDetailedPlacement->bDexterity * 1 +
curr->pDetailedPlacement->bExpLevel * 5 -
curr->pDetailedPlacement->bMarksmanship * 9 +
curr->pDetailedPlacement->bMedical * 10 +
curr->pDetailedPlacement->bMechanical * 3 +
curr->pDetailedPlacement->bExplosive * 4 +
curr->pDetailedPlacement->bLeadership * 5 +
curr->pDetailedPlacement->bStrength * 7 +
curr->pDetailedPlacement->bWisdom * 11 +
curr->pDetailedPlacement->bMorale * 7 +
curr->pDetailedPlacement->bAIMorale * 3 -
curr->pDetailedPlacement->bBodyType * 7 +
4 * 6 +
curr->pDetailedPlacement->sSectorX * 7 -
curr->pDetailedPlacement->ubSoldierClass * 4 +
curr->pDetailedPlacement->bTeam * 7 +
curr->pDetailedPlacement->bDirection * 5 +
curr->pDetailedPlacement->fOnRoof * 17 +
curr->pDetailedPlacement->sInsertionGridNo * 1 +
3;
FileWrite( hfile, &usCheckSum, 2, &uiNumBytesWritten );
if( uiNumBytesWritten != 2 )
{
goto FAIL_SAVE;
}
}
}
}
ubSectorID = SECTOR( sSectorX, sSectorY );
FileWrite( hfile, &ubSectorID, 1, &uiNumBytesWritten );
if( uiNumBytesWritten != 1 )
{
goto FAIL_SAVE;
}
FileClose( hfile );
if( fEnemy )
{
SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS );
}
else
{
SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_CIV_PRESERVED_TEMP_FILE_EXISTS );
}
return TRUE;
FAIL_SAVE:
FileClose( hfile );
return FALSE;
}
BOOLEAN CountNumberOfElitesRegularsAdminsAndCreaturesFromEnemySoldiersTempFile( UINT8 *pubNumElites, UINT8 *pubNumRegulars, UINT8 *pubNumAdmins, UINT8 *pubNumCreatures )
{
// SOLDIERINITNODE *curr;
SOLDIERCREATE_STRUCT tempDetailedPlacement;
INT32 i;
INT32 slots = 0;
UINT32 uiNumBytesRead;
UINT32 uiTimeStamp;
HWFILE hfile;
INT16 sSectorX, sSectorY;
UINT16 usCheckSum;
CHAR8 zMapName[ 128 ];
#ifdef JA2TESTVERSION
CHAR8 zReason[256];
#endif
INT8 bSectorZ;
UINT8 ubSectorID;
// UINT8 ubNumElites = 0, ubNumTroops = 0, ubNumAdmins = 0, ubNumCreatures = 0;
// UINT8 ubStrategicElites, ubStrategicTroops, ubStrategicAdmins, ubStrategicCreatures;
//make sure the variables are initialized
*pubNumElites = 0;
*pubNumRegulars = 0;
*pubNumAdmins = 0;
*pubNumCreatures = 0;
//STEP ONE: Set up the temp file to read from.
//Convert the current sector location into a file name
// GetMapFileName( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, zTempName, FALSE );
//add the 'e' for 'Enemy preserved' to the front of the map name
// sprintf( zMapName, "%s\\e_%s", MAPS_DIR, zTempName);
GetMapTempFileName( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, zMapName, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
//Open the file for reading
hfile = FileOpen( zMapName, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
if( hfile == 0 )
{ //Error opening map modification file
return FALSE;
}
//STEP TWO: determine whether or not we should use this data.
//because it is the demo, it is automatically used.
FileRead( hfile, &sSectorY, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading sSectorY. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorY != sSectorY )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- sSectorY mismatch. KM" );
#endif
goto FAIL_LOAD;
}
// LoadSoldierInitListLinks( hfile );
LookAtButDontProcessEnemySoldierInitListLinks( hfile );
//STEP THREE: read the data
FileRead( hfile, &sSectorX, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading sSectorX. KM" );
#endif
goto FAIL_LOAD;
}
if( gWorldSectorX != sSectorX )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- sSectorX mismatch. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &slots, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading slots. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &uiTimeStamp, 4, &uiNumBytesRead );
if( uiNumBytesRead != 4 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading uiTimeStamp. KM" );
#endif
goto FAIL_LOAD;
}
FileRead( hfile, &bSectorZ, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading bSectorZ. KM" );
#endif
goto FAIL_LOAD;
}
if( gbWorldSectorZ != bSectorZ )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- bSectorZ mismatch. KM" );
#endif
goto FAIL_LOAD;
}
if( !slots )
{
//no need to restore the enemy's to the map. This means we are restoring a saved game.
FileClose( hfile );
return TRUE;
}
if( slots < 0 || slots >= 64 )
{ //bad IO!
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- illegal slot value of %d. KM", slots );
#endif
goto FAIL_LOAD;
}
/*
//get the number of enemies in this sector.
if( bSectorZ )
{
UNDERGROUND_SECTORINFO *pSector;
pSector = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ );
if( !pSector )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- Couldn't find underground sector info for (%d,%d,%d) KM", sSectorX, sSectorY, bSectorZ );
#endif
goto FAIL_LOAD;
}
ubStrategicElites = pSector->ubNumElites;
ubStrategicTroops = pSector->ubNumTroops;
ubStrategicAdmins = pSector->ubNumAdmins;
ubStrategicCreatures = pSector->ubNumCreatures;
}
else
{
SECTORINFO *pSector;
pSector = &SectorInfo[ SECTOR( sSectorX, sSectorY ) ];
ubStrategicCreatures = pSector->ubNumCreatures;
GetNumberOfEnemiesInSector( sSectorX, sSectorY, &ubStrategicAdmins, &ubStrategicTroops, &ubStrategicElites );
}
*/
for( i = 0; i < slots; i++ )
{
FileRead( hfile, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SOLDIERCREATE_STRUCT ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading tempDetailedPlacment %d. KM", i );
#endif
goto FAIL_LOAD;
}
//increment the current type of soldier
switch( tempDetailedPlacement.ubSoldierClass )
{
case SOLDIER_CLASS_ELITE:
(*pubNumElites)++;
break;
case SOLDIER_CLASS_ARMY:
(*pubNumRegulars)++;
break;
case SOLDIER_CLASS_ADMINISTRATOR:
(*pubNumAdmins)++;
break;
case SOLDIER_CLASS_CREATURE:
(*pubNumCreatures)++;
break;
}
FileRead( hfile, &usCheckSum, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading usCheckSum %d. KM", i );
#endif
goto FAIL_LOAD;
}
/*
while( curr )
{
if( !curr->pBasicPlacement->fPriorityExistance )
{
if( curr->pBasicPlacement->bTeam == tempDetailedPlacement.bTeam )
{
curr->pBasicPlacement->fPriorityExistance = TRUE;
if( !curr->pDetailedPlacement )
{ //need to upgrade the placement to detailed placement
curr->pDetailedPlacement = (SOLDIERCREATE_STRUCT*)MemAlloc( sizeof( SOLDIERCREATE_STRUCT ) );
}
//now replace the map pristine placement info with the temp map file version..
memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, sizeof( SOLDIERCREATE_STRUCT ) );
curr->pBasicPlacement->fPriorityExistance = TRUE;
curr->pBasicPlacement->bDirection = curr->pDetailedPlacement->bDirection;
curr->pBasicPlacement->bOrders = curr->pDetailedPlacement->bOrders;
curr->pBasicPlacement->bAttitude = curr->pDetailedPlacement->bAttitude;
curr->pBasicPlacement->bBodyType = curr->pDetailedPlacement->bBodyType;
curr->pBasicPlacement->fOnRoof = curr->pDetailedPlacement->fOnRoof;
curr->pBasicPlacement->ubSoldierClass = curr->pDetailedPlacement->ubSoldierClass;
curr->pBasicPlacement->ubCivilianGroup = curr->pDetailedPlacement->ubCivilianGroup;
curr->pBasicPlacement->fHasKeys = curr->pDetailedPlacement->fHasKeys;
curr->pBasicPlacement->usStartingGridNo = curr->pDetailedPlacement->sInsertionGridNo;
curr->pBasicPlacement->bPatrolCnt = curr->pDetailedPlacement->bPatrolCnt;
memcpy( curr->pBasicPlacement->sPatrolGrid, curr->pDetailedPlacement->sPatrolGrid,
sizeof( INT16 ) * curr->pBasicPlacement->bPatrolCnt );
FileRead( hfile, &usCheckSum, 2, &uiNumBytesRead );
if( uiNumBytesRead != 2 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- EOF while reading usCheckSum %d. KM", i );
#endif
goto FAIL_LOAD;
}
//verify the checksum equation (anti-hack) -- see save
usFileCheckSum =
curr->pDetailedPlacement->bLife * 7 +
curr->pDetailedPlacement->bLifeMax * 8 -
curr->pDetailedPlacement->bAgility * 2 +
curr->pDetailedPlacement->bDexterity * 1 +
curr->pDetailedPlacement->bExpLevel * 5 -
curr->pDetailedPlacement->bMarksmanship * 9 +
curr->pDetailedPlacement->bMedical * 10 +
curr->pDetailedPlacement->bMechanical * 3 +
curr->pDetailedPlacement->bExplosive * 4 +
curr->pDetailedPlacement->bLeadership * 5 +
curr->pDetailedPlacement->bStrength * 7 +
curr->pDetailedPlacement->bWisdom * 11 +
curr->pDetailedPlacement->bMorale * 7 +
curr->pDetailedPlacement->bAIMorale * 3 -
curr->pDetailedPlacement->bBodyType * 7 +
4 * 6 +
curr->pDetailedPlacement->sSectorX * 7 -
curr->pDetailedPlacement->ubSoldierClass * 4 +
curr->pDetailedPlacement->bTeam * 7 +
curr->pDetailedPlacement->bDirection * 5 +
curr->pDetailedPlacement->fOnRoof * 17 +
curr->pDetailedPlacement->sInsertionGridNo * 1 +
3;
if( usCheckSum != usFileCheckSum )
{ //Hacker has modified the stats on the enemy placements.
#ifdef JA2TESTVERSION
sprintf( zReason, "EnemySoldier -- checksum for placement %d failed. KM", i );
#endif
goto FAIL_LOAD;
}
//Add preserved placements as long as they don't exceed the actual population.
switch( curr->pBasicPlacement->ubSoldierClass )
{
case SOLDIER_CLASS_ELITE:
ubNumElites++;
if( ubNumElites <= ubStrategicElites )
{
AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_ARMY:
ubNumTroops++;
if( ubNumTroops <= ubStrategicTroops )
{
AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_ADMINISTRATOR:
ubNumAdmins++;
if( ubNumAdmins <= ubStrategicAdmins )
{
AddPlacementToWorld( curr );
}
break;
case SOLDIER_CLASS_CREATURE:
ubNumCreatures++;
if( ubNumCreatures <= ubStrategicCreatures )
{
AddPlacementToWorld( curr );
}
break;
}
break;
}
}
curr = curr->next;
}
*/
}
FileRead( hfile, &ubSectorID, 1, &uiNumBytesRead );
if( uiNumBytesRead != 1 )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- EOF while reading ubSectorID. KM" );
#endif
goto FAIL_LOAD;
}
if( ubSectorID != SECTOR( sSectorX, sSectorY ) )
{
#ifdef JA2TESTVERSION
sprintf( zReason, "Check EnemySoldier -- ubSectorID mismatch. KM" );
#endif
goto FAIL_LOAD;
}
//successful
FileClose( hfile );
return TRUE;
FAIL_LOAD:
//The temp file load failed either because of IO problems related to hacking/logic, or
//various checks failed for hacker validation. If we reach this point, the "error: exit game"
//dialog would appear in a non-testversion.
FileClose( hfile );
#ifdef JA2TESTVERSION
AssertMsg( 0, zReason );
#endif
return FALSE;
}