CnC_Remastered_Collection/TIBERIANDAWN/SCENARIO.CPP

820 lines
31 KiB
C++
Raw Normal View History

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: F:\projects\c&c\vcs\code\scenario.cpv 2.17 16 Oct 1995 16:52:08 JOE_BOSTIC $ */
/***********************************************************************************************
*** 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 ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : SCENARIO.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : September 10, 1993 *
* *
* Last Update : August 24, 1995 [JLB] *
* *
* This module handles the scenario reading and writing. Scenario related *
* code that is executed between scenario play can also be here. *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Clear_Scenario -- Clears all data in preparation for scenario load. *
* Do_Lose -- Display losing comments. *
* Do_Restart -- Handle the restart mission process. *
* Do_Win -- Display winning congratulations. *
* Fill_In_Data -- Recreate all data that is not loaded with scenario. *
* Read_Scenario -- Reads a scenario from disk. *
* Restate_Mission -- Handles restating the mission objective. *
* Start_Scenario -- Starts the scenario. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
extern int PreserveVQAScreen;
/***********************************************************************************************
* Start_Scenario -- Starts the scenario. *
* *
* This routine will start the scenario. In addition to loading the scenario data, it will *
* play the briefing and action movies. *
* *
* INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). *
* *
* briefing -- Should the briefing be played? Normally this is true except when the *
* scenario is restarting. *
* *
* OUTPUT: Was the scenario started without error? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/04/1995 JLB : Created. *
*=============================================================================================*/
bool Start_Scenario(char *root, bool briefing)
{
if (!Read_Scenario(root)) {
CCDebugString ("C&C95 - Failed to read scenario.\n");
return(false);
}
CCDebugString ("C&C95 - Scenario read OK.\n");
#ifdef DEMO
if (briefing) {
Play_Movie(BriefMovie);
Play_Movie(ActionMovie, TransitTheme);
}
Theme.Queue_Song(THEME_AOI);
#else
/*
** Install some hacks around the movie playing to account for the choose-
** sides introduction. We don't want an intro movie on scenario 1, and
** we don't want a briefing movie on GDI scenario 1.
*/
if (Scenario < 20 && (!Special.IsJurassic || !AreThingiesEnabled)) {
if (Scenario != 1 || Whom == HOUSE_GOOD) {
Play_Movie(IntroMovie);
}
if (briefing) {
PreserveVQAScreen = (Scenario == 1);
Play_Movie(BriefMovie);
}
Play_Movie(ActionMovie, TransitTheme);
if (TransitTheme == THEME_NONE) {
Theme.Queue_Song(THEME_AOI);
}
} else {
Play_Movie(BriefMovie);
Play_Movie(ActionMovie, TransitTheme);
#ifdef NEWMENU
char buffer[25];
sprintf(buffer, "%s.VQA", BriefMovie);
CCFileClass file(buffer);
if (GameToPlay == GAME_NORMAL && !file.Is_Available()) {
VisiblePage.Clear();
Set_Palette(GamePalette);
// Show_Mouse();
/*
** Show the mission briefing. Pretend we are inside the main loop so the palette
** will be correct on the textured buttons.
*/
bool oldinmain = InMainLoop;
InMainLoop = true;
// TO_FIX - Covert ops missions want to pop up a dialog box. ST - 9/6/2019 1:48PM
//Restate_Mission(ScenarioName, TXT_OK, TXT_NONE);
InMainLoop = oldinmain;
// Hide_Mouse();
if (TransitTheme == THEME_NONE) {
Theme.Queue_Song(THEME_AOI);
}
}
#endif
}
#endif
/*
** Set the options values, since the palette has been initialized by Read_Scenario
*/
CCDebugString ("C&C95 - About to call Options.Set.\n");
Options.Set();
CCDebugString ("C&C95 - About to return from Start_Scenario.\n");
return(true);
}
/***********************************************************************************************
* Set_Scenario_Difficulty -- Sets the difficulty of the scenario. *
* *
* Updates the player's difficulty in single-player mode. *
* *
* INPUT: difficulty -- Scenario difficulty *
* *
* OUTPUT: none *
* *
* WARNINGS: Only works in single-player. *
* Must call Start_Scenario first to initialize the player. *
* *
* HISTORY: *
* 10/02/2019 SKY : Created. *
*=============================================================================================*/
void Set_Scenario_Difficulty(int difficulty)
{
if (GameToPlay == GAME_NORMAL) {
switch (difficulty) {
case 0:
PlayerPtr->Assign_Handicap(DIFF_EASY);
break;
case 1:
PlayerPtr->Assign_Handicap(DIFF_NORMAL);
break;
case 2:
PlayerPtr->Assign_Handicap(DIFF_HARD);
break;
default:
break;
}
}
}
/***********************************************************************************************
* Read_Scenario -- Reads a scenario from disk. *
* *
* This will read a scenario from disk. Use this to begin a scenario. *
* It doesn't perform any rendering, it merely sets up the system *
* with the proper data. Setting of the right game state will start *
* the scenario running. *
* *
* INPUT: root -- Scenario root filename *
* *
* OUTPUT: none *
* *
* WARNINGS: You must clear out the system variables before calling *
* this function. Use the Clear_Scenario() function. *
* It is assumed that Scenario is set to the current scenario number. *
* *
* HISTORY: *
* 07/22/1991 : Created. *
* 02/03/1992 JLB : Uses house identification. *
*=============================================================================================*/
bool Read_Scenario(char *root)
{
CCDebugString ("C&C95 - In Read_Scenario.\n");
Clear_Scenario();
ScenarioInit++;
if (Read_Scenario_Ini(root)) {
Fill_In_Data();
Map.Set_View_Dimensions(0, Map.Get_Tab_Height(), Map.MapCellWidth, Map.MapCellHeight);
/*
** SPECIAL CASE:
** Clear out the tutor flags for scenarios one and two. This is designed
** so that tutorial message will reappear in scenario two.
*/
if (Scenario < 5) {
TutorFlags[0] = 0L;
TutorFlags[1] = 0L;
}
} else {
#if (1)
char message[200];
if (root) {
sprintf(message, "Failed to load scenario %s", root);
GlyphX_Debug_Print(message);
} else {
GlyphX_Debug_Print("Failed to load scenario");
}
#else
Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back);
Show_Mouse();
CCMessageBox().Process(TXT_UNABLE_READ_SCENARIO);
Hide_Mouse();
#endif
return(false);
}
ScenarioInit--;
CCDebugString ("C&C95 - Leaving Read_Scenario.\n");
return(true);
}
/***********************************************************************************************
* Fill_In_Data -- Recreate all data that is not loaded with scenario. *
* *
* This routine is called after the INI file for the scenario has been processed. It will *
* infer the game state from the scenario INI data. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 10/07/1992 JLB : Created. *
*=============================================================================================*/
void Fill_In_Data(void)
{
/*
** The basic scenario data load does not contain the full set of
** game data. We now must fill in the missing pieces.
*/
ScenarioInit++;
for (int index = 0; index < Buildings.Count(); index++) {
Buildings.Ptr(index)->Update_Buildables();
}
Map.Flag_To_Redraw(true);
/*
** Bring up the score display on the radar map when starting a multiplayer
** game.
*/
if (GameToPlay != GAME_NORMAL) {
Map.Player_Names(1);
}
ScenarioInit--;
}
/***********************************************************************************************
* Clear_Scenario -- Clears all data in preparation for scenario load. *
* *
* This routine will clear out all data specific to a scenario in *
* preparation for a subsequent scenario data load. This will free *
* all units, animations, and icon maps. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/22/1991 : Created. *
* 03/21/1992 JLB : Changed buffer allocations, so changes memset code. *
* 07/13/1995 JLB : End count down moved here. *
*=============================================================================================*/
void Clear_Scenario(void)
{
EndCountDown = TICKS_PER_SECOND * 30;
CrateCount = 0;
CrateTimer = 0;
CrateMaker = false;
/*
** Call everyone's Init routine, except the Map's; for the Map, only call
** MapClass::Init, which clears the Cell array. The Display::Init requires
** a Theater argument, and the theater is not known at this point; also, it
** would reload MixFiles, which isn't desired. Display::Read_INI calls its
** own Init, which will Init the entire Map hierarchy.
*/
Map.Init_Clear();
Score.Init();
Logic.Init();
HouseClass::Init();
ObjectClass::Init();
TeamTypeClass::Init();
TeamClass::Init();
TriggerClass::Init();
AircraftClass::Init();
AnimClass::Init();
BuildingClass::Init();
BulletClass::Init();
InfantryClass::Init();
OverlayClass::Init();
SmudgeClass::Init();
TemplateClass::Init();
TerrainClass::Init();
UnitClass::Init();
FactoryClass::Init();
Base.Init();
CurrentObject.Clear_All();
Invalidate_Cached_Icons();
}
/***********************************************************************************************
* Do_Win -- Display winning congratulations. *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/05/1992 JLB : Created. *
* 01/01/1995 JLB : Carries money forward into next scenario. *
*=============================================================================================*/
void Do_Win(void)
{
Map.Set_Default_Mouse(MOUSE_NORMAL);
Hide_Mouse();
/*
** If this is a multiplayer game, clear the game's name so we won't respond
** to game queries any more (in Call_Back)
*/
if (GameToPlay != GAME_NORMAL) {
MPlayerGameName[0] = 0;
}
/*
** Determine a cosmetic center point for the text.
*/
int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2);
int y = Map.TacPixelY + (Lepton_To_Pixel(Map.TacLeptonHeight)/2) -32;
/*
** Announce win to player.
*/
Set_Logic_Page(SeenBuff);
#if !(GERMAN | FRENCH)
Fancy_Text_Print(TXT_MISSION, x, y, WHITE, TBLACK, TPF_CENTER|TPF_VCR);
#endif
Fancy_Text_Print(TXT_SCENARIO_WON, x, y+30, WHITE, TBLACK, TPF_CENTER|TPF_VCR);
CountDownTimer.Set(TIMER_SECOND * 3);
Stop_Speaking();
Speak(VOX_ACCOMPLISHED);
while (CountDownTimer.Time() || Is_Speaking()) {
Call_Back();
}
/*
** Stop here if this is a multiplayer game.
*/
if (GameToPlay != GAME_NORMAL) {
if (!PlaybackGame) {
MPlayerGamesPlayed++;
Multi_Score_Presentation();
MPlayerCurGame++;
if (MPlayerCurGame >= MAX_MULTI_GAMES) {
MPlayerCurGame = MAX_MULTI_GAMES - 1;
}
}
GameActive = 0;
Show_Mouse();
return;
}
/*
** Play the winning movie and then start the next scenario.
*/
if (RequiredCD != -2) {
if (Scenario >= 20 && Scenario <60 && GameToPlay == GAME_NORMAL) {
RequiredCD = 2;
} else {
if (Scenario >=60){
RequiredCD = -1;
}else{
if (PlayerPtr->Class->House == HOUSE_GOOD) {
RequiredCD = 0;
} else {
RequiredCD = 1;
}
}
}
}
#ifndef DEMO
Play_Movie(WinMovie);
#endif
Keyboard::Clear();
/*
** Do the ending screens only if not playing back a recorded game.
*/
if (!PlaybackGame) {
#ifdef DEMO
switch (Scenario) {
case 1:
Score.Presentation();
Scenario = 10;
break;
case 10:
Score.Presentation();
Scenario = 6;
break;
default:
Score.Presentation();
GDI_Ending();
GameActive = false;
Show_Mouse();
return;
// Prog_End();
// exit(0);
// break;
}
#else
#ifdef NEWMENU
if (Scenario >= 20) {
Keyboard::Clear();
Score.Presentation();
GameActive = false;
Show_Mouse();
return;
}
#endif
if (PlayerPtr->Class->House == HOUSE_BAD && Scenario == 13) {
Nod_Ending();
//Prog_End();
//exit(0);
SeenBuff.Clear();
Show_Mouse();
GameActive = false;
return;
}
if (PlayerPtr->Class->House == HOUSE_GOOD && Scenario == 15) {
GDI_Ending();
//Prog_End();
//exit(0);
SeenBuff.Clear();
Show_Mouse();
GameActive = false;
return;
}
if ( (Special.IsJurassic && AreThingiesEnabled) && Scenario == 5) {
Prog_End("Do_Win - Last Jurassic mission complete");
if (!RunningAsDLL) {
exit(0);
}
return;
}
if (!Special.IsJurassic || !AreThingiesEnabled) {
Keyboard::Clear();
InterpolationPaletteChanged = TRUE;
InterpolationPalette = Palette;
Score.Presentation();
/*
** Skip scenario #7 if the airfield was blown up.
*/
if (Scenario == 6 && PlayerPtr->Class->House == HOUSE_GOOD && SabotagedType == STRUCT_AIRSTRIP) {
Scenario++;
}
Map_Selection();
}
Scenario++;
#endif
Keyboard::Clear();
}
CarryOverMoney = PlayerPtr->Credits;
int pieces = PlayerPtr->NukePieces;
/*
** Generate a new scenario filename
*/
Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, ScenVar);
Start_Scenario(ScenarioName);
PlayerPtr->NukePieces = pieces;
/*
** Destroy the building that was sabotaged in the previous scenario. This only
** applies to GDI mission #7.
*/
int index;
if (SabotagedType != STRUCT_NONE && Scenario == 7 && PlayerPtr->Class->House == HOUSE_GOOD) {
for (index = 0; index < Buildings.Count(); index++) {
BuildingClass * building = Buildings.Ptr(index);
if (building && !building->IsInLimbo && building->House != PlayerPtr && building->Class->Type == SabotagedType) {
building->Limbo();
delete building;
break;
}
}
/*
** Remove the building from the prebuild list.
*/
for (index = 0; index < Base.Nodes.Count(); index++) {
BaseNodeClass * node = Base.Get_Node(index);
if (node && node->Type == SabotagedType) {
Base.Nodes.Delete(index);
break;
}
}
}
SabotagedType = STRUCT_NONE;
Map.Render();
Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back);
Show_Mouse();
}
/***********************************************************************************************
* Do_Lose -- Display losing comments. *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/05/1992 JLB : Created. *
*=============================================================================================*/
void Do_Lose(void)
{
Map.Set_Default_Mouse(MOUSE_NORMAL);
Hide_Mouse();
/*
** If this is a multiplayer game, clear the game's name so we won't respond
** to game queries any more (in Call_Back)
*/
if (GameToPlay != GAME_NORMAL) {
MPlayerGameName[0] = 0;
}
/*
** Determine a cosmetic center point for the text.
*/
int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2);
int y = Map.TacPixelY + (Lepton_To_Pixel(Map.TacLeptonHeight)/2) -32;
/*
** Announce win to player.
*/
Set_Logic_Page(SeenBuff);
Fancy_Text_Print(TXT_MISSION, x, y, WHITE, TBLACK, TPF_CENTER|TPF_VCR);
Fancy_Text_Print(TXT_SCENARIO_LOST, x, y+30, WHITE, TBLACK, TPF_CENTER|TPF_VCR);
CountDownTimer.Set(TIMER_SECOND * 3);
Stop_Speaking();
Speak(VOX_FAIL);
while (CountDownTimer.Time() || Is_Speaking()) {
Call_Back();
}
#ifdef OBSOLETE
if (Debug_Play_Map) {
Go_Editor(true);
Show_Mouse();
return;
}
#endif
/*
** Stop here if this is a multiplayer game.
*/
if (GameToPlay != GAME_NORMAL) {
if (!PlaybackGame) {
MPlayerGamesPlayed++;
Multi_Score_Presentation();
MPlayerCurGame++;
if (MPlayerCurGame >= MAX_MULTI_GAMES) {
MPlayerCurGame = MAX_MULTI_GAMES - 1;
}
}
GameActive = 0;
Show_Mouse();
return;
}
Play_Movie(LoseMovie);
/*
** Start same scenario again
*/
Set_Palette(GamePalette);
Show_Mouse();
if (!PlaybackGame && !CCMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) {
Hide_Mouse();
Keyboard::Clear();
Start_Scenario(ScenarioName, false);
Map.Render();
} else {
Hide_Mouse();
GameActive = 0;
}
Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back);
Show_Mouse();
}
/***********************************************************************************************
* Do_Restart -- Handle the restart mission process. *
* *
* This routine is called in the main game loop when the mission must be restarted. This *
* routine will throw away the current game and reload the appropriate mission. The *
* game will "resume" at the start of the mission. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/24/1995 JLB : Created. *
*=============================================================================================*/
void Do_Restart(void)
{
bool hidden = Get_Mouse_State();
if (hidden) Show_Mouse();
CCMessageBox().Process(TXT_RESTARTING, TXT_NONE);
Map.Set_Default_Mouse(MOUSE_NORMAL);
Keyboard::Clear();
Start_Scenario(ScenarioName, false);
if (hidden) Hide_Mouse();
Keyboard::Clear();
Map.Render();
}
/***********************************************************************************************
* Restate_Mission -- Handles restating the mission objective. *
* *
* This routine will display the mission objective (as text). It will also give the *
* option to redisplay the mission briefing video. *
* *
* INPUT: name -- The scenario name. This is the unique identifier for the scenario *
* briefing text as it appears in the "MISSION.INI" file. *
* *
* OUTPUT: Returns the response from the dialog. This will either be 1 if the video was *
* requested, or 0 if the return to game options button was selected. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 06/23/1995 JLB : Created. *
* 08/06/1995 JLB : Uses preloaded briefing text. *
*=============================================================================================*/
bool Restate_Mission(char const * name, int button1, int button2)
{
if (name) {
#ifdef JAPANESE
char fname[14];
strcpy(fname, name);
strcat(fname,".CPS");
if(CCFileClass(fname).Is_Available()) {
CCMessageBox box(TXT_NONE, true);
return(box.Process(fname, button1, button2));
}
#else
/*
** Make sure that if there is no briefing movie, that the briefing text is
** the only option available.
*/
bool brief = true;
#ifdef NEWMENU
char buffer[25];
char buffer1[25];
sprintf(buffer, "%s.VQA", BriefMovie);
sprintf(buffer1, "%s.VQA", ActionMovie);
CCFileClass file1(buffer);
CCFileClass file2(buffer1);
if (!file1.Is_Available() && !file2.Is_Available()) {
button1 = TXT_OK;
button2 = TXT_NONE;
brief = false;
}
#endif
/*
** If mission object text was found, then display it.
*/
if (strlen(BriefingText)) {
static char _buff[512];
strcpy(_buff, BriefingText);
// strcpy(_ShapeBuffer, BriefingText);
bool hidden = Get_Mouse_State();
if (hidden) Show_Mouse();
if (CCMessageBox(TXT_OBJECTIVE).Process(_buff, button1, button2)) {
if (hidden) Hide_Mouse();
return(true);
}
if (hidden) Hide_Mouse();
if (!brief) return(true);
return(false);
}
#endif
}
return(false);
Command & Conquer Remastered post-launch patch Improvements to harvester resource finding logic. Don't allow the Advanced Comm Center to be capturable in skirmish or multiplayer. Increased failed pathfinding fudge factor. Buildings accept the Guard command if they can attack. Don't allow force capturing of ally structures. Fixes for laser Orcas in S3cr3t M1ss10n. Properly restore them after save. Reset Orcas after loads. Fixed flag animation rendering in CTF. Potentially fix a crash if aircraft are destroyed outside the map bounds. Fixed legacy Obelisk line rendering. Fix out-of-bounds crash in TD; issue was already fixed in RA. Disable capture flag on Commandos. Drop the flag when entering the limbo state. Fixed end game race condition, winning team ID is always sent before individual player win/lose messages. Fixed Chan spawn on SCB10EA. Don't show enter cursor for enemy units on refineries and repair pads. Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold. Don't debug reveal legacy rendering when a player is defeated. Fixed crash when loading saves of custom campaign maps. Reset harvester archived target when given a direct harvest order. Prevent NOD cargo planes from being force attacked. Fixed unit selection on load. Migrated queued repair pad functionality from RA to TD. Randomly animate infantry in area guard mode. Fixed crash accessing inactive objects. Added some walls in SCG08EB to prevent civilians from killing themselves. TD + RA: Audio: Overiding "Our base is under attack" cooldown timing from legacy from 2 minutes to 30 seconds, so it will be heard 4x as often. Fixed adjacent cell out-of-bounds crash issues. Kill player on disconnect Fixed and improved build time calculations to be consistent between TD and RA. Don't show health bars for cloaked Stealth Tanks in the legacy renderer. Fix selection of individual control groups after mixed selection. More adjustments to SCG08EB; switch C7 to C5, and add civilian AI to avoid Tiberium. Extra safety checks for units that have no weapons and aircraft that can't hunt. Fix loading of multiple infantry onto an APC. Additional safety checks for invalid coordinates. Prevent units from being instantly repaired. Fix map passability. Fail Allied mission 5B if the spy re-boards the starting transport (matches 5A and 5C behavior). Fixed multiplayer formation move causing units to move at light speed. Ignore movement destination checks if a unit is part of a mission-driven team. Fix buffer overrun crash. Ignore mines when determining win conditions. Fixed river passability in Blue Lakes.
2020-06-23 04:43:21 +12:00
}
void Fixup_Scenario(void)
{
/*
** "Fraidycat" civilians avoid wandering into Tiberium for SCG08EB, since they're mission-critical.
*/
bool is_scg08ea = GameToPlay == GAME_NORMAL && PlayerPtr->ActLike == HOUSE_GOOD && Scenario == 8 && ScenVar == SCEN_VAR_B;
for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) {
InfantryTypeClass& infantry_type = (InfantryTypeClass&)InfantryTypeClass::As_Reference(index);
if (infantry_type.IsFraidyCat) {
infantry_type.IsAvoidingTiberium = is_scg08ea;
}
}
/*
** Laser-firing Orcas in the PATSUX secret mission
*/
if (GameToPlay == GAME_NORMAL && Scenario == 72) {
((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_OBELISK_LASER;
} else {
((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_DRAGON;
}
/*
** Modern Balance
*/
if (Special.ModernBalance) {
/*
** GDI Weapons Factory has 30% more health.
*/
((BuildingTypeClass&)BuildingTypeClass::As_Reference(STRUCT_WEAP)).MaxStrength = 520;
/*
** Repair Pad is a pre-requisite for the APC.
*/
((UnitTypeClass&)UnitTypeClass::As_Reference(UNIT_APC)).Pre |= STRUCTF_REPAIR;
} else {
((BuildingTypeClass&)BuildingTypeClass::As_Reference(STRUCT_WEAP)).MaxStrength = 400;
((UnitTypeClass&)UnitTypeClass::As_Reference(UNIT_APC)).Pre &= ~STRUCTF_REPAIR;
}
}