CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

INI.CPP (86252B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 
     16 /* $Header:   F:\projects\c&c\vcs\code\ini.cpv   2.18   16 Oct 1995 16:48:50   JOE_BOSTIC  $ */
     17 /***********************************************************************************************
     18  ***             C O N F I D E N T I A L  ---  W E S T W O O D   S T U D I O S               ***
     19  ***********************************************************************************************
     20  *                                                                                             *
     21  *                 Project Name : Command & Conquer                                            *
     22  *                                                                                             *
     23  *                    File Name : INI.CPP                                                      *
     24  *                                                                                             *
     25  *                   Programmer : Joe L. Bostic                                                *
     26  *                                                                                             *
     27  *                   Start Date : September 10, 1993                                           *
     28  *                                                                                             *
     29  *                  Last Update : July 30, 1995 [BRR]                                          *
     30  *                                                                                             *
     31  *---------------------------------------------------------------------------------------------*
     32  * Functions:                                                                                  *
     33  *   Assign_Houses -- Assigns multiplayer houses to various players                            *
     34  *   Clear_Flag_Spots -- Clears flag overlays off the map                                      *
     35  *   Clip_Move -- moves in given direction from given cell; clips to map                       *
     36  *   Clip_Scatter -- randomly scatters from given cell; won't fall off map                     *
     37  *   Create_Units -- Creates infantry & units, for non-base multiplayer                        *
     38  *   Furthest_Cell -- Finds cell furthest from a group of cells                                *
     39  *   Place_Flags -- Places flags for multiplayer games                                         *
     40  *   Read_Scenario_Ini -- Read specified scenario INI file.                                    *
     41  *   Remove_AI_Players -- Removes the computer AI houses & their units                         *
     42  *   Scan_Place_Object -- places an object >near< the given cell                               *
     43  *   Set_Scenario_Name -- Creates the INI scenario name string.                                *
     44  *   Sort_Cells -- sorts an array of cells by distance                                         *
     45  *   Write_Scenario_Ini -- Write the scenario INI file.                                        *
     46  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     47 
     48 #include	"function.h"
     49 
     50 /************************************* Prototypes *********************************************/
     51 static void Assign_Houses(void);
     52 static void Remove_AI_Players(void);
     53 static void Create_Units(void);
     54 static void Sort_Cells(CELL *cells, int numcells, CELL *outcells);
     55 static int Furthest_Cell(CELL *cells, int numcells, CELL *tcells, int numtcells);
     56 static CELL Clip_Scatter(CELL cell, int maxdist);
     57 static CELL Clip_Move(CELL cell, FacingType facing, int dist);
     58 
     59 
     60 /***********************************************************************************************
     61  * Set_Scenario_Name -- Creates the INI scenario name string.                                  *
     62  *                                                                                             *
     63  *    This routine is used by the scenario loading and saving code. It generates the scenario  *
     64  *    INI root file name for the specified scenario parameters.                                *
     65  *                                                                                             *
     66  * INPUT:                                                                                      *
     67  *         buf         buffer to store filename in; must be long enough for root.ext           *
     68  *       scenario      scenario number                                                         *
     69  *       player      player type for this game (GDI, NOD, multi-player, ...)                   *
     70  *       dir         directional parameter for this game (East/West)                           *
     71  *       var         variation of this game (Lose, A/B/C/D, etc)                               *
     72  *                                                                                             *
     73  * OUTPUT:  none.                                                                              *
     74  *                                                                                             *
     75  * WARNINGS:   none.                                                                           *
     76  *                                                                                             *
     77  * HISTORY:                                                                                    *
     78  *   05/28/1994 JLB : Created.                                                                 *
     79  *   05/01/1995 BRR : 2-player scenarios use same names as multiplayer                         *
     80  *=============================================================================================*/
     81 void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var)
     82 {
     83 	char c_player;			// character representing player type
     84 	char c_dir;				// character representing direction type
     85 	char c_var;				// character representing variation type
     86 	ScenarioVarType i;
     87 	char fname[_MAX_FNAME+_MAX_EXT];
     88 
     89 	/*
     90 	** Set the player-type value.
     91 	*/
     92 	switch (player) {
     93 		case SCEN_PLAYER_GDI:
     94 			c_player = HouseTypeClass::As_Reference(HOUSE_GOOD).Prefix;
     95 //			c_player = 'G';
     96 			break;
     97 
     98 		case SCEN_PLAYER_NOD:
     99 			c_player = HouseTypeClass::As_Reference(HOUSE_BAD).Prefix;
    100 //			c_player = 'B';
    101 			break;
    102 
    103 		case SCEN_PLAYER_JP:
    104 			c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix;
    105 //			c_player = 'J';
    106 			break;
    107 
    108 		/*
    109 		**	Multi player scenario.
    110 		*/
    111 		default:
    112 			c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix;
    113 //			c_player = 'M';
    114 			break;
    115 	}
    116 
    117 	/*
    118 	** Set the directional character value.
    119 	** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W'
    120 	*/
    121 	switch (dir) {
    122 		case SCEN_DIR_EAST:
    123 			c_dir = 'E';
    124 			break;
    125 
    126 		case SCEN_DIR_WEST:
    127 			c_dir = 'W';
    128 			break;
    129 
    130 		default:
    131 		case SCEN_DIR_NONE:
    132 			c_dir = (Random_Pick(0, 1) == 0) ? 'W' : 'E';
    133 			break;
    134 	}
    135 
    136 	/*
    137 	** Set the variation value.
    138 	*/
    139 	if (var == SCEN_VAR_NONE) {
    140 
    141 		/*
    142 		** Find which variations are available for this scenario
    143 		*/
    144 		for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) {
    145 			sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i);
    146 			if (!CCFileClass(fname).Is_Available()) {
    147 				break;
    148 			}
    149 		}
    150 
    151 		if (i==SCEN_VAR_FIRST) {
    152 			c_var = 'X';						// indicates an error
    153 		} else {
    154 			c_var = 'A' + Random_Pick(0, i-1);
    155 		}
    156 	} else {
    157 		switch (var) {
    158 			case SCEN_VAR_A:
    159 				c_var = 'A';
    160 				break;
    161 
    162 			case SCEN_VAR_B:
    163 				c_var = 'B';
    164 				break;
    165 
    166 			case SCEN_VAR_C:
    167 				c_var = 'C';
    168 				break;
    169 
    170 			case SCEN_VAR_D:
    171 				c_var = 'D';
    172 				break;
    173 
    174 			default:
    175 				c_var = 'L';
    176 				break;
    177 
    178 		}
    179 	}
    180 
    181 	/*
    182 	** generate the filename
    183 	*/
    184 	sprintf(buf, "SC%c%02d%c%c", c_player, scenario, c_dir, c_var);
    185 }
    186 
    187 
    188 extern void GlyphX_Assign_Houses(void);	//ST - 6/25/2019 11:08AM
    189 
    190 
    191 /***********************************************************************************************
    192  * Read_Scenario_Ini -- Read specified scenario INI file.                                      *
    193  *                                                                                             *
    194  *    Read in the scenario INI file. This routine only sets the game                           *
    195  *    globals with that data that is explicitly defined in the INI file.                       *
    196  *    The remaining necessary interpolated data is generated elsewhere.                        *
    197  *                                                                                             *
    198  * INPUT:                                                                                      *
    199  *          root      root filename for scenario file to read                                  *
    200  *                                                                                             *
    201  *          fresh      true = should the current scenario be cleared?                          *
    202  *                                                                                             *
    203  * OUTPUT:  bool; Was the scenario read successful?                                            *
    204  *                                                                                             *
    205  * WARNINGS:   none                                                                            *
    206  *                                                                                             *
    207  * HISTORY:                                                                                    *
    208  *   10/07/1992 JLB : Created.                                                                 *
    209  *=============================================================================================*/
    210 bool Read_Scenario_Ini(char *root, bool fresh)
    211 {
    212 	char *buffer;				// Scenario.ini staging buffer pointer.
    213 	char fname[_MAX_FNAME+_MAX_EXT];			// full INI filename
    214 	char buf[128];				// Working string staging buffer.
    215 #ifndef USE_RA_AI
    216 	int rndmax;
    217 	int rndmin;
    218 #endif //USE_RA_AI
    219 	int len;
    220 	unsigned char val;
    221 
    222 	ScenarioInit++;
    223 
    224 	/*
    225 	**	Fetch working pointer to the INI staging buffer. Make sure that the buffer
    226 	**	is cleared out before proceeding.  (Don't use the HidPage for this, since
    227 	**	the HidPage may be needed for various uncompressions during the INI
    228 	**	parsing.)
    229 	*/
    230 	buffer = (char *)_ShapeBuffer;
    231 	memset(buffer, '\0', _ShapeBufferSize);
    232 
    233 	if (fresh) {
    234 		Clear_Scenario();
    235 	}
    236 
    237 	/*
    238 	** If we are not dealing with scenario 1, or a multi player scenario
    239 	** then make sure the correct disk is in the drive.
    240 	*/
    241 	if (RequiredCD != -2) {
    242 		if (Scenario >= 20 && Scenario <60 && GameToPlay == GAME_NORMAL) {
    243 			RequiredCD = 2;
    244 		} else {
    245 			if (Scenario != 1) {
    246 				if (Scenario >=60){
    247 					RequiredCD = -1;
    248 				}else{
    249 					switch (ScenPlayer) {
    250 						case SCEN_PLAYER_GDI:
    251 							RequiredCD = 0;
    252 							break;
    253 						case SCEN_PLAYER_NOD:
    254 							RequiredCD = 1;
    255 							break;
    256 						default:
    257 							RequiredCD = -1;
    258 							break;
    259 					}
    260 				}
    261 			} else {
    262 				RequiredCD = -1;
    263 			}
    264 		}
    265 	}
    266 	if (!Force_CD_Available(RequiredCD)) {
    267 		Prog_End("Read_Scenario_Ini - CD not found", true);
    268 		if (!RunningAsDLL) {
    269 			exit(EXIT_FAILURE);
    270 		}
    271 	}
    272 
    273 	/*
    274 	**	Create scenario filename and read the file.
    275 	*/
    276 
    277 	sprintf(fname,"%s.INI",root);
    278 	CCFileClass file(fname);
    279 	if (!file.Is_Available()) {
    280 		GlyphX_Debug_Print("Failed to find scenario file");
    281 		GlyphX_Debug_Print(fname);
    282 		return(false);
    283 	} else {
    284 		
    285 		GlyphX_Debug_Print("Opened scenario file");
    286 		GlyphX_Debug_Print(fname);
    287 		
    288 		file.Read(buffer, _ShapeBufferSize-1);
    289 	}
    290 
    291 	/*
    292 	** Init the Scenario CRC value
    293 	*/
    294 	ScenarioCRC = 0;
    295 	len = strlen(buffer);
    296 	for (int i = 0; i < len; i++) {
    297 		val = (unsigned char)buffer[i];
    298 #ifndef DEMO
    299 		Add_CRC(&ScenarioCRC, (unsigned long)val);
    300 #endif
    301 	}
    302 
    303 	/*
    304 	**	Fetch the appropriate movie names from the INI file.
    305 	*/
    306 	WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer);
    307 	WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer);
    308 	WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer);
    309 	WWGetPrivateProfileString("Basic", "Win2", "x", WinMovie2, sizeof(WinMovie2), buffer);
    310 	WWGetPrivateProfileString("Basic", "Win3", "x", WinMovie3, sizeof(WinMovie3), buffer);
    311 	WWGetPrivateProfileString("Basic", "Win4", "x", WinMovie4, sizeof(WinMovie4), buffer);
    312 	WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer);
    313 	WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer);
    314 
    315 	/*
    316 	**	For single-player scenarios, 'BuildLevel' is the scenario number.
    317 	**	This must be set before any buildings are created (if a factory is created,
    318 	**	it needs to know the BuildLevel for the sidebar.)
    319 	*/
    320 	if (GameToPlay == GAME_NORMAL) {
    321 #ifdef NEWMENU
    322 		if (Scenario <= 15) {
    323 			BuildLevel = Scenario;
    324 		} else if (_stricmp(ScenarioName, "scg30ea") == 0 || _stricmp(ScenarioName, "scg90ea") == 0 || _stricmp(ScenarioName, "scb22ea") == 0) {
    325 			// N64 missions require build level 15
    326 			BuildLevel = 15;
    327 		} else {
    328 			BuildLevel = WWGetPrivateProfileInt("Basic", "BuildLevel", Scenario, buffer);
    329 		}
    330 #else
    331 		BuildLevel = Scenario;
    332 #endif
    333 	}
    334 
    335 	/*
    336 	**	Jurassic scenarios are allowed to build the full multiplayer set
    337 	**	of objects.
    338 	*/
    339 	if (Special.IsJurassic && AreThingiesEnabled) {
    340 		BuildLevel = 98;
    341 	}
    342 
    343 	/*
    344 	**	Fetch the transition theme for this scenario.
    345 	*/
    346 	TransitTheme = THEME_NONE;
    347 	WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer);
    348 	TransitTheme = Theme.From_Name(buf);
    349 
    350 	/*
    351 	**	Read in the team-type data. The team types must be created before any
    352 	**	triggers can be created.
    353 	*/
    354 	TeamTypeClass::Read_INI(buffer);
    355 	Call_Back();
    356 
    357 	/*
    358 	**	Read in the specific information for each of the house types.  This creates
    359 	**	the houses of different types.
    360 	*/
    361 	HouseClass::Read_INI(buffer);
    362 	Call_Back();
    363 
    364 	/*
    365 	**	Read in the trigger data. The triggers must be created before any other
    366 	**	objects can be initialized.
    367 	*/
    368 	TriggerClass::Read_INI(buffer);
    369 	Call_Back();
    370 
    371 	/*
    372 	**	Read in the map control values. This includes dimensions
    373 	**	as well as theater information.
    374 	*/
    375 	Map.Read_INI(buffer);
    376 	Call_Back();
    377 
    378 	/*
    379 	**	Assign PlayerPtr by reading the player's house from the INI;
    380 	**	Must be done before any TechnoClass objects are created.
    381 	*/
    382 //	if (GameToPlay == GAME_NORMAL && (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer == SCEN_PLAYER_NOD)) {
    383 	if (GameToPlay == GAME_NORMAL) {
    384 		WWGetPrivateProfileString("Basic", "Player", "GoodGuy", buf, 127, buffer);
    385 		CarryOverPercent = WWGetPrivateProfileInt("Basic", "CarryOverMoney", 100, buffer);
    386 		CarryOverPercent = Cardinal_To_Fixed(100, CarryOverPercent);
    387 		CarryOverCap = WWGetPrivateProfileInt("Basic", "CarryOverCap", -1, buffer);
    388 
    389 		PlayerPtr = HouseClass::As_Pointer(HouseTypeClass::From_Name(buf));
    390 		PlayerPtr->IsHuman = true;
    391 		int carryover;
    392 		if (CarryOverCap != -1) {
    393 			carryover = MIN((int)Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent), CarryOverCap);
    394 		} else {
    395 			carryover = Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent);
    396 		}
    397 		PlayerPtr->Credits += carryover;
    398 		PlayerPtr->InitialCredits += carryover;
    399 
    400 		if (Special.IsJurassic) {
    401 			PlayerPtr->ActLike = Whom;
    402 		}
    403 
    404 		if (Special.IsEasy) {
    405 			PlayerPtr->Assign_Handicap(DIFF_EASY);
    406 		} else if (Special.IsDifficult) {
    407 			PlayerPtr->Assign_Handicap(DIFF_HARD);
    408 		}
    409 	} else {
    410 
    411 #ifdef OBSOLETE
    412 		if (GameToPlay==GAME_NORMAL && ScenPlayer==SCEN_PLAYER_JP) {
    413 			PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4);
    414 			PlayerPtr->IsHuman = true;
    415 			PlayerPtr->Credits += CarryOverMoney;
    416 			PlayerPtr->InitialCredits += CarryOverMoney;
    417 			PlayerPtr->ActLike = Whom;
    418 		} else {
    419 			Assign_Houses();
    420 		}
    421 #endif
    422 		//Call new Assign_Houses function. ST - 6/25/2019 11:07AM
    423 		//Assign_Houses();
    424 		GlyphX_Assign_Houses();
    425 	}
    426 
    427 	/*
    428 	**	Attempt to read the map's binary image file; if fails, read the
    429 	**	template data from the INI, for backward compatibility
    430 	*/
    431 	if (fresh) {
    432 		if (!Map.Read_Binary(root, &ScenarioCRC)) {
    433 			TemplateClass::Read_INI(buffer);
    434 		}
    435 	}
    436 	Call_Back();
    437 
    438 	/*
    439 	**	Read in and place the 3D terrain objects.
    440 	*/
    441 	TerrainClass::Read_INI(buffer);
    442 	Call_Back();
    443 
    444 	/*
    445 	**	Read in and place the units (all sides).
    446 	*/
    447 	UnitClass::Read_INI(buffer);
    448 	Call_Back();
    449 
    450 	AircraftClass::Read_INI(buffer);
    451 	Call_Back();
    452 
    453 	/*
    454 	**	Read in and place the infantry units (all sides).
    455 	*/
    456 	InfantryClass::Read_INI(buffer);
    457 	Call_Back();
    458 
    459 	/*
    460 	**	Read in and place all the buildings on the map.
    461 	*/
    462 	BuildingClass::Read_INI(buffer);
    463 	Call_Back();
    464 
    465 	/*
    466 	**	Read in the AI's base information.
    467 	*/
    468 	Base.Read_INI(buffer);
    469 	Call_Back();
    470 
    471 	/*
    472 	**	Read in any normal overlay objects.
    473 	*/
    474 	OverlayClass::Read_INI(buffer);
    475 	Call_Back();
    476 
    477 	/*
    478 	**	Read in any smudge overlays.
    479 	*/
    480 	SmudgeClass::Read_INI(buffer);
    481 	Call_Back();
    482 
    483 	/*
    484 	**	Read in any briefing text.
    485 	*/
    486 	char * stage = &BriefingText[0];
    487 	*stage = '\0';
    488 	int index = 1;
    489 
    490 	/*
    491 	**	Build the full text of the mission objective.
    492 	*/
    493 	for (;;) {
    494 		int len = (sizeof(BriefingText)-strlen(BriefingText))-1;
    495 		if (len <= 0) {
    496 			break;
    497 		}
    498 
    499 		char buff[16];
    500 		sprintf(buff, "%d", index++);
    501 		*stage = '\0';
    502 		WWGetPrivateProfileString("Briefing", buff, "", stage, len, buffer);
    503 		if (strlen(stage) == 0) break;
    504 		strcat(stage, " ");
    505 		stage += strlen(stage);
    506 	}
    507 
    508 	/*
    509 	**	If the briefing text could not be found in the INI file, then search
    510 	**	the mission.ini file.
    511 	*/
    512 	if (BriefingText[0] == '\0') {
    513 		memset(_ShapeBuffer, '\0', _ShapeBufferSize);
    514 		CCFileClass("MISSION.INI").Read(_ShapeBuffer, _ShapeBufferSize);
    515 
    516 		char * buffer = (char *)Add_Long_To_Pointer(_ShapeBuffer, strlen(_ShapeBuffer));
    517 		char * work = &BriefingText[0];
    518 		int index = 1;
    519 
    520 		/*
    521 		**	Build the full text of the mission objective.
    522 		*/
    523 		for (;;) {
    524 			char buff[16];
    525 
    526 			sprintf(buff, "%d", index++);
    527 			*work = '\0';
    528 			WWGetPrivateProfileString(root, buff, "", work, (sizeof(BriefingText)-strlen(BriefingText))-1, _ShapeBuffer);
    529 			if (strlen(work) == 0) break;
    530 			strcat(work, " ");
    531 			work += strlen(work);
    532 		}
    533 	}
    534 
    535 	/*
    536 	**	Perform a final overpass of the map. This handles smoothing of certain
    537 	**	types of terrain (tiberium).
    538 	*/
    539 	Map.Overpass();
    540 	Call_Back();
    541 
    542 	/*
    543 	**	Special cases:
    544 	**		NOD7A cell 2795 - LAND_ROCK
    545 	**		NOD09A - delete airstrike trigger when radar destroyed
    546 	**		NOD10B cell 2015 - LAND_ROCK
    547 	**		NOD13B - trigger AI production when the player reaches the transports
    548 			       - fix repeating airstrike trigger
    549 	**		NOD13C - delete airstrike trigger when radar destroyed
    550 	*/
    551 	if (_stricmp(ScenarioName, "scb07ea") == 0) {
    552 		Map[(CELL)2795].Override_Land_Type(LAND_ROCK);
    553 	}
    554 	if (_stricmp(ScenarioName, "scb09ea") == 0) {
    555 		for (int index = 0; index < Buildings.Count(); ++index) {
    556 			BuildingClass* building = Buildings.Ptr(index);
    557 			if (building != NULL && building->Owner() == HOUSE_GOOD && *building == STRUCT_RADAR) {
    558 				building->Trigger = TriggerClass::As_Pointer("dely");
    559 				if (building->Trigger) {
    560 					building->Trigger->AttachCount++;
    561 				}
    562 				break;
    563 			}
    564 		}
    565 	}
    566 	if (_stricmp(ScenarioName, "scb10eb") == 0) {
    567 		Map[(CELL)2015].Override_Land_Type(LAND_ROCK);
    568 	}
    569 	if (_stricmp(ScenarioName, "scb13eb") == 0) {
    570 		TriggerClass* prod = new TriggerClass();
    571 		prod->Set_Name("prod");
    572 		prod->Event = EVENT_PLAYER_ENTERED;
    573 		prod->Action = TriggerClass::ACTION_BEGIN_PRODUCTION;
    574 		prod->House = HOUSE_BAD;
    575 
    576 		CellTriggers[276] = prod; prod->AttachCount++;
    577 		CellTriggers[340] = prod; prod->AttachCount++;
    578 		CellTriggers[404] = prod; prod->AttachCount++;
    579 		CellTriggers[468] = prod; prod->AttachCount++;
    580 
    581 		TriggerClass* xxxx = TriggerClass::As_Pointer("xxxx");
    582 		assert(xxxx != NULL);
    583 		xxxx->IsPersistant = TriggerClass::PERSISTANT;
    584 	}
    585 	if (_stricmp(ScenarioName, "scb13ec") == 0) {
    586 		for (int index = 0; index < Buildings.Count(); ++index) {
    587 			BuildingClass* building = Buildings.Ptr(index);
    588 			if (building != NULL && building->Owner() == HOUSE_GOOD && *building == STRUCT_RADAR && building->Trigger == NULL) {
    589 				building->Trigger = TriggerClass::As_Pointer("delx");
    590 				if (building->Trigger) {
    591 					building->Trigger->AttachCount++;
    592 				}
    593 				break;
    594 			}
    595 		}
    596 	}
    597 
    598 	/*
    599 	**	Scenario fix-up (applied on loaded games as well)
    600 	*/
    601 	Fixup_Scenario();
    602 
    603 	/*
    604 	**	Multi-player last-minute fixups:
    605 	**	- If computer players are disabled, remove all computer-owned houses
    606 	** - Otherwise, set MPlayerBlitz to 0 or 1, randomly
    607 	**	- If bases are disabled, create the scenario dynamically
    608 	**	- Remove any flag spot overlays lying around
    609 	**	- If capture-the-flag is enabled, assign flags to cells.
    610 	*/
    611 	if (GameToPlay != GAME_NORMAL || ScenPlayer == SCEN_PLAYER_2PLAYER ||
    612 		ScenPlayer == SCEN_PLAYER_MPLAYER) {
    613 
    614 		/*
    615 		**	If Ghosts are disabled and we're not editing, remove computer players
    616 		**	(Must be done after all objects are read in from the INI)
    617 		*/
    618 		if (!MPlayerGhosts && !Debug_Map) {
    619 			//Remove_AI_Players();		    // Done elsewhere now. ST - 6/25/2019 12:33PM
    620 		} else {
    621 
    622 			/*
    623 			** If Ghosts are on, set up their houses for blitzing the humans
    624 			*/
    625 #ifndef USE_RA_AI
    626 			MPlayerBlitz = IRandom (0,1);					// 1 = computer will blitz
    627 			
    628 			if (MPlayerBlitz) {
    629 				if (MPlayerBases) {
    630 					rndmax = 14000;
    631 					rndmin = 10000;
    632 				} else {
    633 					rndmax = 8000;
    634 					rndmin = 4000;
    635 				}
    636 
    637 				for (int i = 0; i < MPlayerMax; i++) {
    638 					HousesType house = (HousesType)(i + (int)HOUSE_MULTI1);
    639 					HouseClass *housep = HouseClass::As_Pointer (house);
    640 					if (housep) {	//Added. ST - 6/25/2019 11:37AM
    641 						housep->BlitzTime = IRandom (rndmin,rndmax);
    642 					}
    643 				}
    644 
    645 			}
    646 #else // USE_RA_AI
    647 			MPlayerBlitz = 0;
    648 #endif // USE_RA_AI
    649 		}
    650 
    651 		/*
    652 		**	Units must be created for each house.  If bases are ON, this routine
    653 		**	will create an MCV along with the units; otherwise, it will just create
    654 		**	a whole bunch of units.  MPlayerUnitCount is the total # of units
    655 		**	to create.
    656 		*/
    657 		if (!Debug_Map) {
    658 			int save_init = ScenarioInit;			// turn ScenarioInit off
    659 			ScenarioInit = 0;
    660 			Create_Units();
    661 			ScenarioInit = save_init;				// turn ScenarioInit back on
    662 		}
    663 
    664 		/*
    665 		**	Place crates if MPlayerGoodies is on.
    666 		*/
    667 		if (MPlayerGoodies) {
    668 			for (int index = 0; index < MPlayerCount; index++) {
    669 			//for (int index = 0; index < 200; index++) {  // Lots of crates for test		
    670 				Map.Place_Random_Crate();
    671 			}
    672 		}
    673 	}
    674 
    675 	Call_Back();
    676 
    677 	/*
    678 	**	Return with flag saying that the scenario file was read.
    679 	*/
    680 	ScenarioInit--;
    681 	return(true);
    682 }
    683 
    684 
    685 /***********************************************************************************************
    686  * Read_Scenario_Ini_File -- Read specified scenario INI file.                                 *
    687  *                                                                                             *
    688  *    Read in the scenario INI file. This routine only sets the game                           *
    689  *    globals with that data that is explicitly defined in the INI file.                       *
    690  *    The remaining necessary interpolated data is generated elsewhere.                        *
    691  *                                                                                             *
    692  * INPUT:																												  *
    693  *				scenario_file_name	path to the ini for the scenario										  *
    694  *																															  *
    695  *				bin_file_name			path to the bin for the scenario										  *
    696  *											 																				  *
    697  *          root      root filename for scenario file to read                                  *
    698  *                                                                                             *
    699  *          fresh      true = should the current scenario be cleared?                          *
    700  *                                                                                             *
    701  * OUTPUT:  bool; Was the scenario read successful?                                            *
    702  *                                                                                             *
    703  * WARNINGS:   none                                                                            *
    704  *                                                                                             *
    705  * HISTORY:                                                                                    *
    706  *   10/28/2019 JAS : Created.                                                                 *
    707  *=============================================================================================*/
    708 bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const char* root, bool fresh)
    709 {
    710 	ScenarioInit++;
    711 
    712 	char *buffer = (char *)_ShapeBuffer;
    713 	memset(buffer, '\0', _ShapeBufferSize);
    714 	char buf[128];
    715 	int len;
    716 	unsigned char val;
    717 
    718 	CCFileClass file(scenario_file_name);
    719 	if (!file.Is_Available()) {
    720 		GlyphX_Debug_Print("Failed to find scenario file");
    721 		GlyphX_Debug_Print(scenario_file_name);
    722 		return(false);
    723 	}
    724 	else {
    725 
    726 		GlyphX_Debug_Print("Opened scenario file");
    727 		GlyphX_Debug_Print(scenario_file_name);
    728 
    729 		file.Read(buffer, _ShapeBufferSize - 1);
    730 	}
    731 
    732 	/*
    733 	** Init the Scenario CRC value
    734 	*/
    735 	ScenarioCRC = 0;
    736 	len = strlen(buffer);
    737 	for (int i = 0; i < len; i++) {
    738 		val = (unsigned char)buffer[i];
    739 #ifndef DEMO
    740 		Add_CRC(&ScenarioCRC, (unsigned long)val);
    741 #endif
    742 	}
    743 
    744 	/*
    745 	**	Fetch the appropriate movie names from the INI file.
    746 	*/
    747 	WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer);
    748 	WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer);
    749 	WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer);
    750 	WWGetPrivateProfileString("Basic", "Win2", "x", WinMovie2, sizeof(WinMovie2), buffer);
    751 	WWGetPrivateProfileString("Basic", "Win3", "x", WinMovie3, sizeof(WinMovie3), buffer);
    752 	WWGetPrivateProfileString("Basic", "Win4", "x", WinMovie4, sizeof(WinMovie4), buffer);
    753 	WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer);
    754 	WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer);
    755 
    756 	/*
    757 	**	For single-player scenarios, 'BuildLevel' is the scenario number.
    758 	**	This must be set before any buildings are created (if a factory is created,
    759 	**	it needs to know the BuildLevel for the sidebar.)
    760 	*/
    761 	if (GameToPlay == GAME_NORMAL) {
    762 		/*
    763 		** In this function we are only dealing with custom maps, so set based on the BuildLevel from the map, or 98 if none.
    764 		** ST - 4/22/2020 5:14PM
    765 		*/
    766 		BuildLevel = WWGetPrivateProfileInt("Basic", "BuildLevel", 98, buffer);
    767 	}
    768 
    769 	/*
    770 	**	Jurassic scenarios are allowed to build the full multiplayer set
    771 	**	of objects.
    772 	*/
    773 	if (Special.IsJurassic && AreThingiesEnabled) {
    774 		BuildLevel = 98;
    775 	}
    776 
    777 	/*
    778 	**	Fetch the transition theme for this scenario.
    779 	*/
    780 	TransitTheme = THEME_NONE;
    781 	WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer);
    782 	TransitTheme = Theme.From_Name(buf);
    783 
    784 	/*
    785 	**	Read in the team-type data. The team types must be created before any
    786 	**	triggers can be created.
    787 	*/
    788 	TeamTypeClass::Read_INI(buffer);
    789 	Call_Back();
    790 
    791 	/*
    792 	**	Read in the specific information for each of the house types.  This creates
    793 	**	the houses of different types.
    794 	*/
    795 	HouseClass::Read_INI(buffer);
    796 	Call_Back();
    797 
    798 	/*
    799 	**	Read in the trigger data. The triggers must be created before any other
    800 	**	objects can be initialized.
    801 	*/
    802 	TriggerClass::Read_INI(buffer);
    803 	Call_Back();
    804 
    805 	/*
    806 	**	Read in the map control values. This includes dimensions
    807 	**	as well as theater information.
    808 	*/
    809 	Map.Read_INI(buffer);
    810 	Call_Back();
    811 
    812 	/*
    813 	**	Assign PlayerPtr by reading the player's house from the INI;
    814 	**	Must be done before any TechnoClass objects are created.
    815 	*/
    816 	//	if (GameToPlay == GAME_NORMAL && (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer == SCEN_PLAYER_NOD)) {
    817 	if (GameToPlay == GAME_NORMAL) {
    818 		WWGetPrivateProfileString("Basic", "Player", "GoodGuy", buf, 127, buffer);
    819 		CarryOverPercent = WWGetPrivateProfileInt("Basic", "CarryOverMoney", 100, buffer);
    820 		CarryOverPercent = Cardinal_To_Fixed(100, CarryOverPercent);
    821 		CarryOverCap = WWGetPrivateProfileInt("Basic", "CarryOverCap", -1, buffer);
    822 
    823 		PlayerPtr = HouseClass::As_Pointer(HouseTypeClass::From_Name(buf));
    824 		PlayerPtr->IsHuman = true;
    825 		int carryover;
    826 		if (CarryOverCap != -1) {
    827 			carryover = MIN((int)Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent), CarryOverCap);
    828 		}
    829 		else {
    830 			carryover = Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent);
    831 		}
    832 		PlayerPtr->Credits += carryover;
    833 		PlayerPtr->InitialCredits += carryover;
    834 
    835 		if (Special.IsJurassic) {
    836 			PlayerPtr->ActLike = Whom;
    837 		}
    838 
    839 		if (Special.IsEasy) {
    840 			PlayerPtr->Assign_Handicap(DIFF_EASY);
    841 		}
    842 		else if (Special.IsDifficult) {
    843 			PlayerPtr->Assign_Handicap(DIFF_HARD);
    844 		}
    845 	}
    846 	else {
    847 
    848 #ifdef OBSOLETE
    849 		if (GameToPlay == GAME_NORMAL && ScenPlayer == SCEN_PLAYER_JP) {
    850 			PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4);
    851 			PlayerPtr->IsHuman = true;
    852 			PlayerPtr->Credits += CarryOverMoney;
    853 			PlayerPtr->InitialCredits += CarryOverMoney;
    854 			PlayerPtr->ActLike = Whom;
    855 		}
    856 		else {
    857 			Assign_Houses();
    858 		}
    859 #endif
    860 		//Call new Assign_Houses function. ST - 6/25/2019 11:07AM
    861 		//Assign_Houses();
    862 		GlyphX_Assign_Houses();
    863 	}
    864 
    865 	/*
    866 	**	Attempt to read the map's binary image file; if fails, read the
    867 	**	template data from the INI, for backward compatibility
    868 	*/
    869 	if (fresh) {
    870 		if (!Map.Read_Binary_File(bin_file_name, &ScenarioCRC)) {
    871 			TemplateClass::Read_INI(buffer);
    872 		}
    873 	}
    874 	Call_Back();
    875 
    876 	/*
    877 	**	Read in and place the 3D terrain objects.
    878 	*/
    879 	TerrainClass::Read_INI(buffer);
    880 	Call_Back();
    881 
    882 	/*
    883 	**	Read in and place the units (all sides).
    884 	*/
    885 	UnitClass::Read_INI(buffer);
    886 	Call_Back();
    887 
    888 	AircraftClass::Read_INI(buffer);
    889 	Call_Back();
    890 
    891 	/*
    892 	**	Read in and place the infantry units (all sides).
    893 	*/
    894 	InfantryClass::Read_INI(buffer);
    895 	Call_Back();
    896 
    897 	/*
    898 	**	Read in and place all the buildings on the map.
    899 	*/
    900 	BuildingClass::Read_INI(buffer);
    901 	Call_Back();
    902 
    903 	/*
    904 	**	Read in the AI's base information.
    905 	*/
    906 	Base.Read_INI(buffer);
    907 	Call_Back();
    908 
    909 	/*
    910 	**	Read in any normal overlay objects.
    911 	*/
    912 	OverlayClass::Read_INI(buffer);
    913 	Call_Back();
    914 
    915 	/*
    916 	**	Read in any smudge overlays.
    917 	*/
    918 	SmudgeClass::Read_INI(buffer);
    919 	Call_Back();
    920 
    921 	/*
    922 	**	Read in any briefing text.
    923 	*/
    924 	char * stage = &BriefingText[0];
    925 	*stage = '\0';
    926 	int index = 1;
    927 
    928 	/*
    929 	**	Build the full text of the mission objective.
    930 	*/
    931 	for (;;) {
    932 		int len = (sizeof(BriefingText) - strlen(BriefingText)) - 1;
    933 		if (len <= 0) {
    934 			break;
    935 		}
    936 
    937 		char buff[16];
    938 		sprintf(buff, "%d", index++);
    939 		*stage = '\0';
    940 		WWGetPrivateProfileString("Briefing", buff, "", stage, len, buffer);
    941 		if (strlen(stage) == 0) break;
    942 		strcat(stage, " ");
    943 		stage += strlen(stage);
    944 	}
    945 
    946 	/*
    947 	**	If the briefing text could not be found in the INI file, then search
    948 	**	the mission.ini file.
    949 	*/
    950 	if (BriefingText[0] == '\0') {
    951 		memset(_ShapeBuffer, '\0', _ShapeBufferSize);
    952 		CCFileClass("MISSION.INI").Read(_ShapeBuffer, _ShapeBufferSize);
    953 
    954 		char * buffer = (char *)Add_Long_To_Pointer(_ShapeBuffer, strlen(_ShapeBuffer));
    955 		char * work = &BriefingText[0];
    956 		int index = 1;
    957 
    958 		/*
    959 		**	Build the full text of the mission objective.
    960 		*/
    961 		for (;;) {
    962 			char buff[16];
    963 
    964 			sprintf(buff, "%d", index++);
    965 			*work = '\0';
    966 			WWGetPrivateProfileString(root, buff, "", work, (sizeof(BriefingText) - strlen(BriefingText)) - 1, _ShapeBuffer);
    967 			if (strlen(work) == 0) break;
    968 			strcat(work, " ");
    969 			work += strlen(work);
    970 		}
    971 	}
    972 
    973 	/*
    974 	**	Perform a final overpass of the map. This handles smoothing of certain
    975 	**	types of terrain (tiberium).
    976 	*/
    977 	Map.Overpass();
    978 	Call_Back();
    979 
    980 	/*
    981 	**	Scenario fix-up (applied on loaded games as well)
    982 	*/
    983 	Fixup_Scenario();
    984 
    985 	/*
    986 	**	Multi-player last-minute fixups:
    987 	**	- If computer players are disabled, remove all computer-owned houses
    988 	** - Otherwise, set MPlayerBlitz to 0 or 1, randomly
    989 	**	- If bases are disabled, create the scenario dynamically
    990 	**	- Remove any flag spot overlays lying around
    991 	**	- If capture-the-flag is enabled, assign flags to cells.
    992 	*/
    993 	if (GameToPlay != GAME_NORMAL || ScenPlayer == SCEN_PLAYER_2PLAYER ||
    994 		ScenPlayer == SCEN_PLAYER_MPLAYER) {
    995 
    996 		/*
    997 		**	If Ghosts are disabled and we're not editing, remove computer players
    998 		**	(Must be done after all objects are read in from the INI)
    999 		*/
   1000 		if (!MPlayerGhosts && !Debug_Map) {
   1001 			//Remove_AI_Players();		    // Done elsewhere now. ST - 6/25/2019 12:33PM
   1002 		}
   1003 		else {
   1004 
   1005 			/*
   1006 			** If Ghosts are on, set up their houses for blitzing the humans
   1007 			*/
   1008 #ifndef USE_RA_AI
   1009 			MPlayerBlitz = IRandom(0, 1);					// 1 = computer will blitz
   1010 
   1011 			if (MPlayerBlitz) {
   1012 				if (MPlayerBases) {
   1013 					rndmax = 14000;
   1014 					rndmin = 10000;
   1015 				}
   1016 				else {
   1017 					rndmax = 8000;
   1018 					rndmin = 4000;
   1019 				}
   1020 
   1021 				for (int i = 0; i < MPlayerMax; i++) {
   1022 					HousesType house = (HousesType)(i + (int)HOUSE_MULTI1);
   1023 					HouseClass *housep = HouseClass::As_Pointer(house);
   1024 					if (housep) {	//Added. ST - 6/25/2019 11:37AM
   1025 						housep->BlitzTime = IRandom(rndmin, rndmax);
   1026 					}
   1027 				}
   1028 
   1029 			}
   1030 #else // USE_RA_AI
   1031 			MPlayerBlitz = 0;
   1032 #endif // USE_RA_AI
   1033 		}
   1034 
   1035 		/*
   1036 		**	Units must be created for each house.  If bases are ON, this routine
   1037 		**	will create an MCV along with the units; otherwise, it will just create
   1038 		**	a whole bunch of units.  MPlayerUnitCount is the total # of units
   1039 		**	to create.
   1040 		*/
   1041 		if (!Debug_Map) {
   1042 			int save_init = ScenarioInit;			// turn ScenarioInit off
   1043 			ScenarioInit = 0;
   1044 			Create_Units();
   1045 			ScenarioInit = save_init;				// turn ScenarioInit back on
   1046 		}
   1047 
   1048 		/*
   1049 		**	Place crates if MPlayerGoodies is on.
   1050 		*/
   1051 		if (MPlayerGoodies) {
   1052 			for (int index = 0; index < MPlayerCount; index++) {
   1053 				//for (int index = 0; index < 200; index++) {  // Lots of crates for test		
   1054 				Map.Place_Random_Crate();
   1055 			}
   1056 		}
   1057 	}
   1058 
   1059 	Call_Back();
   1060 
   1061 	/*
   1062 	**	Return with flag saying that the scenario file was read.
   1063 	*/
   1064 	ScenarioInit--;
   1065 	return(true);
   1066 }
   1067 
   1068 
   1069 
   1070 /***********************************************************************************************
   1071  * Read_Movies_From_Scenario_Ini -- Reads just the movie files from the scenario.              *
   1072  *                                                                                             *
   1073  *                                                                                             *
   1074  * INPUT:                                                                                      *
   1075  *          root      root filename for scenario file to read                                  *
   1076  *                                                                                             *
   1077  *          fresh      true = should the current scenario be cleared?                          *
   1078  *                                                                                             *
   1079  * OUTPUT:  bool; Was the scenario read successful?                                            *
   1080  *                                                                                             *
   1081  * WARNINGS:   none                                                                            *
   1082  *                                                                                             *
   1083  * HISTORY:                                                                                    *
   1084  *   10/14/2019 JAS : Created.                                                                 *
   1085  *=============================================================================================*/
   1086 bool Read_Movies_From_Scenario_Ini(char *root, bool fresh)
   1087 {
   1088 	char *buffer;				// Scenario.ini staging buffer pointer.
   1089 	char fname[_MAX_FNAME + _MAX_EXT];			// full INI filename
   1090 //	char buf[128];				// Working string staging buffer.
   1091 #ifndef USE_RA_AI
   1092 	int rndmax;
   1093 	int rndmin;
   1094 #endif //USE_RA_AI
   1095 	int len;
   1096 	unsigned char val;
   1097 
   1098 	ScenarioInit++;
   1099 
   1100 	/*
   1101 	**	Fetch working pointer to the INI staging buffer. Make sure that the buffer
   1102 	**	is cleared out before proceeding.  (Don't use the HidPage for this, since
   1103 	**	the HidPage may be needed for various uncompressions during the INI
   1104 	**	parsing.)
   1105 	*/
   1106 	buffer = (char *)_ShapeBuffer;
   1107 	memset(buffer, '\0', _ShapeBufferSize);
   1108 
   1109 	if (fresh) {
   1110 		Clear_Scenario();
   1111 	}
   1112 
   1113 	/*
   1114 	** If we are not dealing with scenario 1, or a multi player scenario
   1115 	** then make sure the correct disk is in the drive.
   1116 	*/
   1117 	if (RequiredCD != -2) {
   1118 		if (Scenario >= 20 && Scenario < 60 && GameToPlay == GAME_NORMAL) {
   1119 			RequiredCD = 2;
   1120 		}
   1121 		else {
   1122 			if (Scenario != 1) {
   1123 				if (Scenario >= 60) {
   1124 					RequiredCD = -1;
   1125 				}
   1126 				else {
   1127 					switch (ScenPlayer) {
   1128 					case SCEN_PLAYER_GDI:
   1129 						RequiredCD = 0;
   1130 						break;
   1131 					case SCEN_PLAYER_NOD:
   1132 						RequiredCD = 1;
   1133 						break;
   1134 					default:
   1135 						RequiredCD = -1;
   1136 						break;
   1137 					}
   1138 				}
   1139 			}
   1140 			else {
   1141 				RequiredCD = -1;
   1142 			}
   1143 		}
   1144 	}
   1145 	if (!Force_CD_Available(RequiredCD)) {
   1146 		Prog_End("Read_Scenario_Ini - CD not found", true);
   1147 		if (!RunningAsDLL) {
   1148 			exit(EXIT_FAILURE);
   1149 		}
   1150 	}
   1151 
   1152 	/*
   1153 	**	Create scenario filename and read the file.
   1154 	*/
   1155 
   1156 	sprintf(fname, "%s.INI", root);
   1157 	CCFileClass file(fname);
   1158 	if (!file.Is_Available()) {
   1159 		GlyphX_Debug_Print("Failed to find scenario file");
   1160 		GlyphX_Debug_Print(fname);
   1161 		return(false);
   1162 	}
   1163 	else {
   1164 
   1165 		GlyphX_Debug_Print("Opened scenario file");
   1166 		GlyphX_Debug_Print(fname);
   1167 
   1168 		file.Read(buffer, _ShapeBufferSize - 1);
   1169 	}
   1170 
   1171 	/*
   1172 	** Init the Scenario CRC value
   1173 	*/
   1174 	ScenarioCRC = 0;
   1175 	len = strlen(buffer);
   1176 	for (int i = 0; i < len; i++) {
   1177 		val = (unsigned char)buffer[i];
   1178 #ifndef DEMO
   1179 		Add_CRC(&ScenarioCRC, (unsigned long)val);
   1180 #endif
   1181 	}
   1182 
   1183 	/*
   1184 	**	Fetch the appropriate movie names from the INI file.
   1185 	*/
   1186 	WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer);
   1187 	WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer);
   1188 	WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer);
   1189 	WWGetPrivateProfileString("Basic", "Win2", "x", WinMovie2, sizeof(WinMovie2), buffer);
   1190 	WWGetPrivateProfileString("Basic", "Win3", "x", WinMovie3, sizeof(WinMovie3), buffer);
   1191 	WWGetPrivateProfileString("Basic", "Win4", "x", WinMovie4, sizeof(WinMovie4), buffer);
   1192 	WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer);
   1193 	WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer);
   1194 
   1195 	/*
   1196 	**	Fetch the transition theme for this scenario.
   1197 	*/
   1198 	TransitTheme = THEME_NONE;
   1199 	WWGetPrivateProfileString("Basic", "Theme", "No Theme", MovieThemeName, sizeof(MovieThemeName), buffer);
   1200 	//TransitTheme = Theme.From_Name(buf);
   1201 
   1202 	/*
   1203 	**	Return with flag saying that the scenario file was read.
   1204 	*/
   1205 	ScenarioInit--;
   1206 	return(true);
   1207 }
   1208 
   1209 
   1210 
   1211 /***********************************************************************************************
   1212  * Write_Scenario_Ini -- Write the scenario INI file.                                          *
   1213  *                                                                                             *
   1214  * INPUT:                                                                                      *
   1215  *      root      root filename for the scenario                                               *
   1216  *                                                                                             *
   1217  * OUTPUT:                                                                                     *
   1218  *      none.                                                                                  *
   1219  *                                                                                             *
   1220  * WARNINGS:                                                                                   *
   1221  *      none.                                                                                  *
   1222  *                                                                                             *
   1223  * HISTORY:                                                                                    *
   1224  *   10/07/1992 JLB : Created.                                                                 *
   1225  *   05/11/1995 JLB : Updates movie data.                                                      *
   1226  *=============================================================================================*/
   1227 void Write_Scenario_Ini(char *root)
   1228 {
   1229 #ifndef CHEAT_KEYS
   1230 	root = root;
   1231 #else
   1232 	char * buffer;			// Scenario.ini staging buffer pointer.
   1233 	char fname[_MAX_FNAME+_MAX_EXT];		// full scenario name
   1234 	HousesType house;
   1235 	CCFileClass file;
   1236 
   1237 	/*
   1238 	**	Get a working pointer to the INI staging buffer. Make sure that the buffer
   1239 	**	starts cleared out of any data.
   1240 	*/
   1241 	buffer = (char *)_ShapeBuffer;
   1242 	memset(buffer, '\0', _ShapeBufferSize);
   1243 
   1244 	switch (ScenPlayer) {
   1245 		case SCEN_PLAYER_GDI:
   1246 			house = HOUSE_GOOD;
   1247 			break;
   1248 
   1249 		case SCEN_PLAYER_NOD:
   1250 			house = HOUSE_BAD;
   1251 			break;
   1252 
   1253 		case SCEN_PLAYER_JP:
   1254 			house = HOUSE_JP;
   1255 			break;
   1256 
   1257 		default:
   1258 			house = HOUSE_MULTI1;
   1259 			break;
   1260 	}
   1261 
   1262 	/*
   1263 	**	Create scenario filename and clear the buffer to empty.
   1264 	*/
   1265 	sprintf(fname,"%s.INI",root);
   1266 	file.Set_Name(fname);
   1267 	if (file.Is_Available()) {
   1268 //		file.Open(READ);
   1269 		file.Read(buffer, _ShapeBufferSize-1);
   1270 //		file.Close();
   1271 	} else {
   1272 		sprintf(buffer, "; Scenario %d control for house %s.\r\n", Scenario, HouseTypeClass::As_Reference(house).IniName);
   1273 	}
   1274 
   1275 	WWWritePrivateProfileString("Basic", "Intro", IntroMovie, buffer);
   1276 	WWWritePrivateProfileString("Basic", "Brief", BriefMovie, buffer);
   1277 	WWWritePrivateProfileString("Basic", "Win", WinMovie, buffer);
   1278 	WWWritePrivateProfileString("Basic", "Win2", WinMovie, buffer);
   1279 	WWWritePrivateProfileString("Basic", "Win3", WinMovie, buffer);
   1280 	WWWritePrivateProfileString("Basic", "Win4", WinMovie, buffer);
   1281 	WWWritePrivateProfileString("Basic", "Lose", LoseMovie, buffer);
   1282 	WWWritePrivateProfileString("Basic", "Action", ActionMovie, buffer);
   1283 	WWWritePrivateProfileString("Basic", "Player", PlayerPtr->Class->IniName, buffer);
   1284 	WWWritePrivateProfileString("Basic", "Theme", Theme.Base_Name(TransitTheme), buffer);
   1285 	WWWritePrivateProfileInt("Basic", "BuildLevel", BuildLevel, buffer);
   1286 	WWWritePrivateProfileInt("Basic", "CarryOverMoney", Fixed_To_Cardinal(100, CarryOverPercent), buffer);
   1287 	WWWritePrivateProfileInt("Basic", "CarryOverCap", CarryOverCap, buffer);
   1288 
   1289 	TeamTypeClass::Write_INI(buffer, true);
   1290 	TriggerClass::Write_INI(buffer, true);
   1291 	Map.Write_INI(buffer);
   1292 	Map.Write_Binary(root);
   1293 	HouseClass::Write_INI(buffer);
   1294 	UnitClass::Write_INI(buffer);
   1295 	InfantryClass::Write_INI(buffer);
   1296 	BuildingClass::Write_INI(buffer);
   1297 	TerrainClass::Write_INI(buffer);
   1298 	OverlayClass::Write_INI(buffer);
   1299 	SmudgeClass::Write_INI(buffer);
   1300 
   1301 	Base.Write_INI(buffer);
   1302 
   1303 	/*
   1304 	**	Write the scenario data out to a file.
   1305 	*/
   1306 //	file.Open(WRITE);
   1307 	file.Write(buffer, strlen(buffer));
   1308 //	file.Close();
   1309 
   1310 	/*
   1311 	**	Now update the Master INI file, containing the master list of triggers & teams
   1312 	*/
   1313 	memset(buffer, '\0', _ShapeBufferSize);
   1314 
   1315 	file.Set_Name("MASTER.INI");
   1316 	if (file.Is_Available()) {
   1317 //		file.Open(READ);
   1318 		file.Read(buffer, _ShapeBufferSize-1);
   1319 //		file.Close();
   1320 	} else {
   1321 		sprintf(buffer, "; Master Trigger & Team List.\r\n");
   1322 	}
   1323 
   1324 	TeamTypeClass::Write_INI(buffer, false);
   1325 	TriggerClass::Write_INI(buffer, false);
   1326 
   1327 //	file.Open(WRITE);
   1328 	file.Write(buffer,strlen(buffer));
   1329 //	file.Close();
   1330 #endif
   1331 }
   1332 
   1333 
   1334 /***********************************************************************************************
   1335  * Assign_Houses -- Assigns multiplayer houses to various players                              *
   1336  *                                                                                             *
   1337  * INPUT:                                                                                      *
   1338  *      none.                                                                                  *
   1339  *                                                                                             *
   1340  * OUTPUT:                                                                                     *
   1341  *      none.                                                                                  *
   1342  *                                                                                             *
   1343  * WARNINGS:                                                                                   *
   1344  *      none.                                                                                  *
   1345  *                                                                                             *
   1346  * HISTORY:                                                                                    *
   1347  *   06/09/1995 BRR : Created.                                                                 *
   1348  *   07/14/1995 JLB : Records name of player in house structure.                               *
   1349  *=============================================================================================*/
   1350 static void Assign_Houses(void)
   1351 {
   1352 	HousesType house;
   1353 	HousesType pref_house;
   1354 	HouseClass *housep;
   1355 	bool house_used[MAX_PLAYERS];	// true = this house is in use
   1356 	bool color_used[16];				// true = this color is in use. We have more than 6 color options now, so bumped this to 16. ST - 6/19/2019 5:18PM
   1357 	int i,j;
   1358 	PlayerColorType color;
   1359 	HousesType house2;
   1360 	HouseClass *housep2;
   1361 
   1362 	/*
   1363 	**	Init the 'used' flag for all houses & colors to 0
   1364 	*/
   1365 	for (i = 0; i < MAX_PLAYERS; i++) {
   1366 		house_used[i] = false;
   1367 	}
   1368 	for (i = 0; i < 16; i++) {
   1369 		color_used[i] = false;
   1370 	}
   1371 
   1372 	/*
   1373 	**	For each player, randomly pick a house
   1374 	*/
   1375 	for (i = 0; i < MPlayerCount; i++) {
   1376 		j = Random_Pick(0, MPlayerMax-1);
   1377 
   1378 		/*
   1379 		**	If this house was already selected, decrement 'i' & keep looping.
   1380 		*/
   1381 		if (house_used[j]) {
   1382 			i--;
   1383 			continue;
   1384 		}
   1385 
   1386 		/*
   1387 		**	Set the house, preferred house (GDI/NOD), color, and actual house;
   1388 		**	get a pointer to the house instance
   1389 		*/
   1390 		house = (HousesType)(j + (int)HOUSE_MULTI1);
   1391 		pref_house = MPlayerID_To_HousesType(MPlayerID[i]);
   1392 		color = MPlayerID_To_ColorIndex(MPlayerID[i]);
   1393 		housep = HouseClass::As_Pointer(house);
   1394 		MPlayerHouses[i] = house;
   1395 
   1396 		/*
   1397 		**	Mark this house & color as used
   1398 		*/
   1399 		house_used[j] = true;
   1400 		color_used[color] = true;
   1401 
   1402 		/*
   1403 		**	Set the house's IsHuman, Credits, ActLike, & RemapTable
   1404 		*/
   1405 		memset((char *)housep->Name, 0, MPLAYER_NAME_MAX);
   1406 		strncpy((char *)housep->Name, MPlayerNames[i], MPLAYER_NAME_MAX-1);
   1407 		housep->IsHuman = true;
   1408 		housep->Init_Data(color, pref_house, MPlayerCredits);
   1409 
   1410 		/*
   1411 		**	If this ID is for myself, set up PlayerPtr
   1412 		*/
   1413 		if (MPlayerID[i] == MPlayerLocalID) {
   1414 			PlayerPtr = housep;
   1415 		}
   1416 	}
   1417 
   1418 	/*
   1419 	**	For all houses not assigned to a player, set them up for computer use
   1420 	*/
   1421 	for (i = 0; i < MPlayerMax; i++) {
   1422 		if (house_used[i] == false) {
   1423 
   1424 			/*
   1425 			**	Set the house, preferred house (GDI/NOD), and color; get a pointer
   1426 			**	to the house instance
   1427 			*/
   1428 			house = (HousesType)(i + (int)HOUSE_MULTI1);
   1429 			pref_house = (HousesType)(IRandom(0, 1) + (int)HOUSE_GOOD);
   1430 			for (;;) {
   1431 				color = Random_Pick(REMAP_FIRST, REMAP_LAST);
   1432 				if (color_used[color] == false) {
   1433 					break;
   1434 				}
   1435 			}
   1436 			housep = HouseClass::As_Pointer (house);
   1437 
   1438 			/*
   1439 			**	Mark this house & color as used
   1440 			*/
   1441 			house_used[i] = true;
   1442 			color_used[color] = true;
   1443 
   1444 			/*
   1445 			**	Set the house's IsHuman, Credits, ActLike, & RemapTable
   1446 			*/
   1447 			housep->IsHuman = false;
   1448 			housep->Init_Data(color, pref_house, MPlayerCredits);
   1449 		}
   1450 	}
   1451 
   1452 	/*
   1453 	**	Now make all computer-owned houses allies of each other.
   1454 	*/
   1455 	for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) {
   1456 		housep = HouseClass::As_Pointer(house);
   1457 		if (housep->IsHuman)
   1458 			continue;
   1459 
   1460 		for (house2 = HOUSE_MULTI1; house2 < (HOUSE_MULTI1 + MPlayerMax); house2++) {
   1461 			housep2 = HouseClass::As_Pointer (house2);
   1462 			if (housep2->IsHuman)
   1463 				continue;
   1464 			housep->Make_Ally(house2);
   1465 		}
   1466 	}
   1467 }
   1468 
   1469 
   1470 /***********************************************************************************************
   1471  * Remove_AI_Players -- Removes the computer AI houses & their units                           *
   1472  *                                                                                             *
   1473  * INPUT:                                                                                      *
   1474  *      none.                                                                                  *
   1475  *                                                                                             *
   1476  * OUTPUT:                                                                                     *
   1477  *      none.                                                                                  *
   1478  *                                                                                             *
   1479  * WARNINGS:                                                                                   *
   1480  *      none.                                                                                  *
   1481  *                                                                                             *
   1482  * HISTORY:                                                                                    *
   1483  *   06/09/1995 BRR : Created.                                                                 *
   1484  *=============================================================================================*/
   1485 static void Remove_AI_Players(void)
   1486 {
   1487 	int i;
   1488 	HousesType house;
   1489 	HouseClass *housep;
   1490 
   1491 	for (i = 0; i < MAX_PLAYERS; i++) {
   1492 		house = (HousesType)(i + (int)HOUSE_MULTI1);
   1493 		housep = HouseClass::As_Pointer (house);
   1494 		if (housep->IsHuman == false) {
   1495 			housep->Clobber_All();
   1496 		}
   1497 	}
   1498 }
   1499 
   1500 #define USE_GLYPHX_START_LOCATIONS 1
   1501 
   1502 /***********************************************************************************************
   1503  * Create_Units -- Creates infantry & units, for non-base multiplayer                          *
   1504  *                                                                                             *
   1505  * This routine uses data tables to determine which units to create for either                 *
   1506  * a GDI or NOD house, and how many of each.                                                   *
   1507  *                                                                                             *
   1508  * It also sets each house's FlagHome & FlagLocation to the Waypoint selected                  *
   1509  * as that house's "home" cell.                                                                *
   1510  *                                                                                             *
   1511  *   ------------------ Unit Summary: -------------------------------                          *
   1512  *   UNIT_MTANK               Medium tank (M1).            GDI      7                          *
   1513  *   UNIT_JEEP               4x4 jeep replacement.      GDI      5                             *
   1514  *   UNIT_MLRS               MLRS rocket launcher.      GDI      99                            *
   1515  *   UNIT_APC                  APC.                        GDI      10                         *
   1516  *   UNIT_HTANK               Heavy tank (Mammoth).      GDI      13                           *
   1517  *                                                                                             *
   1518  *   UNIT_LTANK               Light tank ('Bradly').      NOD      5                           *
   1519  *   UNIT_BUGGY               Rat patrol dune buggy type NOD      5                            *
   1520  *   UNIT_ARTY               Artillery unit.            NOD      10                            *
   1521  *   UNIT_FTANK               Flame thrower tank.         NOD      11                          *
   1522  *   UNIT_STANK               Stealth tank (Romulan).      NOD      13                         *
   1523  *   UNIT_BIKE               Nod recon motor-bike.      NOD      99                            *
   1524  *                                                                                             *
   1525  *   ~1/3 chance of getting: {UNIT_MHQ,               Mobile Head Quarters.                    *
   1526  *                                                                                             *
   1527  *   ------------------ Infantry Summary: -------------------------------                      *
   1528  *   INFANTRY_E1,            Mini-gun armed.            GDI/NOD                                *
   1529  *   INFANTRY_E2,            Grenade thrower.            GDI                                   *
   1530  *   INFANTRY_E3,            Rocket launcher.            NOD                                   *
   1531  *   INFANTRY_E6,            Rocket launcher             GDI                                   *
   1532  *   INFANTRY_E4,            Flame thrower equipped.      NOD                                  *
   1533  *   INFANTRY_RAMBO,         Commando.                  GDI/NOD                                *
   1534  *                                                                                             *
   1535  * INPUT:                                                                                      *
   1536  *      none.                                                                                  *
   1537  *                                                                                             *
   1538  * OUTPUT:                                                                                     *
   1539  *      none.                                                                                  *
   1540  *                                                                                             *
   1541  * WARNINGS:                                                                                   *
   1542  *      none.                                                                                  *
   1543  *                                                                                             *
   1544  * HISTORY:                                                                                    *
   1545  *   06/09/1995 BRR : Created.                                                                 *
   1546  *=============================================================================================*/
   1547 static int ReserveInfantryIndex = 0;
   1548 static void Reserve_Infantry()
   1549 {
   1550 	if (Infantry.Count() == Infantry.Length()) {
   1551 		delete Infantry.Ptr(ReserveInfantryIndex);
   1552 		ReserveInfantryIndex = (ReserveInfantryIndex + 1) % Infantry.Length();
   1553 	}
   1554 }
   1555 
   1556 static int ReserveUnitIndex = 0;
   1557 static void Reserve_Unit()
   1558 {
   1559 	if (Units.Count() == Units.Length()) {
   1560 		delete Units.Ptr(ReserveUnitIndex);
   1561 		ReserveUnitIndex = (ReserveUnitIndex + 1) % Units.Length();
   1562 	}
   1563 }
   1564 
   1565 static void Create_Units(void)
   1566 {
   1567 	enum {
   1568 		NUM_UNIT_CATEGORIES = 8,
   1569 		NUM_INFANTRY_CATEGORIES = 5,
   1570 	};
   1571 
   1572 	static struct {
   1573 		int MinLevel;
   1574 		int GDICount;
   1575 		UnitType GDIType;
   1576 		int NODCount;
   1577 		UnitType NODType;
   1578 	} utable[] = {
   1579 		{0,	1,UNIT_MTANK,	2,UNIT_LTANK},
   1580 		{2,	1,UNIT_JEEP,	1,UNIT_BUGGY},
   1581 		{3,	1,UNIT_MLRS,	1,UNIT_ARTY},
   1582 		{4,	1,UNIT_APC,		2,UNIT_BUGGY},
   1583 		{5,	1,UNIT_JEEP,	1,UNIT_BIKE},
   1584 		{5,	2,UNIT_JEEP,	1,UNIT_FTANK},
   1585 		{6,	1,UNIT_MSAM,	1,UNIT_MSAM},
   1586 		{7,	1,UNIT_HTANK,	2,UNIT_STANK},
   1587 	};
   1588 	static int num_units[NUM_UNIT_CATEGORIES];		// # of each type of unit to create
   1589 	int tot_units;												// total # units to create
   1590 
   1591 	static struct {
   1592 		int MinLevel;
   1593 		int GDICount;
   1594 		InfantryType GDIType;
   1595 		int NODCount;
   1596 		InfantryType NODType;
   1597 	} itable[] = {
   1598 		{0,	1,INFANTRY_E1,	1,INFANTRY_E1},
   1599 		{1,	1,INFANTRY_E2,	1,INFANTRY_E3},
   1600 		{3,	1,INFANTRY_E3,	1,INFANTRY_E3},
   1601 		{5,	1,INFANTRY_E3,	1,INFANTRY_E4},
   1602 		{7,	1,INFANTRY_RAMBO,	1,INFANTRY_RAMBO},
   1603 	};
   1604 	static int num_infantry[NUM_INFANTRY_CATEGORIES];// # of each type of infantry to create
   1605 	int tot_infantry;											// total # infantry to create
   1606 
   1607 	CELL waypts[26];
   1608 //	CELL sorted_waypts[26];
   1609 	int num_waypts;
   1610 
   1611 	HousesType h;			// house loop counter
   1612 	HouseClass *hptr;		// ptr to house being processed
   1613 
   1614 	CELL centroid;			// centroid of this house's stuff
   1615 //	int try_count;			// # times we've tried to select a centroid
   1616 	CELL centerpt;			// centroid for a category of objects, as a CELL
   1617 
   1618 	int u_limit;			// last allowable index of units for this BuildLevel
   1619 	int i_limit;			// last allowable index of infantry for this BuildLevel
   1620 	TechnoClass *obj;		// newly-created object
   1621 	int i,j,k;				// loop counters
   1622 	int scaleval;			// value to scale # units or infantry
   1623 
   1624 	ReserveInfantryIndex = ReserveUnitIndex = 0;
   1625 
   1626 	/*------------------------------------------------------------------------
   1627 	For the current BuildLevel, find the max allowable index into the tables
   1628 	------------------------------------------------------------------------*/
   1629 	for (i = 0; i < NUM_UNIT_CATEGORIES; i++) {
   1630 		if (BuildLevel >= (unsigned)utable[i].MinLevel)
   1631 			u_limit = i;
   1632 	}
   1633 	for (i = 0; i < NUM_INFANTRY_CATEGORIES; i++) {
   1634 		if (BuildLevel >= (unsigned)utable[i].MinLevel)
   1635 			i_limit = i;
   1636 	}
   1637 
   1638 	/*------------------------------------------------------------------------
   1639 	Compute how many of each buildable category to create
   1640 	------------------------------------------------------------------------*/
   1641 	/*........................................................................
   1642 	Compute allowed # units
   1643 	........................................................................*/
   1644 	tot_units = (MPlayerUnitCount * 2) / 3;
   1645 //	tot_units = MAX(tot_units, 1);
   1646 
   1647 	/*........................................................................
   1648 	Init # of each category to 0
   1649 	........................................................................*/
   1650 	for (i = 0; i <= u_limit; i++)
   1651 		num_units[i] = 0;
   1652 
   1653 	/*........................................................................
   1654 	Increment # of each category, until we've used up all units
   1655 	........................................................................*/
   1656 	j = 0;
   1657 	for (i = 0; i < tot_units; i++) {
   1658 		num_units[j]++;
   1659 		j++;
   1660 		if (j > u_limit)
   1661 			j = 0;
   1662 	}
   1663 
   1664 	/*........................................................................
   1665 	Compute allowed # infantry
   1666 	........................................................................*/
   1667 	tot_infantry = MPlayerUnitCount - tot_units;
   1668 
   1669 	/*........................................................................
   1670 	Init # of each category to 0
   1671 	........................................................................*/
   1672 	for (i = 0; i <= i_limit; i++)
   1673 		num_infantry[i] = 0;
   1674 
   1675 	/*........................................................................
   1676 	Increment # of each category, until we've used up all infantry
   1677 	........................................................................*/
   1678 	j = 0;
   1679 	for (i = 0; i < tot_infantry; i++) {
   1680 		num_infantry[j]++;
   1681 		j++;
   1682 		if (j > i_limit)
   1683 			j = 0;
   1684 	}
   1685 
   1686 	/*------------------------------------------------------------------------
   1687 	Now sort all the Waypoints on the map by distance.
   1688 	------------------------------------------------------------------------*/
   1689 	num_waypts = 0;									// counts # waypoints
   1690 
   1691 	/*........................................................................
   1692 	First, copy all valid waytpoints into my 'waypts' array
   1693 	........................................................................*/
   1694 	for (i = 0; i < 26; i++) {
   1695 		if (Waypoint[i] != -1) {
   1696 			waypts[num_waypts] = Waypoint[i];
   1697 			num_waypts++;
   1698 		}
   1699 	}
   1700 
   1701 	/*........................................................................
   1702 	Now sort the 'waypts' array
   1703 	........................................................................*/
   1704 #ifndef USE_GLYPHX_START_LOCATIONS
   1705 	//Sort_Cells (waypts, num_waypts, sorted_waypts);
   1706 #endif
   1707 
   1708 	/*------------------------------------------------------------------------
   1709 	Loop through all houses.  Computer-controlled houses, with MPlayerBases
   1710 	ON, are treated as though bases are OFF (since we have no base-building
   1711 	AI logic.)
   1712 	------------------------------------------------------------------------*/
   1713 	for (h = HOUSE_MULTI1; h < (HOUSE_MULTI1 + MPlayerMax); h++) {
   1714 
   1715 		/*.....................................................................
   1716 		Get a pointer to this house; if there is none, go to the next house
   1717 		.....................................................................*/
   1718 		hptr = HouseClass::As_Pointer(h);
   1719 		if (!hptr)
   1720 			continue;
   1721 
   1722 #ifdef USE_GLYPHX_START_LOCATIONS
   1723 		/*
   1724 		** New code that respects the start locations passed in from GlyphX.
   1725 		**
   1726 		** ST - 1/8/2020 3:39PM
   1727 		*/
   1728 		centroid = waypts[hptr->StartLocationOverride];
   1729 
   1730 #else // USE_GLYPHX_START_LOCATIONS
   1731 		/*
   1732 		** Original start position logic.
   1733 		*/
   1734 
   1735 		/*.....................................................................
   1736 		Pick a random waypoint; if the chosen waypoint isn't valid, try again.
   1737 		'centroid' will be the centroid of all this house's stuff.
   1738 		.....................................................................*/
   1739 		try_count = 0;
   1740 		while (1) {
   1741 			j = IRandom(0,MPlayerMax - 1);
   1742 			if (sorted_waypts[j] != -1) {
   1743 				centroid = sorted_waypts[j];
   1744 				sorted_waypts[j] = -1;
   1745 				break;
   1746 			}
   1747 			try_count++;
   1748 
   1749 			/*..................................................................
   1750 			OK, we've tried enough; just pick any old cell at random, as long
   1751 			as it's mappable.
   1752 			..................................................................*/
   1753 			if (try_count > 200) {
   1754 				while (1) {
   1755 					centroid = IRandom(0,MAP_CELL_TOTAL - 1);
   1756 					if (Map.In_Radar(centroid))
   1757 						break;
   1758 				}
   1759 				break;
   1760 			}
   1761 		}
   1762 #endif // USE_GLYPHX_START_LOCATIONS
   1763 
   1764 		/*---------------------------------------------------------------------
   1765 		If Bases are ON, human & computer houses are treated differently
   1766 		---------------------------------------------------------------------*/
   1767 		if (MPlayerBases) {
   1768 			/*..................................................................
   1769 			- For a human-controlled house:
   1770 			  - Set 'scaleval' to 1
   1771 			  - Create an MCV
   1772 			  - Attach a flag to it for capture-the-flag mode
   1773 			..................................................................*/
   1774 			if (hptr->IsHuman) {
   1775 				scaleval = 1;
   1776 
   1777 #ifndef USE_RA_AI           // Moved to below. ST - 7/25/2019 11:21AM
   1778 				obj = new UnitClass (UNIT_MCV, h);
   1779 				if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) {
   1780 					if (!Scan_Place_Object(obj, centroid)) {
   1781 						delete obj;
   1782 						obj = NULL;
   1783 					}
   1784 				}
   1785 				if (obj) {
   1786 					hptr->FlagHome = 0;
   1787 					hptr->FlagLocation = 0;
   1788 					if (Special.IsCaptureTheFlag) {
   1789 						hptr->Flag_Attach((UnitClass *)obj,true);
   1790 					}
   1791 				}
   1792 #endif //USE_RA_AI
   1793 			} else {
   1794 
   1795 				/*..................................................................
   1796 				- For computer-controlled house:
   1797 				  - Set 'scaleval' to 3
   1798 				  - Create a Mobile HQ for capture-the-flag mode
   1799 				..................................................................*/
   1800 				// Added fix for divide by zero. ST - 6/26/2019 10:40AM
   1801 				int ai_player_count = MPlayerMax - MPlayerCount;
   1802 				//scaleval = 3 / (MPlayerMax - MPlayerCount);
   1803 				//scaleval = max(ai_player_count, 1);
   1804 				scaleval = 1;		//Set to 1 since EA QA can't beat skirmish with scaleval set higher.
   1805 				
   1806 				//if (scaleval==0) {
   1807 				//	scaleval = 1;
   1808 				//}
   1809 
   1810 #ifndef USE_RA_AI           // Give the AI an MCV below. ST - 7/25/2019 11:22AM
   1811 				if (Special.IsCaptureTheFlag) {
   1812 					obj = new UnitClass (UNIT_MHQ, h);
   1813 					if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) {
   1814 						if (!Scan_Place_Object(obj, centroid)) {
   1815 							delete obj;
   1816 							obj = NULL;
   1817 						}
   1818 					}
   1819 					hptr->FlagHome = 0;					// turn house's flag off
   1820 					hptr->FlagLocation = 0;
   1821 				}
   1822 #endif //USE_RA_AI
   1823 			}
   1824 
   1825 #ifdef USE_RA_AI
   1826 			/*
   1827 			** Moved HQ code down here, so the AI player gets one too. ST - 7/25/2019 11:21AM
   1828 			*/
   1829 			Reserve_Unit();
   1830 			obj = new UnitClass (UNIT_MCV, h);
   1831 			if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) {
   1832 				if (!Scan_Place_Object(obj, centroid)) {
   1833 					delete obj;
   1834 					obj = NULL;
   1835 				}
   1836 			}
   1837 			if (obj) {
   1838 				hptr->FlagHome = 0;
   1839 				hptr->FlagLocation = 0;
   1840 				if (Special.IsCaptureTheFlag) {
   1841 					hptr->Flag_Attach((UnitClass *)obj,true);
   1842 				}
   1843 			}
   1844 #endif //USE_RA_AI
   1845 
   1846 
   1847 		} else {
   1848 
   1849 			/*---------------------------------------------------------------------
   1850 			If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for
   1851 			capture-the-flag mode.
   1852 			---------------------------------------------------------------------*/
   1853 			scaleval = 1;
   1854 			if (Special.IsCaptureTheFlag) {
   1855 				Reserve_Unit();
   1856 				obj = new UnitClass (UNIT_MHQ, h);
   1857 				obj->Unlimbo(Cell_Coord(centroid),DIR_N);
   1858 				hptr->FlagHome = 0;					// turn house's flag off
   1859 				hptr->FlagLocation = 0;
   1860 			}
   1861 		}
   1862 
   1863 		/*---------------------------------------------------------------------
   1864 		Set the house's max # units (this is used in the Mission_Timed_Hunt())
   1865 		---------------------------------------------------------------------*/
   1866 		hptr->MaxUnit = MPlayerUnitCount * scaleval;
   1867 
   1868 		/*---------------------------------------------------------------------
   1869 		Create units for this house
   1870 		---------------------------------------------------------------------*/
   1871 		for (i = 0; i <= u_limit; i++) {
   1872 			/*..................................................................
   1873 			Find the center point for this category.
   1874 			..................................................................*/
   1875 			centerpt = Clip_Scatter(centroid,4);
   1876 
   1877 			/*..................................................................
   1878 			Place objects; loop through all unit in this category
   1879 			..................................................................*/
   1880 			for (j = 0; j < num_units[i] * scaleval; j++) {
   1881 				/*...............................................................
   1882 				Create a GDI unit
   1883 				...............................................................*/
   1884 				if (hptr->ActLike == HOUSE_GOOD) {
   1885 					for (k = 0; k < utable[i].GDICount; k++) {
   1886 						Reserve_Unit();
   1887 						obj = new UnitClass (utable[i].GDIType, h);
   1888 						if (!Scan_Place_Object(obj, centerpt)) {
   1889 							delete obj;
   1890 						} else {
   1891 							
   1892 							/*
   1893 							** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM
   1894 							*/
   1895 							//if (!hptr->IsHuman) {
   1896 							//	obj->Set_Mission(MISSION_TIMED_HUNT);
   1897 							//}
   1898 							if (!hptr->IsHuman) {
   1899 								obj->Set_Mission(MISSION_GUARD_AREA);
   1900 							}
   1901 						}
   1902 					}
   1903 				} else {
   1904 
   1905 					/*...............................................................
   1906 					Create a NOD unit
   1907 					...............................................................*/
   1908 					for (k = 0; k < utable[i].NODCount; k++) {
   1909 						Reserve_Unit();
   1910 						obj = new UnitClass (utable[i].NODType, h);
   1911 						if (!Scan_Place_Object(obj, centerpt)) {
   1912 							delete obj;
   1913 						} else {
   1914 							/*
   1915 							** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM
   1916 							*/
   1917 							//if (!hptr->IsHuman) {
   1918 							//	obj->Set_Mission(MISSION_TIMED_HUNT);
   1919 							//}
   1920 							if (!hptr->IsHuman) {
   1921 								obj->Set_Mission(MISSION_GUARD_AREA);
   1922 							}
   1923 						}
   1924 					}
   1925 				}
   1926 			}
   1927 		}
   1928 
   1929 		/*---------------------------------------------------------------------
   1930 		Create infantry
   1931 		---------------------------------------------------------------------*/
   1932 		for (i = 0; i <= i_limit; i++) {
   1933 			/*..................................................................
   1934 			Find the center point for this category.
   1935 			..................................................................*/
   1936 			centerpt = Clip_Scatter(centroid,4);
   1937 
   1938 			/*..................................................................
   1939 			Place objects; loop through all unit in this category
   1940 			..................................................................*/
   1941 			for (j = 0; j < num_infantry[i] * scaleval; j++) {
   1942 				/*...............................................................
   1943 				Create GDI infantry (Note: Unlimbo calls Enter_Idle_Mode(), which
   1944 				assigns the infantry to HUNT; we must use Set_Mission() to override
   1945 				this state.)
   1946 				...............................................................*/
   1947 				if (hptr->ActLike == HOUSE_GOOD) {
   1948 					for (k = 0; k < itable[i].GDICount; k++) {
   1949 						Reserve_Infantry();
   1950 						obj = new InfantryClass (itable[i].GDIType, h);
   1951 						if (!Scan_Place_Object(obj, centerpt)) {
   1952 							delete obj;
   1953 						} else {
   1954 							/*
   1955 							** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM
   1956 							*/
   1957 							//if (!hptr->IsHuman) {
   1958 							//	obj->Set_Mission(MISSION_TIMED_HUNT);
   1959 							//}
   1960 							if (!hptr->IsHuman) {
   1961 								obj->Set_Mission(MISSION_GUARD_AREA);
   1962 							}
   1963 						}
   1964 					}
   1965 				} else {
   1966 
   1967 					/*...............................................................
   1968 					Create NOD infantry
   1969 					...............................................................*/
   1970 					for (k = 0; k < itable[i].NODCount; k++) {
   1971 						Reserve_Infantry();
   1972 						obj = new InfantryClass (itable[i].NODType, h);
   1973 						if (!Scan_Place_Object(obj, centerpt)) {
   1974 							delete obj;
   1975 						} else {
   1976 							/*
   1977 							** Don't use MISSION_TIMED_HUNT since it can trigger blitz behavior. ST - 2/28/2020 10:51AM
   1978 							*/
   1979 							//if (!hptr->IsHuman) {
   1980 							//	obj->Set_Mission(MISSION_TIMED_HUNT);
   1981 							//}
   1982 							if (!hptr->IsHuman) {
   1983 								obj->Set_Mission(MISSION_GUARD_AREA);
   1984 							}
   1985 						}
   1986 					}
   1987 				}
   1988 			}
   1989 		}
   1990 	}
   1991 }
   1992 
   1993 
   1994 /***********************************************************************************************
   1995  * Scan_Place_Object -- places an object >near< the given cell                                 *
   1996  *                                                                                             *
   1997  * INPUT:                                                                                      *
   1998  *      obj      ptr to object to Unlimbo                                                      *
   1999  *      cell      center of search area                                                        *
   2000  *                                                                                             *
   2001  * OUTPUT:                                                                                     *
   2002  *      true = object was placed; false = it wasn't                                            *
   2003  *                                                                                             *
   2004  * WARNINGS:                                                                                   *
   2005  *      none.                                                                                  *
   2006  *                                                                                             *
   2007  * HISTORY:                                                                                    *
   2008  *   06/09/1995 BRR : Created.                                                                 *
   2009  *=============================================================================================*/
   2010 int Scan_Place_Object(ObjectClass *obj, CELL cell)
   2011 {
   2012 	int dist;				// for object placement
   2013 	FacingType rot;		// for object placement
   2014 	FacingType fcounter;	// for object placement
   2015 	int tryval;
   2016 	CELL newcell;
   2017 	TechnoClass *techno;
   2018 	int skipit;
   2019 
   2020 	/*------------------------------------------------------------------------
   2021 	First try to unlimbo the object in the given cell.
   2022 	------------------------------------------------------------------------*/
   2023 	if (Map.In_Radar(cell)) {
   2024 		techno = Map[cell].Cell_Techno();
   2025 		if (!techno || (techno->What_Am_I()==RTTI_INFANTRY &&
   2026 			obj->What_Am_I()==RTTI_INFANTRY)) {
   2027 			if (obj->Unlimbo(Cell_Coord(cell),DIR_N)) {
   2028 				return(true);
   2029 			}
   2030 		}
   2031 	}
   2032 
   2033 	/*------------------------------------------------------------------------
   2034 	Loop through distances from the given center cell; skip the center cell.
   2035 	For each distance, try placing the object along each rotational direction;
   2036 	if none are available, try each direction with a random scatter value.
   2037 	If that fails, go to the next distance.
   2038 	This ensures that the closest coordinates are filled first.
   2039 	------------------------------------------------------------------------*/
   2040 	for (dist = 1; dist < 32; dist++) {
   2041 		/*.....................................................................
   2042 		Pick a random starting direction
   2043 		.....................................................................*/
   2044 		rot = (FacingType)IRandom (FACING_N, FACING_NW);
   2045 		/*.....................................................................
   2046 		Try all directions twice
   2047 		.....................................................................*/
   2048 		for (tryval = 0 ; tryval < 2; tryval++) {
   2049 			/*..................................................................
   2050 			Loop through all directions, at this distance.
   2051 			..................................................................*/
   2052 			for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) {
   2053 
   2054 				skipit = false;
   2055 
   2056 				/*...............................................................
   2057 				Pick a coordinate along this directional axis
   2058 				...............................................................*/
   2059 				newcell = Clip_Move(cell, rot, dist);
   2060 
   2061 				/*...............................................................
   2062 				If this is our second try at this distance, add a random scatter
   2063 				to the desired cell, so our units aren't all aligned along spokes.
   2064 				...............................................................*/
   2065 				if (tryval > 0)
   2066 					newcell = Clip_Scatter (newcell, 1);
   2067 
   2068 				/*...............................................................
   2069 				If, by randomly scattering, we've chosen the exact center, skip
   2070 				it & try another direction.
   2071 				...............................................................*/
   2072 				if (newcell==cell)
   2073 					skipit = true;
   2074 
   2075 				if (!skipit) {
   2076 					/*............................................................
   2077 					Only attempt to Unlimbo the object if:
   2078 					- there is no techno in the cell
   2079 					- the techno in the cell & the object are both infantry
   2080 					............................................................*/
   2081 					techno = Map[newcell].Cell_Techno();
   2082 					if (!techno || (techno->What_Am_I()==RTTI_INFANTRY &&
   2083 						obj->What_Am_I()==RTTI_INFANTRY)) {
   2084 						if (obj->Unlimbo(Cell_Coord(newcell),DIR_N)) {
   2085 							return(true);
   2086 						}
   2087 					}
   2088 				}
   2089 
   2090 				rot++;
   2091 				if (rot > FACING_NW)
   2092 					rot = FACING_N;
   2093 			}
   2094 		}
   2095 	}
   2096 
   2097 	return(false);
   2098 }
   2099 
   2100 
   2101 /***********************************************************************************************
   2102  * Sort_Cells -- sorts an array of cells by distance                                           *
   2103  *                                                                                             *
   2104  * INPUT:                                                                                      *
   2105  *      cells         array to sort                                                            *
   2106  *      numcells      # entries in 'cells'                                                     *
   2107  *      outcells      array to store sorted values in                                          *
   2108  *                                                                                             *
   2109  * OUTPUT:                                                                                     *
   2110  *      none.                                                                                  *
   2111  *                                                                                             *
   2112  * WARNINGS:                                                                                   *
   2113  *      none.                                                                                  *
   2114  *                                                                                             *
   2115  * HISTORY:                                                                                    *
   2116  *   07/19/1995 BRR : Created.                                                                 *
   2117  *=============================================================================================*/
   2118 static void Sort_Cells(CELL *cells, int numcells, CELL *outcells)
   2119 {
   2120 	int i,j,k;
   2121 	int num_sorted = 0;
   2122 	int num_unsorted = numcells;
   2123 
   2124 	/*------------------------------------------------------------------------
   2125 	Pick the first cell at random
   2126 	------------------------------------------------------------------------*/
   2127 	j = Random_Pick(0,numcells - 1);
   2128 	outcells[0] = cells[j];
   2129 	num_sorted++;
   2130 
   2131 	for (k = j; k < num_unsorted - 1; k++) {
   2132 		cells[k] = cells[k + 1];
   2133 	}
   2134 	num_unsorted--;
   2135 
   2136 	/*------------------------------------------------------------------------
   2137 	After the first cell, assign the other cells based on who's furthest away
   2138 	from the chosen ones.
   2139 	------------------------------------------------------------------------*/
   2140 	for (i = 0; i < numcells; i++) {
   2141 		j = Furthest_Cell (outcells, num_sorted, cells, num_unsorted);
   2142 		outcells[num_sorted] = cells[j];
   2143 		num_sorted++;
   2144 
   2145 		for (k = j; k < num_unsorted - 1; k++) {
   2146 			cells[k] = cells[k + 1];
   2147 		}
   2148 		num_unsorted--;
   2149 	}
   2150 }
   2151 
   2152 
   2153 /***********************************************************************************************
   2154  * Furthest_Cell -- Finds cell furthest from a group of cells                                  *
   2155  *                                                                                             *
   2156  * INPUT:                                                                                      *
   2157  *      cells            array of cells to find furthest-cell-away-from                        *
   2158  *      numcells         # entries in 'cells'                                                  *
   2159  *      tcells         array of cells to test; one of these will be selected as being          *
   2160  *                     "furthest" from all the cells in 'cells'                                *
   2161  *      numtcells      # entries in 'tcells'                                                   *
   2162  *                                                                                             *
   2163  * OUTPUT:                                                                                     *
   2164  *      index of 'tcell' that's furthest away from 'cells'                                     *
   2165  *                                                                                             *
   2166  * WARNINGS:                                                                                   *
   2167  *      none.                                                                                  *
   2168  *                                                                                             *
   2169  * HISTORY:                                                                                    *
   2170  *   07/19/1995 BRR : Created.                                                                 *
   2171  *=============================================================================================*/
   2172 static int Furthest_Cell(CELL *cells, int numcells, CELL *tcells, int numtcells)
   2173 {
   2174 	int i;
   2175 	int j;
   2176 	int mindist;			// minimum distance a 'tcell' is from a 'cell'
   2177 	int maxmindist;		// the highest mindist value of all tcells
   2178 	int maxmin_idx;		// index of the tcell with largest mindist
   2179 	int dist;				// working distance measure
   2180 
   2181 	/*------------------------------------------------------------------------
   2182 	Initialize
   2183 	------------------------------------------------------------------------*/
   2184 	maxmindist = 0;
   2185 	maxmin_idx = 0;
   2186 
   2187 	/*------------------------------------------------------------------------
   2188 	Loop through all test cells, finding the furthest one from all entries in
   2189 	the 'cells' array
   2190 	------------------------------------------------------------------------*/
   2191 	for (i = 0; i < numtcells; i++) {
   2192 
   2193 		/*.....................................................................
   2194 		Find the 'cell' closest to this 'tcell'
   2195 		.....................................................................*/
   2196 		mindist = 0xffff;
   2197 		for (j = 0; j < numcells; j++) {
   2198 			dist = Distance (tcells[i],cells[j]);
   2199 			if (dist <= mindist) {
   2200 				mindist = dist;
   2201 			}
   2202 		}
   2203 
   2204 		/*.....................................................................
   2205 		If this tcell is further away than the others, save its distance &
   2206 		index value
   2207 		.....................................................................*/
   2208 		if (mindist >= maxmindist) {
   2209 			maxmindist = mindist;
   2210 			maxmin_idx = i;
   2211 		}
   2212 	}
   2213 
   2214 	return (maxmin_idx);
   2215 }
   2216 
   2217 
   2218 /***********************************************************************************************
   2219  * Clip_Scatter -- randomly scatters from given cell; won't fall off map                       *
   2220  *                                                                                             *
   2221  * INPUT:                                                                                      *
   2222  *      cell      cell to scatter from                                                         *
   2223  *      maxdist   max distance to scatter                                                      *
   2224  *                                                                                             *
   2225  * OUTPUT:                                                                                     *
   2226  *      new cell number                                                                        *
   2227  *                                                                                             *
   2228  * WARNINGS:                                                                                   *
   2229  *      none.                                                                                  *
   2230  *                                                                                             *
   2231  * HISTORY:                                                                                    *
   2232  *   07/30/1995 BRR : Created.                                                                 *
   2233  *=============================================================================================*/
   2234 static CELL Clip_Scatter(CELL cell, int maxdist)
   2235 {
   2236 	int x,y;
   2237 	int xdist;
   2238 	int ydist;
   2239 	int xmin,xmax;
   2240 	int ymin,ymax;
   2241 
   2242 	/*------------------------------------------------------------------------
   2243 	Get X & Y coords of given starting cell
   2244 	------------------------------------------------------------------------*/
   2245 	x = Cell_X(cell);
   2246 	y = Cell_Y(cell);
   2247 
   2248 	/*------------------------------------------------------------------------
   2249 	Compute our x & y limits
   2250 	------------------------------------------------------------------------*/
   2251 	xmin = Map.MapCellX;
   2252 	xmax = xmin + Map.MapCellWidth - 1;
   2253 	ymin = Map.MapCellY;
   2254 	ymax = ymin + Map.MapCellHeight - 1;
   2255 
   2256 	/*------------------------------------------------------------------------
   2257 	Adjust the x-coordinate
   2258 	------------------------------------------------------------------------*/
   2259 	xdist = IRandom(0,maxdist);
   2260 	if (IRandom(0,1)==0) {
   2261 		x += xdist;
   2262 		if (x > xmax) {
   2263 			x = xmax;
   2264 		}
   2265 	} else {
   2266 		x -= xdist;
   2267 		if (x < xmin) {
   2268 			x = xmin;
   2269 		}
   2270 	}
   2271 
   2272 	/*------------------------------------------------------------------------
   2273 	Adjust the y-coordinate
   2274 	------------------------------------------------------------------------*/
   2275 	ydist = IRandom(0,maxdist);
   2276 	if (IRandom(0,1)==0) {
   2277 		y += ydist;
   2278 		if (y > ymax) {
   2279 			y = ymax;
   2280 		}
   2281 	} else {
   2282 		y -= ydist;
   2283 		if (y < ymin) {
   2284 			y = ymin;
   2285 		}
   2286 	}
   2287 
   2288 	return (XY_Cell(x,y));
   2289 }
   2290 
   2291 
   2292 /***********************************************************************************************
   2293  * Clip_Move -- moves in given direction from given cell; clips to map                         *
   2294  *                                                                                             *
   2295  * INPUT:                                                                                      *
   2296  *      cell      cell to start from                                                           *
   2297  *      facing   direction to move                                                             *
   2298  *      dist      distance to move                                                             *
   2299  *                                                                                             *
   2300  * OUTPUT:                                                                                     *
   2301  *      new cell number                                                                        *
   2302  *                                                                                             *
   2303  * WARNINGS:                                                                                   *
   2304  *      none.                                                                                  *
   2305  *                                                                                             *
   2306  * HISTORY:                                                                                    *
   2307  *   07/30/1995 BRR : Created.                                                                 *
   2308  *=============================================================================================*/
   2309 static CELL Clip_Move(CELL cell, FacingType facing, int dist)
   2310 {
   2311 	int x,y;
   2312 	int xmin,xmax;
   2313 	int ymin,ymax;
   2314 
   2315 	/*------------------------------------------------------------------------
   2316 	Get X & Y coords of given starting cell
   2317 	------------------------------------------------------------------------*/
   2318 	x = Cell_X(cell);
   2319 	y = Cell_Y(cell);
   2320 
   2321 	/*------------------------------------------------------------------------
   2322 	Compute our x & y limits
   2323 	------------------------------------------------------------------------*/
   2324 	xmin = Map.MapCellX;
   2325 	xmax = xmin + Map.MapCellWidth - 1;
   2326 	ymin = Map.MapCellY;
   2327 	ymax = ymin + Map.MapCellHeight - 1;
   2328 
   2329 	/*------------------------------------------------------------------------
   2330 	Adjust the x-coordinate
   2331 	------------------------------------------------------------------------*/
   2332 	switch (facing) {
   2333 		case FACING_N:
   2334 			y -= dist;
   2335 			break;
   2336 
   2337 		case FACING_NE:
   2338 			x += dist;
   2339 			y -= dist;
   2340 			break;
   2341 
   2342 		case FACING_E:
   2343 			x += dist;
   2344 			break;
   2345 
   2346 		case FACING_SE:
   2347 			x += dist;
   2348 			y += dist;
   2349 			break;
   2350 
   2351 		case FACING_S:
   2352 			y += dist;
   2353 			break;
   2354 
   2355 		case FACING_SW:
   2356 			x -= dist;
   2357 			y += dist;
   2358 			break;
   2359 
   2360 		case FACING_W:
   2361 			x -= dist;
   2362 			break;
   2363 
   2364 		case FACING_NW:
   2365 			x -= dist;
   2366 			y -= dist;
   2367 			break;
   2368 	}
   2369 
   2370 	/*------------------------------------------------------------------------
   2371 	Clip to the map
   2372 	------------------------------------------------------------------------*/
   2373 	if (x > xmax)
   2374 		x = xmax;
   2375 	if (x < xmin)
   2376 		x = xmin;
   2377 
   2378 	if (y > ymax)
   2379 		y = ymax;
   2380 	if (y < ymin)
   2381 		y = ymin;
   2382 
   2383 	return (XY_Cell(x,y));
   2384 }