#include "ASD.h" // added by Flugente #include "types.h" #include "Campaign Init.h" #include "random.h" #include "Campaign Types.h" #include "Queen Command.h" #include "Overhead.h" #include "Strategic Movement.h" #include "Strategic Status.h" #include "GameSettings.h" #include "Strategic AI.h" #include "XML.h" #include "expat.h" #include "Debug Control.h" #include "Tactical Save.h" #include "connect.h" #include #include "XMLWriter.h" #include "UndergroundInit.h" #include "GameInitOptionsScreen.h" extern BOOLEAN InitStrategicMovementCosts(); void InitKnowFacilitiesFlags( ); UNDERGROUND_SECTORINFO* gpUndergroundSectorInfoTail = NULL; // Lesh: this array controls randomization of sectors // Note that some sectors in game already using alternative map // It is Skyrider quest: B15, E14, D12, C16 // weapon caches: E11, H5, H10, J12, M9 // Madlab quest: H7, H16, I11, E4 // Do not enable randomization of this sectors until you are know what you're doing BOOLEAN RandomSector[256] = { // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* A */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* G */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* H */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* I */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* J */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* K */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* L */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* M */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* N */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* O */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* P */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; typedef enum { ALTSECTORS_ELEMENT_NONE = 0, ALTSECTORS_ELEMENT_LIST, ALTSECTORS_ELEMENT_ROW, } ALTSECTORS_PARSE_STAGE; typedef struct { ALTSECTORS_PARSE_STAGE curElement; CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1]; UINT32 uiRowNumber; UINT32 currentDepth; UINT32 maxReadDepth; } altSectorsParseData; static void XMLCALL altSectorsStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts) { altSectorsParseData * pData = (altSectorsParseData *) userData; if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element? { if(strcmp(name, "ALT_SECTORS_LIST") == 0 && pData->curElement == ALTSECTORS_ELEMENT_NONE) { pData->curElement = ALTSECTORS_ELEMENT_LIST; pData->maxReadDepth++; //we are not skipping this element } else if(strcmp(name, "ROW") == 0 && pData->curElement == ALTSECTORS_ELEMENT_LIST) { pData->curElement = ALTSECTORS_ELEMENT_ROW; if(strcmp(atts[0], "y") == 0) { if ( isalpha(atts[1][0]) ) pData->uiRowNumber = (UINT32)(atts[1][0] - 0x40); else pData->uiRowNumber = atol(atts[1]); } pData->maxReadDepth++; //we are not skipping this element } pData->szCharData[0] = '\0'; } pData->currentDepth++; } static void XMLCALL altSectorsCharacterDataHandle(void *userData, const XML_Char *str, int len) { altSectorsParseData * pData = (altSectorsParseData *) userData; if(pData->currentDepth <= pData->maxReadDepth && strlen(pData->szCharData) < MAX_CHAR_DATA_LENGTH) strncat(pData->szCharData,str,__min((unsigned int)len,MAX_CHAR_DATA_LENGTH-strlen(pData->szCharData))); } static void XMLCALL altSectorsEndElementHandle(void *userData, const XML_Char *name) { altSectorsParseData * pData = (altSectorsParseData *) userData; if(pData->currentDepth <= pData->maxReadDepth) //we're at the end of an element that we've been reading { if(strcmp(name, "ALT_SECTORS_LIST") == 0 && pData->curElement == ALTSECTORS_ELEMENT_LIST) { pData->curElement = ALTSECTORS_ELEMENT_NONE; } else if(strcmp(name, "ROW") == 0 && pData->curElement == ALTSECTORS_ELEMENT_ROW) { STR8 curBuffer = pData->szCharData + strspn(pData->szCharData," \t\n\r"); UINT32 curCellIndex = 0; UINT32 curNumber; pData->curElement = ALTSECTORS_ELEMENT_LIST; while(curBuffer[0] != '\0') { sscanf( curBuffer,"%d",&curNumber); RandomSector[curCellIndex+16*(pData->uiRowNumber - 1)] = curNumber; curCellIndex++; curBuffer += strcspn(curBuffer," \t\n\r\0"); curBuffer += strspn(curBuffer," \t\n\r"); } } pData->maxReadDepth--; } pData->currentDepth--; } BOOLEAN ReadInAltSectors(STR fileName) { HWFILE hFile; UINT32 uiBytesRead; UINT32 uiFSize; CHAR8 * lpcBuffer; XML_Parser parser = XML_ParserCreate(NULL); altSectorsParseData pData; // Open xml file hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ); if ( !hFile ) return( FALSE ); uiFSize = FileGetSize(hFile); lpcBuffer = (CHAR8 *) MemAlloc(uiFSize+1); //Read in block if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) ) { MemFree(lpcBuffer); return( FALSE ); } lpcBuffer[uiFSize] = 0; //add a null terminator FileClose( hFile ); XML_SetElementHandler(parser, altSectorsStartElementHandle, altSectorsEndElementHandle); XML_SetCharacterDataHandler(parser, altSectorsCharacterDataHandle); memset(&pData,0,sizeof(pData)); XML_SetUserData(parser, &pData); if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) { CHAR8 errorBuf[511]; sprintf(errorBuf, "XML Parser Error in AltSectors.xml: %s at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); LiveMessage(errorBuf); MemFree(lpcBuffer); return FALSE; } MemFree(lpcBuffer); XML_ParserFree(parser); return( TRUE ); } BOOLEAN WriteInAltSectors(STR fileName) { XMLWriter xmlw; xmlw.openNode(L"ALT_SECTORS_LIST"); UINT32 x, y; for(y = 1;y <= 16;y++) { xmlw.addAttributeToNextValue(L"y",(char)(y+0x40)); std::stringstream ss; for(x = 1;x <= 16; x++) { ss << " " << RandomSector[ ((y - 1) * 16) + (x - 1) ]; } xmlw.addValue(L"ROW", ss.str()); } xmlw.closeNode(); xmlw.writeToFile(fileName); return (TRUE); } UNDERGROUND_SECTORINFO* NewUndergroundNode( UINT8 ubSectorX, UINT8 ubSectorY, UINT8 ubSectorZ ) { UNDERGROUND_SECTORINFO *curr; curr = (UNDERGROUND_SECTORINFO*)MemAlloc( sizeof( UNDERGROUND_SECTORINFO ) ); AssertMsg( curr, "Failed to create an underground sector info node." ); memset( curr, 0, sizeof( UNDERGROUND_SECTORINFO ) ); curr->ubSectorX = ubSectorX; curr->ubSectorY = ubSectorY; curr->ubSectorZ = ubSectorZ; if( gpUndergroundSectorInfoTail ) { gpUndergroundSectorInfoTail->next = curr; gpUndergroundSectorInfoTail = gpUndergroundSectorInfoTail->next; } else { gpUndergroundSectorInfoHead = curr; gpUndergroundSectorInfoTail = gpUndergroundSectorInfoHead; } return curr; } // setup which know facilities are in which cities void InitKnowFacilitiesFlags( ) { ///////////////////////////////////////////////////////////////// // HEADROCK HAM 3.5: No longer required. Function disconnected. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //SECTORINFO *pSector; // HEADROCK HAM 3.4: Externalized. All "known" facilities are read from gFacilityLocations. The // ones that have (fHidden=0) will be added to the sector info now, and thus visible immediately. // The others are added only once the player visits those sectors. /*for(UINT32 cnt=0; (gFacilityLocations[cnt].uiSectorID >= 0 && gFacilityLocations[cnt].uiSectorID <= 256); cnt++ ) { // Is the current sector valid? Is the facility set to be revealed on game-start? if (gFacilityLocations[cnt].uiSectorID > 0 && gFacilityLocations[cnt].uiSectorID < 257 && gFacilityLocations[cnt].fHidden == 0) { // Reveal the facility. pSector = &SectorInfo[gFacilityLocations[cnt].uiSectorID]; pSector->uiFacilitiesFlags |= (1 << (gFacilityLocations[cnt].uiFacilityType-1)); } }*/ /* HEADROCK HAM 3.4: Externalized. // Cambria hospital pSector = &SectorInfo[SEC_G8]; pSector->uiFacilitiesFlags |= SFCF_HOSPITAL; pSector = &SectorInfo[SEC_F8]; pSector->uiFacilitiesFlags |= SFCF_HOSPITAL; pSector = &SectorInfo[SEC_G9]; pSector->uiFacilitiesFlags |= SFCF_HOSPITAL; pSector = &SectorInfo[SEC_F9]; pSector->uiFacilitiesFlags |= SFCF_HOSPITAL; // Drassen airport pSector = &SectorInfo[SEC_B13]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; pSector = &SectorInfo[SEC_C13]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; pSector = &SectorInfo[SEC_D13]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; // Meduna airport & military complex pSector = &SectorInfo[SEC_N3]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; pSector = &SectorInfo[SEC_N4]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; pSector = &SectorInfo[SEC_N5]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; pSector = &SectorInfo[SEC_O3]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; pSector = &SectorInfo[SEC_O4]; pSector->uiFacilitiesFlags |= SFCF_AIRPORT; */ return; } //Mobile groups are handled separately from sectors, because they are on the move. void GeneratePatrolGroups() { GROUP *pGroup; UINT8 ubNumTroops; ubNumTroops = (UINT8)(3 + gGameOptions.ubDifficultyLevel + Random( 3 )); pGroup = CreateNewEnemyGroupDepartingFromSector( SEC_C7, 0, ubNumTroops, 0, 0, 0, 0 ); pGroup->ubTransportationMask = CAR; AddWaypointToPGroup( pGroup, 8, 3 ); //C8 AddWaypointToPGroup( pGroup, 7, 3 ); //C7 ubNumTroops = (UINT8)(3 + gGameOptions.ubDifficultyLevel + Random( 3 )); pGroup = CreateNewEnemyGroupDepartingFromSector( SEC_D9, 0, ubNumTroops, 0, 0, 0, 0 ); AddWaypointToPGroup( pGroup, 9, 5 ); //E9 AddWaypointToPGroup( pGroup, 9, 4 ); //D9 pGroup->ubTransportationMask = TRUCK; ubNumTroops = (UINT8)(3 + gGameOptions.ubDifficultyLevel + Random( 3 )); pGroup = CreateNewEnemyGroupDepartingFromSector( SEC_B9, 0, ubNumTroops, 0, 0, 0, 0 ); AddWaypointToPGroup( pGroup, 12, 2 ); //B12 AddWaypointToPGroup( pGroup, 9, 2 ); //B9 pGroup->ubTransportationMask = FOOT; ubNumTroops = (UINT8)(3 + gGameOptions.ubDifficultyLevel + Random( 3 )); pGroup = CreateNewEnemyGroupDepartingFromSector( SEC_A14, 0, ubNumTroops, 0, 0, 0, 0 ); pGroup->ubMoveType = ENDTOEND_FORWARDS; AddWaypointToPGroup( pGroup, 13, 1 ); //A13 AddWaypointToPGroup( pGroup, 15, 1 ); //A15 AddWaypointToPGroup( pGroup, 15, 5 ); //E15 AddWaypointToPGroup( pGroup, 13, 5 ); //E13 AddWaypointToPGroup( pGroup, 12, 5 ); //E12 AddWaypointToPGroup( pGroup, 12, 3 ); //C12 pGroup->ubTransportationMask = TRACKED; ubNumTroops = (UINT8)(5 + gGameOptions.ubDifficultyLevel * 2 + Random( 4 )); pGroup = CreateNewEnemyGroupDepartingFromSector( SEC_N6, 0, ubNumTroops, 0, 0, 0, 0 ); AddWaypointToPGroup( pGroup, 9, 14 ); //N9 AddWaypointToPGroup( pGroup, 6, 14 ); //N6 pGroup->ubTransportationMask = CAR; ubNumTroops = (UINT8)(5 + gGameOptions.ubDifficultyLevel * 2 + Random( 4 )); pGroup = CreateNewEnemyGroupDepartingFromSector( SEC_N10, 0, ubNumTroops, 0, 0, 0, 0 ); AddWaypointToPGroup( pGroup, 10, 11 ); //K10 AddWaypointToPGroup( pGroup, 10, 14 ); //N10 pGroup->ubTransportationMask = CAR; } void TrashUndergroundSectorInfo() { UNDERGROUND_SECTORINFO *curr; while( gpUndergroundSectorInfoHead ) { curr = gpUndergroundSectorInfoHead; gpUndergroundSectorInfoHead = gpUndergroundSectorInfoHead->next; MemFree( curr ); } gpUndergroundSectorInfoHead = NULL; gpUndergroundSectorInfoTail = NULL; } //Defines the sectors that can be occupied by enemies, creatures, etc. It also //contains the network of cave connections critical for strategic creature spreading, as we can't //know how the levels connect without loading the maps. This is completely hardcoded, and any //changes to the maps, require changes accordingly. void BuildUndergroundSectorInfoList() { UNDERGROUND_SECTORINFO *curr; SECTORINFO *pSector = NULL; TrashUndergroundSectorInfo(); //if (LetLuaBuildUndergroundSectorInfoList()) g_luaUnderground.InitializeSectorList(); // lua script succeeded return; // If lua script had any errors // reset whatever the script may have done // and use hardcoded underground initialization. TrashUndergroundSectorInfo(); //******************** //* BASEMENT LEVEL 1 * //******************** //Miguel's basement. Nothing here. curr = NewUndergroundNode( 10, 1, 1 ); //Chitzena mine. Nothing here. curr = NewUndergroundNode( 2, 2, 1 ); //San mona mine. Nothing here. curr = NewUndergroundNode( 4, 4, 1 ); curr = NewUndergroundNode( 5, 4, 1 ); //J9 curr = NewUndergroundNode( 9, 10, 1 ); curr->ubNumTroops = zDiffSetting[gGameOptions.ubDifficultyLevel].iJ9B1NumTroops; //J9 feeding zone curr = NewUndergroundNode( 9, 10, 2 ); curr->ubNumCreatures = (UINT8)(2 + zDiffSetting[gGameOptions.ubDifficultyLevel].iJ9B2NumCreatures*2 + Random( 2 )); //K4 curr = NewUndergroundNode( 4, 11, 1 ); curr->ubNumTroops = (UINT8)(6 + zDiffSetting[gGameOptions.ubDifficultyLevel].iK4B1NumTroops*2 + Random( 3 )); curr->ubNumElites = (UINT8)(4 + zDiffSetting[gGameOptions.ubDifficultyLevel].iK4B1NumElites + Random( 2 )); //O3 curr = NewUndergroundNode( 3, 15, 1 ); curr->ubNumTroops = (UINT8)(6 + zDiffSetting[gGameOptions.ubDifficultyLevel].iO3B1NumTroops*2 + Random( 3 )); curr->ubNumElites = (UINT8)(4 + zDiffSetting[gGameOptions.ubDifficultyLevel].iO3B1NumElites + Random( 2 )); //P3 curr = NewUndergroundNode( 3, 16, 1 ); if (gGameOptions.ubDifficultyLevel == DIF_LEVEL_EASY ) curr->ubNumElites = (UINT8)(zDiffSetting[gGameOptions.ubDifficultyLevel].iP3B1NumElites + Random( 3 )); else if (gGameOptions.ubDifficultyLevel == DIF_LEVEL_MEDIUM ) curr->ubNumElites = (UINT8)(zDiffSetting[gGameOptions.ubDifficultyLevel].iP3B1NumElites + Random( 5 )); else if (gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD ) curr->ubNumElites = (UINT8)(zDiffSetting[gGameOptions.ubDifficultyLevel].iP3B1NumElites + Random( 6 )); else curr->ubNumElites = zDiffSetting[gGameOptions.ubDifficultyLevel].iP3B1NumElites; //Do all of the mandatory underground mine sectors //Drassen's mine //D13_B1 curr = NewUndergroundNode( 13, 4, 1 ); //E13_B1 curr = NewUndergroundNode( 13, 5, 1 ); //E13_B2 curr = NewUndergroundNode( 13, 5, 2 ); //F13_B2 curr = NewUndergroundNode( 13, 6, 2 ); //G13_B2 curr = NewUndergroundNode( 13, 7, 2 ); //G13_B3 curr = NewUndergroundNode( 13, 7, 3 ); //F13_B3 curr = NewUndergroundNode( 13, 6, 3 ); //Cambria's mine //H8_B1 curr = NewUndergroundNode( 8, 8, 1 ); //H9_B1 curr = NewUndergroundNode( 9, 8, 1 ); //H9_B2 curr = NewUndergroundNode( 9, 8, 2 ); //H8_B2 curr = NewUndergroundNode( 8, 8, 2 ); //H8_B3 curr = NewUndergroundNode( 8, 8, 3 ); //I8_B3 curr = NewUndergroundNode( 8, 9, 3 ); //J8_B3 curr = NewUndergroundNode( 8, 10, 3 ); //Alma's mine //I14_B1 curr = NewUndergroundNode( 14, 9, 1 ); //J14_B1 curr = NewUndergroundNode( 14, 10, 1 ); //J14_B2 curr = NewUndergroundNode( 14, 10, 2 ); //J13_B2 curr = NewUndergroundNode( 13, 10, 2 ); //J13_B3 curr = NewUndergroundNode( 13, 10, 3 ); //K13_B3 curr = NewUndergroundNode( 13, 11, 3 ); //Grumm's mine //H3_B1 curr = NewUndergroundNode( 3, 8, 1 ); //I3_B1 curr = NewUndergroundNode( 3, 9, 1 ); //I3_B2 curr = NewUndergroundNode( 3, 9, 2 ); //H3_B2 curr = NewUndergroundNode( 3, 8, 2 ); //H4_B2 curr = NewUndergroundNode( 4, 8, 2 ); curr->uiFlags |= SF_PENDING_ALTERNATE_MAP; //H4_B3 curr = NewUndergroundNode( 4, 8, 3 ); //G4_B3 curr = NewUndergroundNode( 4, 7, 3 ); } // Lesh: this function creates randomized world // every sector can be randomized between common and alternative, chances 50/50 // randomization of individual sectors can be switched off via array RandomSector[] void InitWorld() { INT16 sSectorCounter; for (sSectorCounter = 0; sSectorCounter < 256; sSectorCounter++) { // WANNE - MP: Disable "random sectors" in a multiplayer game if (!is_networked) { if ( RandomSector[ sSectorCounter ] ) { if ( Random(100) >= 50 ) SectorInfo[ sSectorCounter ].uiFlags |= SF_USE_ALTERNATE_MAP; } } } } //This is the function that is called only once, when the player begins a new game. This will calculate //starting numbers of the queen's army in various parts of the map, which will vary from campaign to campaign. //This is also highly effected by the game's difficulty setting. void InitNewCampaign() { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitNewCampaign"); //First clear all the sector information of all enemy existance. Conveniently, the //ubGroupType is also cleared, which is perceived to be an empty group. //memset( &SectorInfo, 0, sizeof( SECTORINFO ) * 256 ); for (std::vector::iterator secIter = SectorInfo.begin(); secIter != SectorInfo.end(); ++secIter) { memset( &(*secIter), 0, sizeof( SECTORINFO ) ); } InitStrategicMovementCosts(); RemoveAllGroups(); InitWorld(); // Lesh: generate different world each time using alternative maps // HEADROCK HAM 3.5: This is no longer required. //InitKnowFacilitiesFlags( ); BuildUndergroundSectorInfoList(); #ifdef JA2UB // no UB #else if (!is_networked) // allow overhead view of omerta A9 on game onset // HEADROCK HAM 3.5: Externalized. SetSectorFlag( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY, startingZ, SF_ALREADY_VISITED ); //hayden #endif // Flugente: init special AI InitASD(); //Generates the initial forces in a new campaign. The idea is to randomize numbers and sectors //so that no two games are the same. InitStrategicAI(); InitStrategicStatus(); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"InitNewCampaign done"); }