HOUSE.CPP (344526B)
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: /counterstrike/HOUSE.CPP 4 3/13/97 7:11p Steve_tall $ */ 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 : HOUSE.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : May 21, 1994 * 28 * * 29 * Last Update : November 4, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * HouseClass::AI -- Process house logic. * 34 * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * 35 * HouseClass::AI_Attack -- Handles offensive attack logic. * 36 * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * 37 * HouseClass::AI_Building -- Determines what building to build. * 38 * HouseClass::AI_Fire_Sale -- Check for and perform a fire sale. * 39 * HouseClass::AI_Infantry -- Determines the infantry unit to build. * 40 * HouseClass::AI_Money_Check -- Handles money production logic. * 41 * HouseClass::AI_Power_Check -- Handle the power situation. * 42 * HouseClass::AI_Unit -- Determines what unit to build next. * 43 * HouseClass::Abandon_Production -- Abandons production of item type specified. * 44 * HouseClass::Active_Add -- Add an object to active duty for this house. * 45 * HouseClass::Active_Remove -- Remove this object from active duty for this house. * 46 * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * 47 * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * 48 * HouseClass::Adjust_Power -- Adjust the power value of the house. * 49 * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * 50 * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * 51 * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * 52 * HouseClass::Attacked -- Lets player know if base is under attack. * 53 * HouseClass::Available_Money -- Fetches the total credit worth of the house. * 54 * HouseClass::Begin_Production -- Starts production of the specified object type. * 55 * HouseClass::Blowup_All -- blows up everything * 56 * HouseClass::Can_Build -- General purpose build legality checker. * 57 * HouseClass::Clobber_All -- removes all objects for this house * 58 * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * 59 * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * 60 * HouseClass::Detach -- Removes specified object from house tracking systems. * 61 * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * 62 * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * 63 * HouseClass::Expert_AI -- Handles expert AI processing. * 64 * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * 65 * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * 66 * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * 67 * HouseClass::Find_Build_Location -- Finds a suitable building location. * 68 * HouseClass::Find_Building -- Finds a building of specified type. * 69 * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * 70 * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * 71 * HouseClass::Fire_Sale -- Cause all buildings to be sold. * 72 * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * 73 * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * 74 * HouseClass::Flag_Remove -- Removes the flag from the specified target. * 75 * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * 76 * HouseClass::Flag_To_Lose -- Flags the house to die soon. * 77 * HouseClass::Flag_To_Win -- Flags the house to win soon. * 78 * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * 79 * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * 80 * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * 81 * HouseClass::HouseClass -- Constructor for a house object. * 82 * HouseClass::Init -- init's in preparation for new scenario * 83 * HouseClass::Init_Data -- Initializes the multiplayer color data. * 84 * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * 85 * HouseClass::Is_Ally -- Checks to see if the object is an ally. * 86 * HouseClass::Is_Ally -- Determines if the specified house is an ally. * 87 * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * 88 * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * 89 * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * 90 * HouseClass::Make_Ally -- Make the specified house an ally. * 91 * HouseClass::Make_Enemy -- Make an enemy of the house specified. * 92 * HouseClass::Manual_Place -- Inform display system of building placement mode. * 93 * HouseClass::One_Time -- Handles one time initialization of the house array. * 94 * HouseClass::Place_Object -- Places the object (building) at location specified. * 95 * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * 96 * HouseClass::Power_Fraction -- Fetches the current power output rating. * 97 * HouseClass::Production_Begun -- Records that production has begun. * 98 * HouseClass::Read_INI -- Reads house specific data from INI. * 99 * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * 100 * HouseClass::Recalc_Center -- Recalculates the center point of the base. * 101 * HouseClass::Refund_Money -- Refunds money to back to the house. * 102 * HouseClass::Remap_Table -- Fetches the remap table for this house object. * 103 * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * 104 * HouseClass::Set_Factory -- Assign specified factory to house tracking. * 105 * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * 106 * HouseClass::Special_Weapon_AI -- Fires special weapon. * 107 * HouseClass::Spend_Money -- Removes money from the house. * 108 * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * 109 * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * 110 * HouseClass::Suggested_New_Team -- Determine what team should be created. * 111 * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * 112 * HouseClass::Suspend_Production -- Temporarily puts production on hold. * 113 * HouseClass::Tally_Score -- Fills in the score system for this round * 114 * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * 115 * HouseClass::Tracking_Add -- Informs house of new inventory item. * 116 * HouseClass::Tracking_Remove -- Remove object from house tracking system. * 117 * HouseClass::Where_To_Go -- Determines where the object should go and wait. * 118 * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * 119 * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * 120 * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * 121 * HouseClass::Write_INI -- Writes the house data to the INI database. * 122 * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * 123 * HouseClass::delete -- Deallocator function for a house object. * 124 * HouseClass::new -- Allocator for a house class. * 125 * HouseClass::operator HousesType -- Conversion to HousesType operator. * 126 * HouseClass::~HouseClass -- Default destructor for a house object. * 127 * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * 128 * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * 129 * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * 130 * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * 131 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 132 133 #include "function.h" 134 #include "vortex.h" 135 136 //#include "WolDebug.h" 137 138 /* 139 ** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM 140 */ 141 #include "SidebarGlyphx.h" 142 143 TFixedIHeapClass<HouseClass::BuildChoiceClass> HouseClass::BuildChoice; 144 145 int TFixedIHeapClass<HouseClass::BuildChoiceClass>::Save(Pipe &) const 146 { 147 return(true); 148 } 149 150 int TFixedIHeapClass<HouseClass::BuildChoiceClass>::Load(Straw &) 151 { 152 return(0); 153 } 154 155 void TFixedIHeapClass<HouseClass::BuildChoiceClass>::Code_Pointers(void) 156 { 157 } 158 159 void TFixedIHeapClass<HouseClass::BuildChoiceClass>::Decode_Pointers(void) 160 { 161 } 162 163 extern bool RedrawOptionsMenu; 164 165 /*********************************************************************************************** 166 * HouseClass::operator HousesType -- Conversion to HousesType operator. * 167 * * 168 * This operator will automatically convert from a houses class object into the HousesType * 169 * enumerated value. * 170 * * 171 * INPUT: none * 172 * * 173 * OUTPUT: Returns with the object's HousesType value. * 174 * * 175 * WARNINGS: none * 176 * * 177 * HISTORY: * 178 * 01/23/1995 JLB : Created. * 179 *=============================================================================================*/ 180 HouseClass::operator HousesType(void) const 181 { 182 assert(Houses.ID(this) == ID); 183 184 return(Class->House); 185 } 186 187 188 /*********************************************************************************************** 189 * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * 190 * * 191 * This will calculate the current tiberium (gold) load as a ratio of the maximum storage * 192 * capacity. * 193 * * 194 * INPUT: none * 195 * * 196 * OUTPUT: Returns the current tiberium storage situation as a ratio of load over capacity. * 197 * * 198 * WARNINGS: none * 199 * * 200 * HISTORY: * 201 * 07/31/1996 JLB : Created. * 202 *=============================================================================================*/ 203 fixed HouseClass::Tiberium_Fraction(void) const 204 { 205 if (Tiberium == 0) { 206 return(0); 207 } 208 return(fixed(Tiberium, Capacity)); 209 } 210 211 212 /*********************************************************************************************** 213 * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * 214 * * 215 * Use this routine to convert a house number into the house pointer that it represents. * 216 * A simple index into the Houses template array is not sufficient, since the array order * 217 * is arbitrary. An actual scan through the house object is required in order to find the * 218 * house object desired. * 219 * * 220 * INPUT: house -- The house type number to look up. * 221 * * 222 * OUTPUT: Returns with a pointer to the house object that the house number represents. * 223 * * 224 * WARNINGS: none * 225 * * 226 * HISTORY: * 227 * 01/23/1995 JLB : Created. * 228 *=============================================================================================*/ 229 HouseClass * HouseClass::As_Pointer(HousesType house) 230 { 231 if (house != HOUSE_NONE) { 232 for (int index = 0; index < Houses.Count(); index++) { 233 if (Houses.Ptr(index)->Class->House == house) { 234 return(Houses.Ptr(index)); 235 } 236 } 237 } 238 return(0); 239 } 240 241 242 /*********************************************************************************************** 243 * HouseClass::One_Time -- Handles one time initialization of the house array. * 244 * * 245 * This basically calls the constructor for each of the houses in the game. All other * 246 * data specific to the house is initialized when the scenario is loaded. * 247 * * 248 * INPUT: none * 249 * * 250 * OUTPUT: none * 251 * * 252 * WARNINGS: Only call this ONCE at the beginning of the game. * 253 * * 254 * HISTORY: * 255 * 12/09/1994 JLB : Created. * 256 *=============================================================================================*/ 257 void HouseClass::One_Time(void) 258 { 259 BuildChoice.Set_Heap(STRUCT_COUNT); 260 } 261 262 263 /*********************************************************************************************** 264 * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * 265 * * 266 * The handicap rating will affect combat, movement, and production for the house. It can * 267 * either make it more or less difficult for the house (controlled by the handicap value). * 268 * * 269 * INPUT: handicap -- The handicap value to assign to this house. The default value for * 270 * a house is DIFF_NORMAL. * 271 * * 272 * OUTPUT: Returns with the old handicap value. * 273 * * 274 * WARNINGS: none * 275 * * 276 * HISTORY: * 277 * 07/09/1996 JLB : Created. * 278 * 10/22/1996 JLB : Uses act like value for multiplay only. * 279 *=============================================================================================*/ 280 DiffType HouseClass::Assign_Handicap(DiffType handicap) 281 { 282 DiffType old = Difficulty; 283 Difficulty = handicap; 284 285 if (Session.Type != GAME_NORMAL) { 286 HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike); 287 FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias; 288 GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; 289 AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; 290 ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias; 291 ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias; 292 CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias; 293 RepairDelay = Rule.Diff[handicap].RepairDelay; 294 BuildDelay = Rule.Diff[handicap].BuildDelay; 295 BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; 296 } else { 297 FirepowerBias = Rule.Diff[handicap].FirepowerBias; 298 GroundspeedBias = Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; 299 AirspeedBias = Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; 300 ArmorBias = Rule.Diff[handicap].ArmorBias; 301 ROFBias = Rule.Diff[handicap].ROFBias; 302 CostBias = Rule.Diff[handicap].CostBias; 303 RepairDelay = Rule.Diff[handicap].RepairDelay; 304 BuildDelay = Rule.Diff[handicap].BuildDelay; 305 BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; 306 } 307 308 return(old); 309 } 310 311 312 313 #ifdef CHEAT_KEYS 314 315 void HouseClass::Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const 316 { 317 mono->Set_Cursor(x, y); 318 mono->Printf("A:%-5d I:%-5d V:%-5d", ZoneInfo[zone].AirDefense, ZoneInfo[zone].InfantryDefense, ZoneInfo[zone].ArmorDefense); 319 } 320 321 322 /*********************************************************************************************** 323 * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * 324 * * 325 * This utility function will output the current status of the house class to the mono * 326 * screen. Through this information bugs may be fixed or detected. * 327 * * 328 * INPUT: none * 329 * * 330 * OUTPUT: none * 331 * * 332 * WARNINGS: none * 333 * * 334 * HISTORY: * 335 * 05/31/1994 JLB : Created. * 336 *=============================================================================================*/ 337 void HouseClass::Debug_Dump(MonoClass * mono) const 338 { 339 mono->Set_Cursor(0, 0); 340 mono->Print(Text_String(TXT_DEBUG_HOUSE)); 341 342 mono->Set_Cursor(1, 1);mono->Printf("[%d]%14.14s", Class->House, Name()); 343 mono->Set_Cursor(20, 1);mono->Printf("[%d]%13.13s", ActLike, HouseTypeClass::As_Reference(ActLike).Name()); 344 mono->Set_Cursor(39, 1);mono->Printf("%2d", Control.TechLevel); 345 mono->Set_Cursor(45, 1);mono->Printf("%2d", Difficulty); 346 mono->Set_Cursor(52, 1);mono->Printf("%2d", State); 347 mono->Set_Cursor(58, 1);mono->Printf("%2d", Blockage); 348 mono->Set_Cursor(65, 1);mono->Printf("%2d", IQ); 349 mono->Set_Cursor(72, 1);mono->Printf("%5d", (long)RepairTimer); 350 351 mono->Set_Cursor(1, 3);mono->Printf("%08X", AScan); 352 mono->Set_Cursor(10, 3);mono->Printf("%8.8s", (BuildAircraft == AIRCRAFT_NONE) ? " " : AircraftTypeClass::As_Reference(BuildAircraft).Graphic_Name()); 353 mono->Set_Cursor(21, 3);mono->Printf("%3d", CurAircraft); 354 mono->Set_Cursor(27, 3);mono->Printf("%8d", Credits); 355 mono->Set_Cursor(37, 3);mono->Printf("%5d", Power); 356 mono->Set_Cursor(45, 3);mono->Printf("%04X", RadarSpied); 357 mono->Set_Cursor(52, 3);mono->Printf("%5d", PointTotal); 358 mono->Set_Cursor(62, 3);mono->Printf("%5d", (long)TeamTime); 359 mono->Set_Cursor(71, 3);mono->Printf("%5d", (long)AlertTime); 360 361 mono->Set_Cursor(1, 5);mono->Printf("%08X", BScan); 362 mono->Set_Cursor(10, 5);mono->Printf("%8.8s", (BuildStructure == STRUCT_NONE) ? " " : BuildingTypeClass::As_Reference(BuildStructure).Graphic_Name()); 363 mono->Set_Cursor(21, 5);mono->Printf("%3d", CurBuildings); 364 mono->Set_Cursor(27, 5);mono->Printf("%8d", Tiberium); 365 mono->Set_Cursor(37, 5);mono->Printf("%5d", Drain); 366 mono->Set_Cursor(44, 5);mono->Printf("%16.16s", QuarryName[PreferredTarget]); 367 mono->Set_Cursor(62, 5);mono->Printf("%5d", (long)TriggerTime); 368 mono->Set_Cursor(71, 5);mono->Printf("%5d", (long)BorrowedTime); 369 370 mono->Set_Cursor(1, 7);mono->Printf("%08X", UScan); 371 mono->Set_Cursor(10, 7);mono->Printf("%8.8s", (BuildUnit == UNIT_NONE) ? " " : UnitTypeClass::As_Reference(BuildUnit).Graphic_Name()); 372 mono->Set_Cursor(21, 7);mono->Printf("%3d", CurUnits); 373 mono->Set_Cursor(27, 7);mono->Printf("%8d", Control.InitialCredits); 374 mono->Set_Cursor(38, 7);mono->Printf("%5d", UnitsLost); 375 mono->Set_Cursor(44, 7);mono->Printf("%08X", Allies); 376 mono->Set_Cursor(71, 7);mono->Printf("%5d", (long)Attack); 377 378 mono->Set_Cursor(1, 9);mono->Printf("%08X", IScan); 379 mono->Set_Cursor(10, 9);mono->Printf("%8.8s", (BuildInfantry == INFANTRY_NONE) ? " " : InfantryTypeClass::As_Reference(BuildInfantry).Graphic_Name()); 380 mono->Set_Cursor(21, 9);mono->Printf("%3d", CurInfantry); 381 mono->Set_Cursor(27, 9);mono->Printf("%8d", Capacity); 382 mono->Set_Cursor(38, 9);mono->Printf("%5d", BuildingsLost); 383 mono->Set_Cursor(45, 9);mono->Printf("%4d", Radius / CELL_LEPTON_W); 384 mono->Set_Cursor(71, 9);mono->Printf("%5d", (long)AITimer); 385 386 mono->Set_Cursor(1, 11);mono->Printf("%08X", VScan); 387 mono->Set_Cursor(10, 11);mono->Printf("%8.8s", (BuildVessel == VESSEL_NONE) ? " " : VesselTypeClass::As_Reference(BuildVessel).Graphic_Name()); 388 mono->Set_Cursor(21, 11);mono->Printf("%3d", CurVessels); 389 mono->Set_Cursor(54, 11);mono->Printf("%04X", Coord_Cell(Center)); 390 mono->Set_Cursor(71, 11);mono->Printf("%5d", (long)DamageTime); 391 392 393 for (int index = 0; index < ARRAY_SIZE(Scen.GlobalFlags); index++) { 394 mono->Set_Cursor(1+index, 15); 395 if (Scen.GlobalFlags[index] != 0) { 396 mono->Print("1"); 397 } else { 398 mono->Print("0"); 399 } 400 if (index >= 24) break; 401 } 402 if (Enemy != HOUSE_NONE) { 403 char const * name = ""; 404 name = HouseClass::As_Pointer(Enemy)->Name(); 405 mono->Set_Cursor(53, 15);mono->Printf("[%d]%21.21s", Enemy, HouseTypeClass::As_Reference(Enemy).Name()); 406 } 407 408 Print_Zone_Stats(27, 11, ZONE_NORTH, mono); 409 Print_Zone_Stats(27, 13, ZONE_CORE, mono); 410 Print_Zone_Stats(27, 15, ZONE_SOUTH, mono); 411 Print_Zone_Stats(1, 13, ZONE_WEST, mono); 412 Print_Zone_Stats(53, 13, ZONE_EAST, mono); 413 414 mono->Fill_Attrib(1, 17, 12, 1, IsActive ? MonoClass::INVERSE : MonoClass::NORMAL); 415 mono->Fill_Attrib(1, 18, 12, 1, IsHuman ? MonoClass::INVERSE : MonoClass::NORMAL); 416 mono->Fill_Attrib(1, 19, 12, 1, IsPlayerControl ? MonoClass::INVERSE : MonoClass::NORMAL); 417 mono->Fill_Attrib(1, 20, 12, 1, IsAlerted ? MonoClass::INVERSE : MonoClass::NORMAL); 418 mono->Fill_Attrib(1, 21, 12, 1, IsDiscovered ? MonoClass::INVERSE : MonoClass::NORMAL); 419 mono->Fill_Attrib(1, 22, 12, 1, IsMaxedOut ? MonoClass::INVERSE : MonoClass::NORMAL); 420 421 mono->Fill_Attrib(14, 17, 12, 1, IsDefeated ? MonoClass::INVERSE : MonoClass::NORMAL); 422 mono->Fill_Attrib(14, 18, 12, 1, IsToDie ? MonoClass::INVERSE : MonoClass::NORMAL); 423 mono->Fill_Attrib(14, 19, 12, 1, IsToWin ? MonoClass::INVERSE : MonoClass::NORMAL); 424 mono->Fill_Attrib(14, 20, 12, 1, IsToLose ? MonoClass::INVERSE : MonoClass::NORMAL); 425 mono->Fill_Attrib(14, 21, 12, 1, IsCivEvacuated ? MonoClass::INVERSE : MonoClass::NORMAL); 426 mono->Fill_Attrib(14, 22, 12, 1, IsRecalcNeeded ? MonoClass::INVERSE : MonoClass::NORMAL); 427 428 mono->Fill_Attrib(27, 17, 12, 1, IsVisionary ? MonoClass::INVERSE : MonoClass::NORMAL); 429 mono->Fill_Attrib(27, 18, 12, 1, IsTiberiumShort ? MonoClass::INVERSE : MonoClass::NORMAL); 430 mono->Fill_Attrib(27, 19, 12, 1, IsSpied ? MonoClass::INVERSE : MonoClass::NORMAL); 431 mono->Fill_Attrib(27, 20, 12, 1, IsThieved ? MonoClass::INVERSE : MonoClass::NORMAL); 432 mono->Fill_Attrib(27, 21, 12, 1, IsGPSActive ? MonoClass::INVERSE : MonoClass::NORMAL); 433 mono->Fill_Attrib(27, 22, 12, 1, IsStarted ? MonoClass::INVERSE : MonoClass::NORMAL); 434 435 mono->Fill_Attrib(40, 17, 12, 1, IsResigner ? MonoClass::INVERSE : MonoClass::NORMAL); 436 mono->Fill_Attrib(40, 18, 12, 1, IsGiverUpper ? MonoClass::INVERSE : MonoClass::NORMAL); 437 mono->Fill_Attrib(40, 19, 12, 1, IsBuiltSomething ? MonoClass::INVERSE : MonoClass::NORMAL); 438 mono->Fill_Attrib(40, 20, 12, 1, IsBaseBuilding ? MonoClass::INVERSE : MonoClass::NORMAL); 439 } 440 #endif 441 442 443 /*********************************************************************************************** 444 * HouseClass::new -- Allocator for a house class. * 445 * * 446 * This is the allocator for a house class. Since there can be only * 447 * one of each type of house, this is allocator has restricted * 448 * functionality. Any attempt to allocate a house structure for a * 449 * house that already exists, just returns a pointer to the previously * 450 * allocated house. * 451 * * 452 * INPUT: house -- The house to allocate a class object for. * 453 * * 454 * OUTPUT: Returns with a pointer to the allocated class object. * 455 * * 456 * WARNINGS: none * 457 * * 458 * HISTORY: * 459 * 05/22/1994 JLB : Created. * 460 *=============================================================================================*/ 461 void * HouseClass::operator new(size_t) 462 { 463 void * ptr = Houses.Allocate(); 464 if (ptr) { 465 ((HouseClass *)ptr)->IsActive = true; 466 } 467 return(ptr); 468 } 469 470 471 /*********************************************************************************************** 472 * HouseClass::delete -- Deallocator function for a house object. * 473 * * 474 * This function marks the house object as "deallocated". Such a * 475 * house object is available for reallocation later. * 476 * * 477 * INPUT: ptr -- Pointer to the house object to deallocate. * 478 * * 479 * OUTPUT: none * 480 * * 481 * WARNINGS: none * 482 * * 483 * HISTORY: * 484 * 05/22/1994 JLB : Created. * 485 *=============================================================================================*/ 486 void HouseClass::operator delete(void * ptr) 487 { 488 if (ptr) { 489 ((HouseClass *)ptr)->IsActive = false; 490 } 491 Houses.Free((HouseClass *)ptr); 492 } 493 494 495 /*********************************************************************************************** 496 * HouseClass::HouseClass -- Constructor for a house object. * 497 * * 498 * This function is the constructor and it marks the house object * 499 * as being allocated. * 500 * * 501 * INPUT: none * 502 * * 503 * OUTPUT: none * 504 * * 505 * WARNINGS: none * 506 * * 507 * HISTORY: * 508 * 05/22/1994 JLB : Created. * 509 *=============================================================================================*/ 510 #define VOX_NOT_READY VOX_NONE 511 HouseClass::HouseClass(HousesType house) : 512 RTTI(RTTI_HOUSE), 513 ID(Houses.ID(this)), 514 Class(HouseTypes.Ptr(house)), 515 Difficulty(Scen.CDifficulty), 516 FirepowerBias(1), 517 GroundspeedBias(1), 518 AirspeedBias(1), 519 ArmorBias(1), 520 ROFBias(1), 521 CostBias(1), 522 BuildSpeedBias(1), 523 RepairDelay(0), 524 BuildDelay(0), 525 ActLike(Class->House), 526 IsHuman(false), 527 WasHuman(false), 528 IsPlayerControl(false), 529 IsStarted(false), 530 IsAlerted(false), 531 IsBaseBuilding(false), 532 IsDiscovered(false), 533 IsMaxedOut(false), 534 IsDefeated(false), 535 IsToDie(false), 536 IsToLose(false), 537 IsToWin(false), 538 IsCivEvacuated(false), 539 IsRecalcNeeded(true), 540 IsVisionary(false), 541 IsTiberiumShort(false), 542 IsSpied(false), 543 IsThieved(false), 544 IsGPSActive(false), 545 IsBuiltSomething(false), 546 IsResigner(false), 547 IsGiverUpper(false), 548 IsParanoid(false), 549 IsToLook(true), 550 IsQueuedMovementToggle(false), 551 DidRepair(false), 552 IQ(Control.IQ), 553 State(STATE_BUILDUP), 554 JustBuiltStructure(STRUCT_NONE), 555 JustBuiltInfantry(INFANTRY_NONE), 556 JustBuiltUnit(UNIT_NONE), 557 JustBuiltAircraft(AIRCRAFT_NONE), 558 JustBuiltVessel(VESSEL_NONE), 559 Blockage(0), 560 RepairTimer(0), 561 AlertTime(0), 562 BorrowedTime(0), 563 BScan(0), 564 ActiveBScan(0), 565 OldBScan(0), 566 UScan(0), 567 ActiveUScan(0), 568 OldUScan(0), 569 IScan(0), 570 ActiveIScan(0), 571 OldIScan(0), 572 AScan(0), 573 ActiveAScan(0), 574 OldAScan(0), 575 VScan(0), 576 ActiveVScan(0), 577 OldVScan(0), 578 CreditsSpent(0), 579 HarvestedCredits(0), 580 StolenBuildingsCredits(0), 581 CurUnits(0), 582 CurBuildings(0), 583 CurInfantry(0), 584 CurVessels(0), 585 CurAircraft(0), 586 Tiberium(0), 587 Credits(0), 588 Capacity(0), 589 AircraftTotals(NULL), 590 InfantryTotals(NULL), 591 UnitTotals(NULL), 592 BuildingTotals(NULL), 593 VesselTotals(NULL), 594 DestroyedAircraft(NULL), 595 DestroyedInfantry(NULL), 596 DestroyedUnits(NULL), 597 DestroyedBuildings(NULL), 598 DestroyedVessels(NULL), 599 CapturedBuildings(NULL), 600 TotalCrates(NULL), 601 AircraftFactories(0), 602 InfantryFactories(0), 603 UnitFactories(0), 604 BuildingFactories(0), 605 VesselFactories(0), 606 Power(0), 607 Drain(0), 608 AircraftFactory(-1), 609 InfantryFactory(-1), 610 UnitFactory(-1), 611 BuildingFactory(-1), 612 VesselFactory(-1), 613 Radar(RADAR_NONE), 614 FlagLocation(TARGET_NONE), 615 FlagHome(0), 616 UnitsLost(0), 617 BuildingsLost(0), 618 WhoLastHurtMe(house), 619 StartLocationOverride(-1), 620 Center(0), 621 Radius(0), 622 LATime(0), 623 LAType(RTTI_NONE), 624 LAZone(ZONE_NONE), 625 LAEnemy(HOUSE_NONE), 626 ToCapture(TARGET_NONE), 627 RadarSpied(0), 628 PointTotal(0), 629 PreferredTarget(QUARRY_ANYTHING), 630 ScreenShakeTime(0), 631 Attack(0), 632 Enemy(HOUSE_NONE), 633 AITimer(0), 634 UnitToTeleport(0), 635 BuildStructure(STRUCT_NONE), 636 BuildUnit(UNIT_NONE), 637 BuildInfantry(INFANTRY_NONE), 638 BuildAircraft(AIRCRAFT_NONE), 639 BuildVessel(VESSEL_NONE), 640 NukeDest(0), 641 Allies(0), 642 DamageTime(TICKS_PER_MINUTE * Rule.DamageDelay), 643 TeamTime(TICKS_PER_MINUTE * Rule.TeamDelay), 644 TriggerTime(0), 645 SpeakAttackDelay(1), 646 SpeakPowerDelay(1), 647 SpeakMoneyDelay(1), 648 SpeakMaxedDelay(1), 649 RemapColor(Class->RemapColor), 650 DebugUnlockBuildables(false) 651 { 652 /* 653 ** Explicit in-place construction of the super weapons is 654 ** required here because the default constructor for super 655 ** weapons must serve as a no-initialization constructor (save/load reasons). 656 */ 657 new (&SuperWeapon[SPC_NUCLEAR_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.NukeTime, true, VOX_ABOMB_PREPPING, VOX_ABOMB_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); 658 new (&SuperWeapon[SPC_SONAR_PULSE]) SuperClass(TICKS_PER_MINUTE * Rule.SonarTime, false, VOX_NONE, VOX_SONAR_AVAILABLE, VOX_NOT_READY, VOX_NOT_READY); 659 new (&SuperWeapon[SPC_CHRONOSPHERE]) SuperClass(TICKS_PER_MINUTE * Rule.ChronoTime, true, VOX_CHRONO_CHARGING, VOX_CHRONO_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); 660 new (&SuperWeapon[SPC_PARA_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.ParaBombTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); 661 new (&SuperWeapon[SPC_PARA_INFANTRY]) SuperClass(TICKS_PER_MINUTE * Rule.ParaInfantryTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); 662 new (&SuperWeapon[SPC_SPY_MISSION]) SuperClass(TICKS_PER_MINUTE * Rule.SpyTime, false, VOX_NONE, VOX_SPY_PLANE, VOX_NOT_READY, VOX_NOT_READY); 663 new (&SuperWeapon[SPC_IRON_CURTAIN]) SuperClass(TICKS_PER_MINUTE * Rule.IronCurtainTime, true, VOX_IRON_CHARGING, VOX_IRON_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); 664 new (&SuperWeapon[SPC_GPS]) SuperClass(TICKS_PER_MINUTE * Rule.GPSTime, true, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); 665 666 memset(UnitsKilled, '\0', sizeof(UnitsKilled)); 667 memset(BuildingsKilled, '\0', sizeof(BuildingsKilled)); 668 memset(BQuantity, '\0', sizeof(BQuantity)); 669 memset(UQuantity, '\0', sizeof(UQuantity)); 670 memset(IQuantity, '\0', sizeof(IQuantity)); 671 memset(AQuantity, '\0', sizeof(AQuantity)); 672 memset(VQuantity, '\0', sizeof(VQuantity)); 673 strcpy(IniName, Text_String(TXT_COMPUTER)); // Default computer name. 674 HouseTriggers[house].Clear(); 675 memset((void *)&Regions[0], 0x00, sizeof(Regions)); 676 Make_Ally(house); 677 Assign_Handicap(Scen.CDifficulty); 678 679 /* 680 ** Set the time of the first AI attack. 681 */ 682 Attack = Rule.AttackDelay * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); 683 684 Init_Unit_Trackers(); 685 } 686 687 688 /*********************************************************************************************** 689 * HouseClass::~HouseClass -- House class destructor * 690 * * 691 * * 692 * * 693 * INPUT: Nothing * 694 * * 695 * OUTPUT: Nothing * 696 * * 697 * WARNINGS: None * 698 * * 699 * HISTORY: * 700 * 8/6/96 4:48PM ST : Created * 701 *=============================================================================================*/ 702 HouseClass::~HouseClass (void) 703 { 704 Class = 0; 705 706 Free_Unit_Trackers(); 707 } 708 709 710 /*********************************************************************************************** 711 * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * 712 * * 713 * This is the default constructor that initializes all the values to their default * 714 * settings. * 715 * * 716 * INPUT: none * 717 * * 718 * OUTPUT: none * 719 * * 720 * WARNINGS: none * 721 * * 722 * HISTORY: * 723 * 07/31/1996 JLB : Created. * 724 *=============================================================================================*/ 725 HouseStaticClass::HouseStaticClass(void) : 726 IQ(0), 727 TechLevel(1), 728 Allies(0), 729 MaxUnit(Rule.UnitMax/6), 730 MaxBuilding(Rule.BuildingMax/6), 731 MaxInfantry(Rule.InfantryMax/6), 732 MaxVessel(Rule.VesselMax/6), 733 MaxAircraft(Rule.UnitMax/6), 734 InitialCredits(0), 735 Edge(SOURCE_NORTH) 736 { 737 } 738 739 740 /*********************************************************************************************** 741 * HouseClass::Can_Build -- General purpose build legality checker. * 742 * * 743 * This routine is called when it needs to be determined if the specified object type can * 744 * be built by this house. Production and sidebar maintenance use this routine heavily. * 745 * * 746 * INPUT: type -- Pointer to the type of object that legality is to be checked for. * 747 * * 748 * house -- This is the house to check for legality against. Note that this might * 749 * not be 'this' house since the check could be from a captured factory. * 750 * Captured factories build what the original owner of them could build. * 751 * * 752 * OUTPUT: Can the specified object be built? * 753 * * 754 * WARNINGS: none * 755 * * 756 * HISTORY: * 757 * 07/04/1995 JLB : Created. * 758 * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. * 759 * 10/23/1996 JLB : Hack to allow Tanya to both sides in multiplay. * 760 * 11/04/1996 JLB : Computer uses prerequisite record. * 761 *=============================================================================================*/ 762 bool HouseClass::Can_Build(ObjectTypeClass const * type, HousesType house) const 763 { 764 assert(Houses.ID(this) == ID); 765 assert(type != NULL); 766 767 /* 768 ** An object with a prohibited tech level availability will never be allowed, regardless 769 ** of who requests it. 770 */ 771 if (((TechnoTypeClass const *)type)->Level == -1) return(false); 772 773 #ifdef FIXIT_CSII // checked - ajw 9/28/98 774 /* 775 ** If this is a CounterStrike II-only unit, and we're playing a multiplayer 776 ** game in 'downshifted' mode against CounterStrike or Red Alert, then 777 ** don't allow building this unit. 778 */ 779 if (!NewUnitsEnabled) { 780 switch(type->What_Am_I()) { 781 case RTTI_INFANTRYTYPE: 782 if ( ((InfantryTypeClass *)type)->ID >= INFANTRY_RA_COUNT) 783 return(false); 784 break; 785 case RTTI_UNITTYPE: 786 if ( ((UnitTypeClass *)type)->ID >= UNIT_RA_COUNT) 787 return(false); 788 break; 789 case RTTI_VESSELTYPE: 790 if ( ((VesselTypeClass *)type)->ID >= VESSEL_RA_COUNT) 791 return(false); 792 break; 793 default: 794 break; 795 } 796 } 797 #endif 798 799 /* 800 ** The computer can always build everything. 801 */ 802 if (!IsHuman && Session.Type == GAME_NORMAL) return(true); 803 804 /* 805 ** Special hack to get certain objects to exist for both sides in the game. 806 */ 807 int own = type->Get_Ownable(); 808 809 /* 810 ** Check to see if this owner can build the object type specified. 811 */ 812 if (((1L << house) & own) == 0) { 813 return(false); 814 } 815 816 /* 817 ** Perform some equivalency fixups for the building existence flags. 818 */ 819 long flags = ActiveBScan; 820 821 /* 822 ** The computer records prerequisite buildings because it can't relay on the 823 ** sidebar to keep track of this information. 824 */ 825 if (!IsHuman) { 826 flags = OldBScan; 827 } 828 829 int pre = ((TechnoTypeClass const *)type)->Prerequisite; 830 831 /* 832 ** Advanced power also serves as a prerequisite for normal power. 833 */ 834 if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER; 835 836 /* 837 ** Either tech center counts as a prerequisite. 838 */ 839 if (Session.Type != GAME_NORMAL) { 840 if ((flags & (STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH)) != 0) flags |= STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH; 841 } 842 843 int level = Control.TechLevel; 844 #ifdef CHEAT_KEYS 845 if (Debug_Cheat) { 846 level = 98; 847 pre = 0; 848 } 849 #endif 850 851 // ST - 8/23/2019 4:53PM 852 if (DebugUnlockBuildables) { 853 level = 98; 854 pre = 0; 855 } 856 857 /* 858 ** See if the prerequisite requirements have been met. 859 */ 860 return((pre & flags) == pre && ((TechnoTypeClass const *)type)->Level <= (unsigned)level); 861 } 862 863 864 /*************************************************************************** 865 * HouseClass::Init -- init's in preparation for new scenario * 866 * * 867 * INPUT: * 868 * none. * 869 * * 870 * OUTPUT: * 871 * none. * 872 * * 873 * WARNINGS: * 874 * none. * 875 * * 876 * HISTORY: * 877 * 12/07/1994 BR : Created. * 878 * 12/17/1994 JLB : Resets tracker bits. * 879 *=========================================================================*/ 880 void HouseClass::Init(void) 881 { 882 Houses.Free_All(); 883 884 for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { 885 HouseTriggers[index].Clear(); 886 } 887 } 888 889 // Object selection list is switched with player context for GlyphX. ST - 8/7/2019 10:11AM 890 extern void Logic_Switch_Player_Context(HouseClass *house); 891 extern bool MPSuperWeaponDisable; 892 893 /*********************************************************************************************** 894 * HouseClass::AI -- Process house logic. * 895 * * 896 * This handles the AI for the house object. It should be called once per house per game * 897 * tick. It processes all house global tasks such as low power damage accumulation and * 898 * house specific trigger events. * 899 * * 900 * INPUT: none * 901 * * 902 * OUTPUT: none * 903 * * 904 * WARNINGS: none * 905 * * 906 * HISTORY: * 907 * 12/27/1994 JLB : Created. * 908 * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. * 909 *=============================================================================================*/ 910 extern void Recalculate_Placement_Distances(); 911 912 void HouseClass::AI(void) 913 { 914 assert(Houses.ID(this) == ID); 915 916 // Set PlayerPtr to be this house. ST - 8/7/2019 10:12AM 917 Logic_Switch_Player_Context(this); 918 919 /* 920 ** If base building has been turned on by a trigger, then force the house to begin 921 ** production and team creation as well. This is also true if the IQ is high enough to 922 ** being base building. 923 */ 924 if (!IsHuman && (IsBaseBuilding || IQ >= Rule.IQProduction)) { 925 IsBaseBuilding = true; 926 IsStarted = true; 927 IsAlerted = true; 928 } 929 930 /* 931 ** Check to see if the house wins. 932 */ 933 if (Session.Type == GAME_NORMAL && IsToWin && BorrowedTime == 0 && Blockage <= 0) { 934 IsToWin = false; 935 if (this == PlayerPtr) { 936 PlayerWins = true; 937 } else { 938 PlayerLoses = true; 939 } 940 } 941 942 /* 943 ** Check to see if the house loses. 944 */ 945 if (Session.Type == GAME_NORMAL && IsToLose && BorrowedTime == 0) { 946 IsToLose = false; 947 if (this == PlayerPtr) { 948 PlayerLoses = true; 949 } else { 950 PlayerWins = true; 951 } 952 } 953 954 /* 955 ** Check to see if all objects of this house should be blown up. 956 */ 957 if (IsToDie && BorrowedTime == 0) { 958 IsToDie = false; 959 Blowup_All(); 960 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 961 MPlayer_Defeated(); 962 } 963 } 964 965 /* 966 ** Double check power values to correct illegal conditions. It is possible to 967 ** get a power output of negative (one usually) as a result of damage sustained 968 ** and the fixed point fractional math involved with power adjustments. If the 969 ** power rating drops below zero, then make it zero. 970 */ 971 Power = max(Power, 0); 972 Drain = max(Drain, 0); 973 974 /* 975 ** If the base has been alerted to the enemy and should be attacking, then 976 ** see if the attack timer has expired. If it has, then create the attack 977 ** teams. 978 */ 979 if (IsAlerted && AlertTime == 0) { 980 981 /* 982 ** Adjusted to reduce maximum number of teams created. 983 */ 984 int maxteams = Random_Pick(2, (int)(((Control.TechLevel-1)/3)+1)); 985 for (int index = 0; index < maxteams; index++) { 986 TeamTypeClass const * ttype = Suggested_New_Team(true); 987 if (ttype != NULL) { 988 ScenarioInit++; 989 ttype->Create_One_Of(); 990 ScenarioInit--; 991 } 992 } 993 AlertTime = Rule.AutocreateTime * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); 994 // int mintime = Rule.AutocreateTime * (TICKS_PER_MINUTE/2); 995 // int maxtime = Rule.AutocreateTime * (TICKS_PER_MINUTE*2); 996 // AlertTime = Random_Pick(mintime, maxtime); 997 } 998 999 /* 1000 ** If this house's flag waypoint is a valid cell, see if there's 1001 ** someone sitting on it. If so, make the scatter. 1002 */ 1003 if (FlagHome != 0 && (Frame % TICKS_PER_SECOND) == 0) { 1004 1005 TechnoClass * techno = Map[FlagHome].Cell_Techno(); 1006 if (techno != NULL) { 1007 bool moving = false; 1008 if (techno->Is_Foot()) { 1009 if (Target_Legal(((FootClass *)techno)->NavCom)) { 1010 moving = true; 1011 } 1012 } 1013 1014 if (!moving) { 1015 techno->Scatter(0, true, true); 1016 } 1017 } 1018 } 1019 1020 /* 1021 ** Create teams for this house if necessary. 1022 ** (Use the same timer for some extra capture-the-flag logic.) 1023 */ 1024 if (!IsAlerted && !TeamTime) { 1025 1026 TeamTypeClass const * ttype = Suggested_New_Team(false); 1027 if (ttype) { 1028 ttype->Create_One_Of(); 1029 } 1030 1031 TeamTime = Rule.TeamDelay * TICKS_PER_MINUTE; 1032 } 1033 1034 /* 1035 ** If there is insufficient power, then all buildings that are above 1036 ** half strength take a little bit of damage. 1037 */ 1038 if (DamageTime == 0) { 1039 1040 /* 1041 ** When the power is below required, then the buildings will take damage over 1042 ** time. 1043 */ 1044 if (Power_Fraction() < 1) { 1045 for (int index = 0; index < Buildings.Count(); index++) { 1046 BuildingClass & b = *Buildings.Ptr(index); 1047 1048 if (b.House == this && b.Health_Ratio() > Rule.ConditionYellow) { 1049 // BG: Only damage buildings that require power, to keep the 1050 // land mines from blowing up under low-power conditions 1051 if (b.Class->Drain) { 1052 int damage = 1; 1053 b.Take_Damage(damage, 0, WARHEAD_AP, 0); 1054 } 1055 } 1056 } 1057 } 1058 DamageTime = TICKS_PER_MINUTE * Rule.DamageDelay; 1059 } 1060 1061 /* 1062 ** If there are no more buildings to sell, then automatically cancel the 1063 ** sell mode. 1064 */ 1065 if (PlayerPtr == this && !ActiveBScan && Map.IsSellMode) { 1066 Map.Sell_Mode_Control(0); 1067 } 1068 1069 /* 1070 ** Various base conditions may be announced to the player. Typically, this would be 1071 ** low tiberium capacity or low power. 1072 */ 1073 if (PlayerPtr == this) { 1074 1075 if (SpeakMaxedDelay == 0 && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) { 1076 Speak(VOX_NEED_MO_MONEY); 1077 Map.Flash_Money(); 1078 SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); 1079 1080 int text_id = TXT_INSUFFICIENT_FUNDS; 1081 char const * text = Text_String(TXT_INSUFFICIENT_FUNDS); 1082 if (text != NULL) { 1083 Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 1084 } 1085 } 1086 1087 if (SpeakMaxedDelay == 0 && IsMaxedOut) { 1088 IsMaxedOut = false; 1089 if ((Capacity - Tiberium) < 300 && Capacity > 500 && (ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { 1090 Speak(VOX_NEED_MO_CAPACITY); 1091 SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); 1092 } 1093 } 1094 if (SpeakPowerDelay == 0 && Power_Fraction() < 1) { 1095 if (ActiveBScan & STRUCTF_CONST) { 1096 Speak(VOX_LOW_POWER); 1097 SpeakPowerDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); 1098 Map.Flash_Power(); 1099 1100 int text_id = -1; 1101 char const * text = NULL; 1102 if (BQuantity[STRUCT_AAGUN] > 0) { 1103 text = Text_String(TXT_POWER_AAGUN); 1104 text_id = TXT_POWER_AAGUN; 1105 } 1106 if (BQuantity[STRUCT_TESLA] > 0) { 1107 text = Text_String(TXT_POWER_TESLA); 1108 text_id = TXT_POWER_TESLA; 1109 } 1110 if (text == NULL) { 1111 text = Text_String(TXT_LOW_POWER); 1112 text_id = TXT_LOW_POWER; 1113 } 1114 if (text != NULL) { 1115 Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 1116 } 1117 } 1118 } 1119 } 1120 1121 /* 1122 ** If there is a flag associated with this house, then mark it to be 1123 ** redrawn. 1124 */ 1125 if (Target_Legal(FlagLocation)) { 1126 UnitClass * unit = As_Unit(FlagLocation); 1127 if (unit) { 1128 unit->Mark(MARK_CHANGE); 1129 } else { 1130 CELL cell = As_Cell(FlagLocation); 1131 Map[cell].Flag_Update(); 1132 Map[cell].Redraw_Objects(); 1133 } 1134 } 1135 1136 bool is_time = false; 1137 1138 /* 1139 ** Triggers are only checked every so often. If the trigger timer has expired, 1140 ** then set the trigger processing flag. 1141 */ 1142 if (TriggerTime == 0 || IsBuiltSomething) { 1143 is_time = true; 1144 TriggerTime = TICKS_PER_MINUTE/10; 1145 IsBuiltSomething = false; 1146 } 1147 1148 /* 1149 ** Process any super weapon logic required. 1150 */ 1151 1152 if (Session.Type != GAME_GLYPHX_MULTIPLAYER || !MPSuperWeaponDisable) { 1153 Super_Weapon_Handler(); 1154 } 1155 1156 #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. 1157 if( (Session.Type != GAME_NORMAL || !IsHuman) && Scen.AutoSonarTimer == 0 ) 1158 { 1159 // If house has nothing but subs left, do an automatic sonar pulse to reveal them. 1160 if( VQuantity[ VESSEL_SS ] > 0 ) // Includes count of VESSEL_MISSILESUBs. ajw 1161 { 1162 int iCount = 0; 1163 int i; 1164 for( i = 0; i != STRUCT_COUNT-3; ++i ) 1165 { 1166 iCount += BQuantity[ i ]; 1167 } 1168 if( !iCount ) 1169 { 1170 for( i = 0; i != UNIT_RA_COUNT-3; ++i ) 1171 { 1172 iCount += UQuantity[ i ]; 1173 } 1174 if( !iCount ) 1175 { 1176 // ajw - Found bug - house's civilians are not removed from IQuantity when they die. 1177 // Workaround... 1178 for( i = 0; i <= INFANTRY_DOG; ++i ) 1179 { 1180 iCount += IQuantity[ i ]; 1181 } 1182 if( !iCount ) 1183 { 1184 for( i = 0; i != AIRCRAFT_COUNT; ++i ) 1185 { 1186 iCount += AQuantity[ i ]; 1187 } 1188 if( !iCount ) 1189 { 1190 for( i = 0; i != VESSEL_RA_COUNT; ++i ) 1191 { 1192 if( i != VESSEL_SS ) 1193 iCount += VQuantity[ i ]; 1194 } 1195 if( !iCount ) 1196 { 1197 // Do the ping. 1198 for (int index = 0; index < Vessels.Count(); index++) { 1199 VesselClass * sub = Vessels.Ptr(index); 1200 if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { 1201 sub->PulseCountDown = 15 * TICKS_PER_SECOND; 1202 sub->Do_Uncloak(); 1203 } 1204 } 1205 bAutoSonarPulse = true; 1206 } 1207 } 1208 } 1209 } 1210 } 1211 } 1212 } 1213 #endif 1214 1215 if (Session.Type != GAME_NORMAL) { 1216 Check_Pertinent_Structures(); 1217 } 1218 1219 /* 1220 ** Special win/lose check for multiplayer games; by-passes the 1221 ** trigger system. We must wait for non-zero frame, because init 1222 ** may not properly set IScan etc for each house; you have to go 1223 ** through each object's AI before it will be properly set. 1224 */ 1225 if (Session.Type != GAME_NORMAL && !IsDefeated && 1226 !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && !ActiveVScan && Frame > 0) { 1227 MPlayer_Defeated(); 1228 } 1229 1230 /* 1231 ** Try to spring all events attached to this house. The triggers will check 1232 ** for themselves if they actually need to be sprung or not. 1233 */ 1234 for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { 1235 if (HouseTriggers[Class->House][index]->Spring() && index > 0) { 1236 index--; 1237 continue; 1238 } 1239 } 1240 1241 /* 1242 ** If a radar facility is not present, but the radar is active, then turn the radar off. 1243 ** The radar also is turned off when the power gets below 100% capacity. 1244 */ 1245 if (PlayerPtr == this) { 1246 bool jammed = true; 1247 1248 /* 1249 ** Find if there are any radar facilities, and if they're jammed or not 1250 */ 1251 1252 if (IsGPSActive) { 1253 jammed = false; 1254 } else { 1255 for (int index = 0; index < Buildings.Count(); index++) { 1256 BuildingClass * building = Buildings.Ptr(index); 1257 #ifdef FIXIT_RADAR_JAMMED 1258 if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr) { 1259 #else 1260 if (building && building->House == PlayerPtr) { 1261 #endif 1262 if (*building == STRUCT_RADAR /* || *building == STRUCT_EYE */) { 1263 if (!building->IsJammed) { 1264 jammed = false; 1265 break; 1266 } 1267 } 1268 } 1269 } 1270 } 1271 1272 Map.Set_Jammed(this, jammed); 1273 // Need to add in here where we activate it when only GPS is active. 1274 if (Map.Is_Radar_Active()) { 1275 if (ActiveBScan & STRUCTF_RADAR) { 1276 if (Power_Fraction() < 1 && !IsGPSActive) { 1277 Map.Radar_Activate(0); 1278 } 1279 } else { 1280 if (!IsGPSActive) { 1281 Map.Radar_Activate(0); 1282 } 1283 } 1284 1285 } else { 1286 if (IsGPSActive || (ActiveBScan & STRUCTF_RADAR)) { 1287 if (Power_Fraction() >= 1 || IsGPSActive) { 1288 Map.Radar_Activate(1); 1289 } 1290 } else { 1291 if (Map.Is_Radar_Existing()) { 1292 Map.Radar_Activate(4); 1293 } 1294 } 1295 } 1296 if (!IsGPSActive && !(ActiveBScan & STRUCTF_RADAR)) { 1297 Radar = RADAR_NONE; 1298 } else { 1299 Radar = (Map.Is_Radar_Active() || Map.Is_Radar_Activating()) ? RADAR_ON : RADAR_OFF; 1300 } 1301 } 1302 1303 VisibleCredits.AI(false, this, true); 1304 1305 /* 1306 ** Perform any expert system AI processing. 1307 */ 1308 if (IsBaseBuilding && AITimer == 0) { 1309 AITimer = Expert_AI(); 1310 } 1311 1312 if (!IsBaseBuilding && State == STATE_ENDGAME) { 1313 Fire_Sale(); 1314 Do_All_To_Hunt(); 1315 } 1316 1317 AI_Building(); 1318 AI_Unit(); 1319 AI_Vessel(); 1320 AI_Infantry(); 1321 AI_Aircraft(); 1322 1323 1324 /* 1325 ** If the production possibilities need to be recalculated, then do so now. This must 1326 ** occur after the scan bits have been properly updated. 1327 */ 1328 if (PlayerPtr == this && IsRecalcNeeded) { 1329 IsRecalcNeeded = false; 1330 Map.Recalc(); 1331 1332 /* 1333 ** This placement might affect any prerequisite requirements for construction 1334 ** lists. Update the buildable options accordingly. 1335 */ 1336 for (int index = 0; index < Buildings.Count(); index++) { 1337 BuildingClass * building = Buildings.Ptr(index); 1338 if (building && building->Strength > 0 && building->Owner() == Class->House && building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION) { 1339 1340 if (PlayerPtr == building->House) { 1341 building->Update_Buildables(); 1342 } 1343 } 1344 } 1345 1346 Recalculate_Placement_Distances(); 1347 Check_Pertinent_Structures(); 1348 } 1349 1350 /* 1351 ** See if it's time to re-set the can-repair flag 1352 */ 1353 if (DidRepair && RepairTimer == 0) { 1354 DidRepair = false; 1355 } 1356 1357 if (this == PlayerPtr && IsToLook) { 1358 IsToLook = false; 1359 Map.All_To_Look(PlayerPtr); 1360 } 1361 } 1362 1363 /*********************************************************************************************** 1364 * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * 1365 * * 1366 * This handles any super weapons assigned to this house. It also performs any necessary * 1367 * maintenance that the super weapons require. * 1368 * * 1369 * INPUT: none * 1370 * * 1371 * OUTPUT: none * 1372 * * 1373 * WARNINGS: none * 1374 * * 1375 * HISTORY: * 1376 * 09/17/1996 JLB : Created. * 1377 *=============================================================================================*/ 1378 void HouseClass::Super_Weapon_Handler(void) 1379 { 1380 /* 1381 ** Perform all super weapon AI processing. This just checks to see if 1382 ** the graphic needs changing for the special weapon and updates the 1383 ** sidebar as necessary. 1384 */ 1385 for (SpecialWeaponType special = SPC_FIRST; special < SPC_COUNT; special++) { 1386 SuperClass * super = &SuperWeapon[special]; 1387 1388 if (super->Is_Present()) { 1389 1390 /* 1391 ** Perform any charge-up logic for the super weapon. If the super 1392 ** weapon is owned by the player and a graphic change is detected, then 1393 ** flag the sidebar to be redrawn so the player will see the change. 1394 */ 1395 if (super->AI(this == PlayerPtr)) { 1396 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1397 } 1398 1399 /* 1400 ** Repeating super weapons that require power will be suspended if there 1401 ** is insufficient power available. 1402 */ 1403 if (!super->Is_Ready() && super->Is_Powered() && !super->Is_One_Time()) { 1404 super->Suspend(Power_Fraction() < 1); 1405 } 1406 } 1407 } 1408 1409 /* 1410 ** Check to see if they have launched the GPS, but subsequently lost their 1411 ** tech center. If so, remove the GPS, and shroud the map. 1412 */ 1413 if (IsGPSActive && !(ActiveBScan & STRUCTF_ADVANCED_TECH) ) { 1414 IsGPSActive = false; 1415 1416 /* 1417 ** Updated for client/server multiplayer. ST - 8/12/2019 11:32AM 1418 */ 1419 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 1420 if (IsPlayerControl) { 1421 Map.Shroud_The_Map(PlayerPtr); 1422 } 1423 1424 } else { 1425 1426 if (IsHuman) { 1427 Map.Shroud_The_Map(this); 1428 } 1429 } 1430 } 1431 1432 /* 1433 ** Check to see if the GPS Satellite should be removed from the sidebar 1434 ** because of outside circumstances. The advanced technology facility 1435 ** being destroyed is a good example of this. Having fired the satellite 1436 ** is another good example, because it's a one-shot item. 1437 */ 1438 if (SuperWeapon[SPC_GPS].Is_Present()) { 1439 if (!(ActiveBScan & STRUCTF_ADVANCED_TECH) || IsGPSActive || IsDefeated) { 1440 /* 1441 ** Remove the missile capability when there is no advanced tech facility. 1442 */ 1443 if (SuperWeapon[SPC_GPS].Remove()) { 1444 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1445 IsRecalcNeeded = true; 1446 } 1447 } else { 1448 /* 1449 ** Auto-fire the GPS satellite if it's charged up. 1450 */ 1451 if (SuperWeapon[SPC_GPS].Is_Ready()) { 1452 SuperWeapon[SPC_GPS].Discharged(this == PlayerPtr); 1453 if (SuperWeapon[SPC_GPS].Remove()) { 1454 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1455 } 1456 IsRecalcNeeded = true; 1457 for (int index = 0; index < Buildings.Count(); index++) { 1458 BuildingClass * bldg = Buildings.Ptr(index); 1459 if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this) { 1460 bldg->HasFired = true; 1461 bldg->Assign_Mission(MISSION_MISSILE); 1462 break; 1463 } 1464 } 1465 } 1466 } 1467 } else { 1468 /* 1469 ** If there is no GPS satellite present, but there is a GPS satellite 1470 ** facility available, then make the GPS satellite available as well. 1471 */ 1472 if ((ActiveBScan & STRUCTF_ADVANCED_TECH) != 0 && 1473 !IsGPSActive && 1474 Control.TechLevel >= Rule.GPSTechLevel && 1475 (IsHuman || IQ >= Rule.IQSuperWeapons)) { 1476 1477 bool canfire = false; 1478 for (int index = 0; index < Buildings.Count(); index++) { 1479 BuildingClass * bldg = Buildings.Ptr(index); 1480 if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this && !bldg->IsInLimbo) { 1481 if (!bldg->HasFired) { 1482 canfire = true; 1483 break; 1484 } 1485 } 1486 } 1487 1488 if (canfire) { 1489 SuperWeapon[SPC_GPS].Enable(false, this == PlayerPtr, Power_Fraction() < 1); 1490 1491 /* 1492 ** Flag the sidebar to be redrawn if necessary. 1493 */ 1494 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1495 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1496 if (IsHuman) { 1497 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_GPS, this); 1498 } 1499 } else { 1500 if (this == PlayerPtr) { 1501 Map.Add(RTTI_SPECIAL, SPC_GPS); 1502 Map.Column[1].Flag_To_Redraw(); 1503 } 1504 } 1505 } 1506 } 1507 } 1508 1509 /* 1510 ** Check to see if the chronosphere should be removed from the sidebar 1511 ** because of outside circumstances. The chronosphere facility 1512 ** being destroyed is a good example of this. 1513 */ 1514 if (SuperWeapon[SPC_CHRONOSPHERE].Is_Present()) { 1515 if ( (!(ActiveBScan & STRUCTF_CHRONOSPHERE) && !SuperWeapon[SPC_CHRONOSPHERE].Is_One_Time()) || IsDefeated) { 1516 1517 /* 1518 ** Remove the chronosphere when there is no chronosphere facility. 1519 ** Note that this will not remove the one time created chronosphere. 1520 */ 1521 if (SuperWeapon[SPC_CHRONOSPHERE].Remove()) { 1522 if (this == PlayerPtr) { 1523 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1524 if (Map.IsTargettingMode == SPC_CHRONOSPHERE || Map.IsTargettingMode == SPC_CHRONO2) { 1525 if (Map.IsTargettingMode == SPC_CHRONO2) { 1526 TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); 1527 if (tech && tech->IsActive && tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { 1528 } else { 1529 Map.IsTargettingMode = SPC_NONE; 1530 } 1531 } else { 1532 Map.IsTargettingMode = SPC_NONE; 1533 } 1534 } 1535 #else 1536 if (Map.IsTargettingMode == SPC_CHRONOSPHERE || 1537 Map.IsTargettingMode == SPC_CHRONO2) { 1538 Map.IsTargettingMode = SPC_NONE; 1539 } 1540 #endif 1541 Map.Column[1].Flag_To_Redraw(); 1542 } 1543 IsRecalcNeeded = true; 1544 } 1545 } 1546 } else { 1547 /* 1548 ** If there is no chronosphere present, but there is a chronosphere 1549 ** facility available, then make the chronosphere available as well. 1550 */ 1551 if ((ActiveBScan & STRUCTF_CHRONOSPHERE) && 1552 // (ActLike == HOUSE_GOOD || Session.Type != GAME_NORMAL) && 1553 (unsigned)Control.TechLevel >= BuildingTypeClass::As_Reference(STRUCT_CHRONOSPHERE).Level && 1554 // Control.TechLevel >= Rule.ChronoTechLevel && 1555 (IsHuman || IQ >= Rule.IQSuperWeapons)) { 1556 1557 SuperWeapon[SPC_CHRONOSPHERE].Enable(false, this == PlayerPtr, Power_Fraction() < 1); 1558 1559 /* 1560 ** Flag the sidebar to be redrawn if necessary. 1561 */ 1562 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1563 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1564 if (IsHuman) { 1565 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_CHRONOSPHERE, this); 1566 } 1567 } else { 1568 if (this == PlayerPtr) { 1569 Map.Add(RTTI_SPECIAL, SPC_CHRONOSPHERE); 1570 Map.Column[1].Flag_To_Redraw(); 1571 } 1572 } 1573 } 1574 } 1575 1576 /* 1577 ** Check to see if the iron curtain should be removed from the sidebar 1578 ** because of outside circumstances. The iron curtain facility 1579 ** being destroyed is a good example of this. 1580 */ 1581 if (SuperWeapon[SPC_IRON_CURTAIN].Is_Present()) { 1582 if ( (!(ActiveBScan & STRUCTF_IRON_CURTAIN) && !SuperWeapon[SPC_IRON_CURTAIN].Is_One_Time()) || IsDefeated) { 1583 1584 /* 1585 ** Remove the iron curtain when there is no iron curtain facility. 1586 ** Note that this will not remove the one time created iron curtain. 1587 */ 1588 if (SuperWeapon[SPC_IRON_CURTAIN].Remove()) { 1589 if (this == PlayerPtr) { 1590 if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { 1591 Map.IsTargettingMode = SPC_NONE; 1592 } 1593 Map.Column[1].Flag_To_Redraw(); 1594 } 1595 IsRecalcNeeded = true; 1596 } 1597 } 1598 } else { 1599 /* 1600 ** If there is no iron curtain present, but there is an iron curtain 1601 ** facility available, then make the iron curtain available as well. 1602 */ 1603 if ((ActiveBScan & STRUCTF_IRON_CURTAIN) && 1604 (ActLike == HOUSE_USSR || ActLike == HOUSE_UKRAINE || Session.Type != GAME_NORMAL) && 1605 (IsHuman || IQ >= Rule.IQSuperWeapons)) { 1606 1607 SuperWeapon[SPC_IRON_CURTAIN].Enable(false, this == PlayerPtr, Power_Fraction() < 1); 1608 1609 /* 1610 ** Flag the sidebar to be redrawn if necessary. 1611 */ 1612 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1613 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1614 if (IsHuman) { 1615 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_IRON_CURTAIN, this); 1616 } 1617 } else { 1618 if (this == PlayerPtr) { 1619 Map.Add(RTTI_SPECIAL, SPC_IRON_CURTAIN); 1620 Map.Column[1].Flag_To_Redraw(); 1621 } 1622 } 1623 } 1624 } 1625 1626 /* 1627 ** Check to see if the sonar pulse should be removed from the sidebar 1628 ** because of outside circumstances. The spied-upon enemy sub pen 1629 ** being destroyed is a good example of this. 1630 */ 1631 if (SuperWeapon[SPC_SONAR_PULSE].Is_Present()) { 1632 int usspy = 1 << (Class->House); 1633 bool present = false; 1634 bool powered = false; 1635 for (int q = 0; q < Buildings.Count() && !powered; q++) { 1636 BuildingClass * bldg = Buildings.Ptr(q); 1637 if ((*bldg == STRUCT_SUB_PEN) && (bldg->House->Class->House != Class->House) && (bldg->Spied_By() & usspy) ) { 1638 present = true; 1639 powered = !(bldg->House->Power_Fraction() < 1); 1640 } 1641 } 1642 if ( (!present && !SuperWeapon[SPC_SONAR_PULSE].Is_One_Time()) || IsDefeated) { 1643 1644 /* 1645 ** Remove the sonar pulse when there is no spied-upon enemy sub pen. 1646 ** Note that this will not remove the one time created sonar pulse. 1647 */ 1648 if (SuperWeapon[SPC_SONAR_PULSE].Remove()) { 1649 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1650 IsRecalcNeeded = true; 1651 } 1652 } 1653 } 1654 1655 /* 1656 ** Check to see if the nuclear weapon should be removed from the sidebar 1657 ** because of outside circumstances. The missile silos 1658 ** being destroyed is a good example of this. 1659 */ 1660 if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Present()) { 1661 if ( (!(ActiveBScan & STRUCTF_MSLO) && !SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) || IsDefeated) { 1662 1663 /* 1664 ** Remove the nuke when there is no missile silo. 1665 ** Note that this will not remove the one time created nuke. 1666 */ 1667 if (SuperWeapon[SPC_NUCLEAR_BOMB].Remove()) { 1668 if (this == PlayerPtr) { 1669 if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { 1670 Map.IsTargettingMode = SPC_NONE; 1671 } 1672 Map.Column[1].Flag_To_Redraw(); 1673 } 1674 IsRecalcNeeded = true; 1675 } 1676 } else { 1677 /* 1678 ** Allow the computer to fire the nuclear weapon when the weapon is 1679 ** ready and the owner is the computer. 1680 */ 1681 if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready() && !IsHuman) { 1682 Special_Weapon_AI(SPC_NUCLEAR_BOMB); 1683 } 1684 } 1685 1686 } else { 1687 /* 1688 ** If there is no nuclear missile present, but there is a missile 1689 ** silo available, then make the missile available as well. 1690 */ 1691 if ((ActiveBScan & STRUCTF_MSLO) && 1692 ((ActLike != HOUSE_USSR && ActLike != HOUSE_UKRAINE) || Session.Type != GAME_NORMAL) && 1693 (IsHuman || IQ >= Rule.IQSuperWeapons)) { 1694 1695 SuperWeapon[SPC_NUCLEAR_BOMB].Enable(false, this == PlayerPtr, Power_Fraction() < 1); 1696 1697 /* 1698 ** Flag the sidebar to be redrawn if necessary. 1699 */ 1700 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1701 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1702 if (IsHuman) { 1703 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, this); 1704 } 1705 } else { 1706 if (this == PlayerPtr) { 1707 Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); 1708 Map.Column[1].Flag_To_Redraw(); 1709 } 1710 } 1711 } 1712 } 1713 1714 1715 if (SuperWeapon[SPC_SPY_MISSION].Is_Present()) { 1716 if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { 1717 if (SuperWeapon[SPC_SPY_MISSION].Remove()) { 1718 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1719 IsRecalcNeeded = true; 1720 } 1721 } else { 1722 if (this == PlayerPtr && !SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { 1723 Map.Column[1].Flag_To_Redraw(); 1724 } 1725 if (SuperWeapon[SPC_SPY_MISSION].Is_Ready() && !IsHuman) { 1726 Special_Weapon_AI(SPC_SPY_MISSION); 1727 } 1728 } 1729 } else { 1730 if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && !Scen.IsNoSpyPlane && Control.TechLevel >= Rule.SpyPlaneTechLevel) { 1731 SuperWeapon[SPC_SPY_MISSION].Enable(false, this == PlayerPtr, false); 1732 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1733 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1734 if (IsHuman) { 1735 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SPY_MISSION, this); 1736 } 1737 } else { 1738 if (this == PlayerPtr) { 1739 Map.Add(RTTI_SPECIAL, SPC_SPY_MISSION); 1740 Map.Column[1].Flag_To_Redraw(); 1741 } 1742 } 1743 } 1744 } 1745 1746 if (SuperWeapon[SPC_PARA_BOMB].Is_Present()) { 1747 if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { 1748 if (SuperWeapon[SPC_PARA_BOMB].Remove()) { 1749 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1750 IsRecalcNeeded = true; 1751 } 1752 } else { 1753 if (SuperWeapon[SPC_PARA_BOMB].Is_Ready() && !IsHuman) { 1754 Special_Weapon_AI(SPC_PARA_BOMB); 1755 } 1756 } 1757 } else { 1758 if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaBombTechLevel && Session.Type == GAME_NORMAL) { 1759 SuperWeapon[SPC_PARA_BOMB].Enable(false, this == PlayerPtr, false); 1760 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1761 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1762 if (IsHuman) { 1763 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, this); 1764 } 1765 } else { 1766 if (this == PlayerPtr) { 1767 Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); 1768 Map.Column[1].Flag_To_Redraw(); 1769 } 1770 } 1771 } 1772 } 1773 1774 if (SuperWeapon[SPC_PARA_INFANTRY].Is_Present()) { 1775 if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { 1776 if (SuperWeapon[SPC_PARA_INFANTRY].Remove()) { 1777 if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); 1778 IsRecalcNeeded = true; 1779 } 1780 } else { 1781 if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready() && !IsHuman) { 1782 Special_Weapon_AI(SPC_PARA_INFANTRY); 1783 } 1784 } 1785 } else { 1786 if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaInfantryTechLevel) { 1787 SuperWeapon[SPC_PARA_INFANTRY].Enable(false, this == PlayerPtr, false); 1788 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 1789 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1790 if (IsHuman) { 1791 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_INFANTRY, this); 1792 } 1793 } else { 1794 if (this == PlayerPtr) { 1795 Map.Add(RTTI_SPECIAL, SPC_PARA_INFANTRY); 1796 Map.Column[1].Flag_To_Redraw(); 1797 } 1798 } 1799 } 1800 } 1801 } 1802 1803 1804 /*********************************************************************************************** 1805 * HouseClass::Attacked -- Lets player know if base is under attack. * 1806 * * 1807 * Call this function whenever a building is attacked (with malice). This function will * 1808 * then announce to the player that his base is under attack. It checks to make sure that * 1809 * this is referring to the player's house rather than the enemy's. * 1810 * * 1811 * INPUT: none * 1812 * * 1813 * OUTPUT: none * 1814 * * 1815 * WARNINGS: none * 1816 * * 1817 * HISTORY: * 1818 * 12/27/1994 JLB : Created. * 1819 *=============================================================================================*/ 1820 void HouseClass::Attacked(BuildingClass* source) 1821 { 1822 assert(Houses.ID(this) == ID); 1823 1824 #ifdef FIXIT_BASE_ANNOUNCE 1825 if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) { 1826 #else 1827 if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) { 1828 #endif 1829 if (Session.Type == GAME_NORMAL) { 1830 Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0); 1831 } else { 1832 Speak(VOX_BASE_UNDER_ATTACK, this); 1833 } 1834 1835 // MBL 06.13.2020 - Timing change from 2 minute cooldown, per https://jaas.ea.com/browse/TDRA-6784 1836 // SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); // 2 minutes 1837 // SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE/2); // 30 seconds as requested 1838 SpeakAttackDelay = Options.Normalize_Delay( (TICKS_PER_MINUTE/2)+(TICKS_PER_SECOND*5) ); // Tweaked for accuracy 1839 1840 /* 1841 ** If there is a trigger event associated with being attacked, process it 1842 ** now. 1843 */ 1844 for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { 1845 HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED); 1846 } 1847 } 1848 } 1849 1850 1851 /*********************************************************************************************** 1852 * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * 1853 * * 1854 * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between * 1855 * all storage capable buildings for the house. Harvested Tiberium adds to the credit * 1856 * value of the house, but only up to the maximum storage capacity that the house can * 1857 * currently maintain. * 1858 * * 1859 * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. * 1860 * * 1861 * OUTPUT: none * 1862 * * 1863 * WARNINGS: none * 1864 * * 1865 * HISTORY: * 1866 * 01/25/1995 JLB : Created. * 1867 *=============================================================================================*/ 1868 void HouseClass::Harvested(unsigned tiberium) 1869 { 1870 assert(Houses.ID(this) == ID); 1871 1872 long oldtib = Tiberium; 1873 1874 Tiberium += tiberium; 1875 if (Tiberium > Capacity) { 1876 Tiberium = Capacity; 1877 IsMaxedOut = true; 1878 } 1879 HarvestedCredits += tiberium; 1880 Silo_Redraw_Check(oldtib, Capacity); 1881 } 1882 1883 1884 /*********************************************************************************************** 1885 * HouseClass::Stole -- Accounts for the value of a captured building. * 1886 * * 1887 * Use this routine whenever a building is captured. It keeps track of the cost of the * 1888 * building for use in the scoring routine, because you get an 'economy' boost for the * 1889 * value of the stolen building (but you don't get the credit value for it.) * 1890 * * 1891 * INPUT: worth -- The worth of the building we captured (stole). * 1892 * * 1893 * OUTPUT: none * 1894 * * 1895 * WARNINGS: none * 1896 * * 1897 * HISTORY: * 1898 * 09/05/1996 BWG : Created. * 1899 *=============================================================================================*/ 1900 void HouseClass::Stole(unsigned worth) 1901 { 1902 assert(Houses.ID(this) == ID); 1903 1904 StolenBuildingsCredits += worth; 1905 } 1906 1907 1908 /*********************************************************************************************** 1909 * HouseClass::Available_Money -- Fetches the total credit worth of the house. * 1910 * * 1911 * Use this routine to determine the total credit value of the house. This is the sum of * 1912 * the harvested Tiberium in storage and the initial unspent cash reserves. * 1913 * * 1914 * INPUT: none * 1915 * * 1916 * OUTPUT: Returns with the total credit value of the house. * 1917 * * 1918 * WARNINGS: none * 1919 * * 1920 * HISTORY: * 1921 * 01/25/1995 JLB : Created. * 1922 *=============================================================================================*/ 1923 long HouseClass::Available_Money(void) const 1924 { 1925 assert(Houses.ID(this) == ID); 1926 1927 return(Tiberium + Credits); 1928 } 1929 1930 1931 /*********************************************************************************************** 1932 * HouseClass::Spend_Money -- Removes money from the house. * 1933 * * 1934 * Use this routine to extract money from the house. Typically, this is a result of * 1935 * production spending. The money is extracted from available cash reserves first. When * 1936 * cash reserves are exhausted, then Tiberium is consumed. * 1937 * * 1938 * INPUT: money -- The amount of money to spend. * 1939 * * 1940 * OUTPUT: none * 1941 * * 1942 * WARNINGS: none * 1943 * * 1944 * HISTORY: * 1945 * 01/25/1995 JLB : Created. * 1946 * 06/20/1995 JLB : Spends Tiberium before spending cash. * 1947 *=============================================================================================*/ 1948 void HouseClass::Spend_Money(unsigned money) 1949 { 1950 assert(Houses.ID(this) == ID); 1951 1952 long oldtib = Tiberium; 1953 if (money > (unsigned)Tiberium) { 1954 money -= (unsigned)Tiberium; 1955 Tiberium = 0; 1956 Credits -= money; 1957 } else { 1958 Tiberium -= money; 1959 } 1960 Silo_Redraw_Check(oldtib, Capacity); 1961 CreditsSpent += money; 1962 } 1963 1964 1965 /*********************************************************************************************** 1966 * HouseClass::Refund_Money -- Refunds money to back to the house. * 1967 * * 1968 * Use this routine when money needs to be refunded back to the house. This can occur when * 1969 * construction is aborted. At this point, the exact breakdown of Tiberium or initial * 1970 * credits used for the orignal purchase is lost. Presume as much of the money is in the * 1971 * form of Tiberium as storage capacity will allow. * 1972 * * 1973 * INPUT: money -- The number of credits to refund back to the house. * 1974 * * 1975 * OUTPUT: none * 1976 * * 1977 * WARNINGS: none * 1978 * * 1979 * HISTORY: * 1980 * 01/25/1995 JLB : Created. * 1981 * 06/01/1995 JLB : Refunded money is never lost * 1982 *=============================================================================================*/ 1983 void HouseClass::Refund_Money(unsigned money) 1984 { 1985 assert(Houses.ID(this) == ID); 1986 1987 Credits += money; 1988 } 1989 1990 1991 /*********************************************************************************************** 1992 * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * 1993 * * 1994 * Use this routine to adjust the maximum storage capacity for the house. This storage * 1995 * capacity will limit the number of Tiberium credits that can be stored at any one time. * 1996 * * 1997 * INPUT: adjust -- The adjustment to the Tiberium storage capacity. * 1998 * * 1999 * inanger -- Is this a forced adjustment to capacity due to some hostile event? * 2000 * * 2001 * OUTPUT: Returns with the number of Tiberium credits lost. * 2002 * * 2003 * WARNINGS: none * 2004 * * 2005 * HISTORY: * 2006 * 01/25/1995 JLB : Created. * 2007 *=============================================================================================*/ 2008 int HouseClass::Adjust_Capacity(int adjust, bool inanger) 2009 { 2010 assert(Houses.ID(this) == ID); 2011 2012 long oldcap = Capacity; 2013 int retval = 0; 2014 2015 Capacity += adjust; 2016 Capacity = max(Capacity, 0L); 2017 if (Tiberium > Capacity) { 2018 retval = Tiberium - Capacity; 2019 Tiberium = Capacity; 2020 if (!inanger) { 2021 Refund_Money(retval); 2022 retval = 0; 2023 } else { 2024 IsMaxedOut = true; 2025 } 2026 } 2027 Silo_Redraw_Check(Tiberium, oldcap); 2028 return(retval); 2029 } 2030 2031 2032 /*********************************************************************************************** 2033 * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * 2034 * * 2035 * Call this routine when either the capacity or tiberium levels change for a house. This * 2036 * routine will determine if the aggregate tiberium storage level will result in the * 2037 * silos changing their imagery. If this is detected, then all the silos for this house * 2038 * are flagged to be redrawn. * 2039 * * 2040 * INPUT: oldtib -- Pre-change tiberium level. * 2041 * * 2042 * oldcap -- Pre-change tiberium storage capacity. * 2043 * * 2044 * OUTPUT: none * 2045 * * 2046 * WARNINGS: none * 2047 * * 2048 * HISTORY: * 2049 * 02/02/1995 JLB : Created. * 2050 *=============================================================================================*/ 2051 void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap) 2052 { 2053 assert(Houses.ID(this) == ID); 2054 2055 int oldratio = 0; 2056 if (oldcap) oldratio = (oldtib * 5) / oldcap; 2057 int newratio = 0; 2058 if (Capacity) newratio = (Tiberium * 5) / Capacity; 2059 2060 if (oldratio != newratio) { 2061 for (int index = 0; index < Buildings.Count(); index++) { 2062 BuildingClass * b = Buildings.Ptr(index); 2063 if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) { 2064 b->Mark(MARK_CHANGE); 2065 } 2066 } 2067 } 2068 } 2069 2070 2071 /*********************************************************************************************** 2072 * HouseClass::Is_Ally -- Determines if the specified house is an ally. * 2073 * * 2074 * This routine will determine if the house number specified is a ally to this house. * 2075 * * 2076 * INPUT: house -- The house number to check to see if it is an ally. * 2077 * * 2078 * OUTPUT: Is the house an ally? * 2079 * * 2080 * WARNINGS: none * 2081 * * 2082 * HISTORY: * 2083 * 05/08/1995 JLB : Created. * 2084 *=============================================================================================*/ 2085 bool HouseClass::Is_Ally(HousesType house) const 2086 { 2087 assert(Houses.ID(this) == ID); 2088 2089 if (house != HOUSE_NONE) { 2090 return(((1<<house) & Allies) != 0); 2091 } 2092 return(false); 2093 } 2094 2095 2096 /*********************************************************************************************** 2097 * HouseClass::Is_Ally -- Determines if the specified house is an ally. * 2098 * * 2099 * This routine will examine the specified house and determine if it is an ally. * 2100 * * 2101 * INPUT: house -- Pointer to the house object to check for ally relationship. * 2102 * * 2103 * OUTPUT: Is the specified house an ally? * 2104 * * 2105 * WARNINGS: none * 2106 * * 2107 * HISTORY: * 2108 * 05/08/1995 JLB : Created. * 2109 *=============================================================================================*/ 2110 bool HouseClass::Is_Ally(HouseClass const * house) const 2111 { 2112 assert(Houses.ID(this) == ID); 2113 2114 if (house) { 2115 return(Is_Ally(house->Class->House)); 2116 } 2117 return(false); 2118 } 2119 2120 2121 /*********************************************************************************************** 2122 * HouseClass::Is_Ally -- Checks to see if the object is an ally. * 2123 * * 2124 * This routine will examine the specified object and return whether it is an ally or not. * 2125 * * 2126 * INPUT: object -- The object to examine to see if it is an ally. * 2127 * * 2128 * OUTPUT: Is the specified object an ally? * 2129 * * 2130 * WARNINGS: none * 2131 * * 2132 * HISTORY: * 2133 * 05/08/1995 JLB : Created. * 2134 *=============================================================================================*/ 2135 bool HouseClass::Is_Ally(ObjectClass const * object) const 2136 { 2137 assert(Houses.ID(this) == ID); 2138 2139 if (object) { 2140 return(Is_Ally(object->Owner())); 2141 } 2142 return(false); 2143 } 2144 2145 2146 /*********************************************************************************************** 2147 * HouseClass::Make_Ally -- Make the specified house an ally. * 2148 * * 2149 * This routine will make the specified house an ally to this house. An allied house will * 2150 * not be considered a threat or potential target. * 2151 * * 2152 * INPUT: house -- The house to make an ally of this house. * 2153 * * 2154 * OUTPUT: none * 2155 * * 2156 * WARNINGS: none * 2157 * * 2158 * HISTORY: * 2159 * 05/08/1995 JLB : Created. * 2160 * 08/08/1995 JLB : Breaks off combat when ally commences. * 2161 * 10/17/1995 JLB : Added reveal base when allied. * 2162 *=============================================================================================*/ 2163 void HouseClass::Make_Ally(HousesType house) 2164 { 2165 assert(Houses.ID(this) == ID); 2166 2167 if (Is_Allowed_To_Ally(house)) { 2168 2169 Allies |= (1L << house); 2170 2171 /* 2172 ** Don't consider the newfound ally to be an enemy -- of course. 2173 */ 2174 if (Enemy == house) { 2175 Enemy = HOUSE_NONE; 2176 } 2177 2178 if (ScenarioInit) { 2179 Control.Allies |= (1L << house); 2180 } 2181 2182 if (Session.Type != GAME_NORMAL && !ScenarioInit) { 2183 HouseClass * hptr = HouseClass::As_Pointer(house); 2184 2185 /* 2186 ** An alliance with another human player will cause the computer 2187 ** players (if present) to become paranoid. 2188 */ 2189 if (hptr != NULL && IsHuman && Rule.IsComputerParanoid) { 2190 // if (hptr != NULL && hptr->IsHuman) { 2191 // if (!hptr->IsHuman) { 2192 // hptr->Make_Ally(Class->House); 2193 // } 2194 Computer_Paranoid(); 2195 } 2196 2197 char buffer[80]; 2198 2199 /* 2200 ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure 2201 ** that fighting will most likely stop when the cease fire begins. 2202 */ 2203 for (int index = 0; index < Logic.Count(); index++) { 2204 ObjectClass * object = Logic[index]; 2205 2206 if (object != NULL && object->Is_Techno() && !object->IsInLimbo && object->Owner() == Class->House) { 2207 TARGET target = ((TechnoClass *)object)->TarCom; 2208 if (Target_Legal(target) && As_Techno(target) != NULL) { 2209 if (Is_Ally(As_Techno(target))) { 2210 ((TechnoClass *)object)->Assign_Target(TARGET_NONE); 2211 } 2212 } 2213 } 2214 } 2215 2216 /* 2217 ** Cause all structures to be revealed to the house that has been 2218 ** allied with. 2219 */ 2220 if (Rule.IsAllyReveal && house == PlayerPtr->Class->House) { 2221 for (int index = 0; index < Buildings.Count(); index++) { 2222 BuildingClass const * b = Buildings.Ptr(index); 2223 2224 if (b && !b->IsInLimbo && (HouseClass *)b->House == this) { 2225 Map.Sight_From(Coord_Cell(b->Center_Coord()), b->Class->SightRange, PlayerPtr, false); 2226 } 2227 } 2228 } 2229 2230 if (IsHuman) { 2231 sprintf(buffer, Text_String(TXT_HAS_ALLIED), IniName, HouseClass::As_Pointer(house)->IniName); 2232 // sprintf(buffer, Text_String(TXT_HAS_ALLIED), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[((HouseClass::As_Pointer(house))->Class->House) - HOUSE_MULTI1]->Name); 2233 Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); 2234 } 2235 2236 #if(TEN) 2237 // 2238 // Notify the TEN server of the new alliance 2239 // 2240 if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_TEN) { 2241 Send_TEN_Alliance(hptr->IniName, 1); 2242 } 2243 #endif // TEN 2244 2245 #if(MPATH) 2246 // 2247 // Notify the MPATH server of the new alliance 2248 // 2249 //if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_MPATH) { 2250 //Send_MPATH_Alliance(hptr->IniName, 1); 2251 //} 2252 #endif // MPATH 2253 2254 Map.Flag_To_Redraw(false); 2255 } 2256 } 2257 } 2258 2259 2260 /*********************************************************************************************** 2261 * HouseClass::Make_Enemy -- Make an enemy of the house specified. * 2262 * * 2263 * This routine will flag the house specified so that it will be an enemy to this house. * 2264 * Enemy houses are legal targets for attack. * 2265 * * 2266 * INPUT: house -- The house to make an enemy of this house. * 2267 * * 2268 * OUTPUT: none * 2269 * * 2270 * WARNINGS: none * 2271 * * 2272 * HISTORY: * 2273 * 05/08/1995 JLB : Created. * 2274 * 07/27/1995 JLB : Making war is a bilateral action. * 2275 *=============================================================================================*/ 2276 void HouseClass::Make_Enemy(HousesType house) 2277 { 2278 assert(Houses.ID(this) == ID); 2279 2280 if (house != HOUSE_NONE && Is_Ally(house)) { 2281 HouseClass * enemy = HouseClass::As_Pointer(house); 2282 Allies &= ~(1L << house); 2283 2284 if (ScenarioInit) { 2285 Control.Allies &= !(1L << house); 2286 } 2287 2288 /* 2289 ** Breaking an alliance is a bilateral event. 2290 */ 2291 if (enemy != NULL && enemy->Is_Ally(this)) { 2292 enemy->Allies &= ~(1L << Class->House); 2293 2294 if (ScenarioInit) { 2295 Control.Allies &= ~(1L << Class->House); 2296 } 2297 } 2298 2299 if ((Debug_Flag || Session.Type != GAME_NORMAL) && !ScenarioInit && IsHuman) { 2300 char buffer[80]; 2301 2302 sprintf(buffer, Text_String(TXT_AT_WAR), IniName, HouseClass::As_Pointer(house)->IniName); 2303 // sprintf(buffer, Text_String(TXT_AT_WAR), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[enemy->Class->House - HOUSE_MULTI1]->Name); 2304 Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); 2305 Map.Flag_To_Redraw(false); 2306 2307 #if(TEN) 2308 // 2309 // Notify the TEN server of the broken alliance 2310 // 2311 if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_TEN) { 2312 Send_TEN_Alliance(enemy->IniName, 0); 2313 } 2314 #endif // TEN 2315 2316 #if(MPATH) 2317 // 2318 // Notify the MPATH server of the broken alliance 2319 // 2320 //if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_MPATH) { 2321 //Send_MPATH_Alliance(enemy->IniName, 0); 2322 //} 2323 #endif // MPATH 2324 2325 } 2326 } 2327 } 2328 2329 2330 /*********************************************************************************************** 2331 * HouseClass::Remap_Table -- Fetches the remap table for this house object. * 2332 * * 2333 * This routine will return with the remap table to use when displaying an object owned * 2334 * by this house. If the object is blushing (flashing), then the lightening remap table is * 2335 * always used. The "unit" parameter allows proper remap selection for those houses that * 2336 * have a different remap table for buildings or units. * 2337 * * 2338 * INPUT: blushing -- Is the object blushing (flashing)? * 2339 * * 2340 * remap -- The remap control value to use. * 2341 * REMAP_NONE No remap pointer returned at all. * 2342 * REMAP_NORMAL Return the remap pointer for this house. * 2343 * REMAP_ALTERNATE (Nod solo play only -- forces red remap). * 2344 * Multiplay returns same as REMAP_NORMAL * 2345 * * 2346 * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. * 2347 * * 2348 * WARNINGS: none * 2349 * * 2350 * HISTORY: * 2351 * 05/08/1995 JLB : Created. * 2352 * 10/25/1995 JLB : Uses remap control value. * 2353 *=============================================================================================*/ 2354 unsigned char const * HouseClass::Remap_Table(bool blushing, RemapType remap) const 2355 { 2356 assert(Houses.ID(this) == ID); 2357 2358 if (blushing) return(&Map.FadingLight[0]); 2359 2360 if (remap == REMAP_NONE) return(0); 2361 2362 return(ColorRemaps[RemapColor].RemapTable); 2363 } 2364 2365 2366 /*********************************************************************************************** 2367 * HouseClass::Suggested_New_Team -- Determine what team should be created. * 2368 * * 2369 * This routine examines the house condition and returns with the team that it thinks * 2370 * should be created. The units that are not currently a member of a team are examined * 2371 * to determine the team needed. * 2372 * * 2373 * INPUT: alertcheck -- Select from the auto-create team list. * 2374 * * 2375 * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * 2376 * be created, then NULL is returned. * 2377 * * 2378 * WARNINGS: none * 2379 * * 2380 * HISTORY: * 2381 * 05/08/1995 JLB : Created. * 2382 *=============================================================================================*/ 2383 TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck) 2384 { 2385 assert(Houses.ID(this) == ID); 2386 2387 return(TeamTypeClass::Suggested_New_Team(this, AScan, UScan, IScan, VScan, alertcheck)); 2388 } 2389 2390 2391 /*********************************************************************************************** 2392 * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * 2393 * * 2394 * This routine is called when the threat rating for a region needs to change. The region * 2395 * and threat adjustment are provided. * 2396 * * 2397 * INPUT: region -- The region that adjustment is to occur on. * 2398 * * 2399 * threat -- The threat adjustment to perform. * 2400 * * 2401 * OUTPUT: none * 2402 * * 2403 * WARNINGS: none * 2404 * * 2405 * HISTORY: * 2406 * 05/08/1995 JLB : Created. * 2407 *=============================================================================================*/ 2408 void HouseClass::Adjust_Threat(int region, int threat) 2409 { 2410 assert(Houses.ID(this) == ID); 2411 2412 static int _val[] = { 2413 -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1, 2414 -1, 0, 1, 2415 MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1 2416 }; 2417 static int _thr[] = { 2418 2, 1, 2, 2419 1, 0, 1, 2420 2, 1, 2 2421 }; 2422 int neg; 2423 int * val = &_val[0]; 2424 int * thr = &_thr[0]; 2425 2426 if (threat < 0) { 2427 threat = -threat; 2428 neg = true; 2429 } else { 2430 neg = false; 2431 } 2432 2433 for (int lp = 0; lp < 9; lp ++) { 2434 Regions[region + *val].Adjust_Threat(threat >> *thr, neg); 2435 val++; 2436 thr++; 2437 } 2438 } 2439 2440 2441 /*********************************************************************************************** 2442 * HouseClass::Begin_Production -- Starts production of the specified object type. * 2443 * * 2444 * This routine is called from the event system. It will start production for the object * 2445 * type specified. This will be reflected in the sidebar as well as the house factory * 2446 * tracking variables. * 2447 * * 2448 * INPUT: type -- The type of object to begin production on. * 2449 * * 2450 * id -- The subtype of object. * 2451 * * 2452 * OUTPUT: Returns with the reason why, or why not, production was started. * 2453 * * 2454 * WARNINGS: none * 2455 * * 2456 * HISTORY: * 2457 * 05/08/1995 JLB : Created. * 2458 * 10/21/1996 JLB : Handles max object case. * 2459 *=============================================================================================*/ 2460 ProdFailType HouseClass::Begin_Production(RTTIType type, int id) 2461 { 2462 assert(Houses.ID(this) == ID); 2463 int result = true; 2464 bool initial_start = false; 2465 FactoryClass * fptr; 2466 TechnoTypeClass const * tech = Fetch_Techno_Type(type, id); 2467 2468 fptr = Fetch_Factory(type); 2469 2470 /* 2471 ** If the house is already busy producing the requested object, then 2472 ** return with this failure code, unless we are restarting production. 2473 */ 2474 if (fptr != NULL) { 2475 if (fptr->Is_Building()) { 2476 return(PROD_CANT); 2477 } 2478 } else { 2479 fptr = new FactoryClass(); 2480 if (!fptr) return(PROD_CANT); 2481 Set_Factory(type, fptr); 2482 result = fptr->Set(*tech, *this); 2483 initial_start = true; 2484 2485 /* 2486 ** If set failed, we probably reached the production cap. Don't let the factory linger, preventing further production attempts. 2487 ** ST - 3/17/2020 2:03PM 2488 */ 2489 if (!result) { 2490 Set_Factory(type, NULL); 2491 delete fptr; 2492 fptr = NULL; 2493 } 2494 } 2495 2496 if (result) { 2497 fptr->Start(); 2498 2499 /* 2500 ** Link this factory to the sidebar so that proper graphic feedback 2501 ** can take place. 2502 */ 2503 // Handle Glyphx multiplayer sidebar. ST - 8/14/2019 1:26PM 2504 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 2505 if (IsHuman) { 2506 Sidebar_Glyphx_Factory_Link(fptr->ID, type, id, this); 2507 } 2508 } else { 2509 if (PlayerPtr == this) { 2510 Map.Factory_Link(fptr->ID, type, id); 2511 } 2512 } 2513 2514 return(PROD_OK); 2515 } 2516 2517 delete fptr; 2518 return(PROD_CANT); 2519 } 2520 2521 2522 /*********************************************************************************************** 2523 * HouseClass::Suspend_Production -- Temporarily puts production on hold. * 2524 * * 2525 * This routine is called from the event system whenever the production of the specified * 2526 * type needs to be suspended. The suspended production will be reflected in the sidebar * 2527 * as well as in the house control structure. * 2528 * * 2529 * INPUT: type -- The type of object that production is being suspended for. * 2530 * * 2531 * OUTPUT: Returns why, or why not, production was suspended. * 2532 * * 2533 * WARNINGS: none * 2534 * * 2535 * HISTORY: * 2536 * 05/08/1995 JLB : Created. * 2537 *=============================================================================================*/ 2538 ProdFailType HouseClass::Suspend_Production(RTTIType type) 2539 { 2540 assert(Houses.ID(this) == ID); 2541 2542 FactoryClass * fptr = Fetch_Factory(type); 2543 2544 /* 2545 ** If the house is already busy producing the requested object, then 2546 ** return with this failure code. 2547 */ 2548 if (fptr == NULL) return(PROD_CANT); 2549 2550 /* 2551 ** Actually suspend the production. 2552 */ 2553 fptr->Suspend(); 2554 2555 /* 2556 ** Tell the sidebar that it needs to be redrawn because of this. 2557 */ 2558 if (PlayerPtr == this) { 2559 Map.SidebarClass::IsToRedraw = true; 2560 if (!RunningAsDLL) { // Don't force a redraw when running under GlyphX. PlayerPtr==this will always be true in this case, and we don't want to force a redraw even for AI players 2561 Map.Flag_To_Redraw(false); 2562 } 2563 } 2564 2565 return(PROD_OK); 2566 } 2567 2568 2569 /*********************************************************************************************** 2570 * HouseClass::Abandon_Production -- Abandons production of item type specified. * 2571 * * 2572 * This routine is called from the event system whenever production must be abandoned for * 2573 * the type specified. This will remove the factory and pending object from the sidebar as * 2574 * well as from the house factory record. * 2575 * * 2576 * INPUT: type -- The object type that production is being suspended for. * 2577 * * 2578 * OUTPUT: Returns the reason why or why not, production was suspended. * 2579 * * 2580 * WARNINGS: none * 2581 * * 2582 * HISTORY: * 2583 * 05/08/1995 JLB : Created. * 2584 *=============================================================================================*/ 2585 ProdFailType HouseClass::Abandon_Production(RTTIType type) 2586 { 2587 assert(Houses.ID(this) == ID); 2588 2589 FactoryClass * fptr = Fetch_Factory(type); 2590 2591 /* 2592 ** If there is no factory to abandon, then return with a failure code. 2593 */ 2594 if (fptr == NULL) return(PROD_CANT); 2595 2596 /* 2597 ** Tell the sidebar that it needs to be redrawn because of this. 2598 */ 2599 // Handle Glyphx multiplayer sidebar. ST - 8/7/2019 10:18AM 2600 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 2601 if (IsHuman) { 2602 Sidebar_Glyphx_Abandon_Production(type, fptr->ID, this); 2603 2604 // Need to clear pending object here if legacy renderer enabled 2605 2606 if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING && Map.PendingObjectPtr) { 2607 Map.PendingObjectPtr = 0; 2608 Map.PendingObject = 0; 2609 Map.PendingHouse = HOUSE_NONE; 2610 Map.Set_Cursor_Shape(0); 2611 } 2612 } 2613 } else { 2614 if (PlayerPtr == this) { 2615 Map.Abandon_Production(type, fptr->ID); 2616 2617 if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { 2618 Map.PendingObjectPtr = 0; 2619 Map.PendingObject = 0; 2620 Map.PendingHouse = HOUSE_NONE; 2621 Map.Set_Cursor_Shape(0); 2622 } 2623 } 2624 } 2625 2626 /* 2627 ** Abandon production of the object. 2628 */ 2629 fptr->Abandon(); 2630 Set_Factory(type, NULL); 2631 delete fptr; 2632 2633 return(PROD_OK); 2634 } 2635 2636 2637 /*********************************************************************************************** 2638 * HouseClass::Special_Weapon_AI -- Fires special weapon. * 2639 * * 2640 * This routine will pick a good target to fire the special weapon specified. * 2641 * * 2642 * INPUT: id -- The special weapon id to fire. * 2643 * * 2644 * OUTPUT: none * 2645 * * 2646 * WARNINGS: none * 2647 * * 2648 * HISTORY: * 2649 * 06/24/1995 PWG : Created. * 2650 *=============================================================================================*/ 2651 void HouseClass::Special_Weapon_AI(SpecialWeaponType id) 2652 { 2653 assert(Houses.ID(this) == ID); 2654 2655 /* 2656 ** Loop through all of the building objects on the map 2657 ** and see which ones are available. 2658 */ 2659 BuildingClass * bestptr = NULL; 2660 int best = -1; 2661 2662 for (int index = 0; index < Buildings.Count(); index++) { 2663 BuildingClass * b = Buildings.Ptr(index); 2664 2665 /* 2666 ** If the building is valid, not in limbo, not in the process of 2667 ** being destroyed and not our ally, then we can consider it. 2668 */ 2669 if (b != NULL && !b->IsInLimbo && b->Strength && !Is_Ally(b)) { 2670 if (Percent_Chance(90) && (b->Value() > best || best == -1)) { 2671 best = b->Value(); 2672 bestptr = b; 2673 } 2674 } 2675 } 2676 2677 if (bestptr) { 2678 CELL cell = Coord_Cell(bestptr->Center_Coord()); 2679 Place_Special_Blast(id, cell); 2680 } 2681 } 2682 2683 2684 /*********************************************************************************************** 2685 * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * 2686 * * 2687 * This routine will create a blast effect at the cell specified. This is the result of * 2688 * the special weapons. * 2689 * * 2690 * INPUT: id -- The special weapon id number. * 2691 * * 2692 * cell -- The location where the special weapon attack is to occur. * 2693 * * 2694 * OUTPUT: Was the special weapon successfully fired at the location specified? * 2695 * * 2696 * WARNINGS: none * 2697 * * 2698 * HISTORY: * 2699 * 05/18/1995 JLB : commented. * 2700 * 07/25/1995 JLB : Added scatter effect for nuclear bomb. * 2701 * 07/28/1995 JLB : Revamped to use super weapon class controller. * 2702 *=============================================================================================*/ 2703 extern void Logic_Switch_Player_Context(ObjectClass *object); 2704 extern void Logic_Switch_Player_Context(HouseClass *object); 2705 extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); 2706 2707 bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell) 2708 { 2709 assert(Houses.ID(this) == ID); 2710 2711 // Added. ST - 12/2/2019 11:26AM 2712 bool fired = false; 2713 const char *what = NULL; 2714 2715 BuildingClass * launchsite = 0; 2716 AnimClass * anim = 0; 2717 switch (id) { 2718 case SPC_SONAR_PULSE: 2719 // Automatically discharge the sonar pulse and uncloak all subs. 2720 if (SuperWeapon[SPC_SONAR_PULSE].Is_Ready()) { 2721 SuperWeapon[SPC_SONAR_PULSE].Discharged(this == PlayerPtr); 2722 if (this == PlayerPtr) { 2723 Map.Column[1].Flag_To_Redraw(); 2724 Map.Activate_Pulse(); 2725 } 2726 Sound_Effect(VOC_SONAR); 2727 IsRecalcNeeded = true; 2728 fired = true; 2729 what = "SONAR"; 2730 for (int index = 0; index < Vessels.Count(); index++) { 2731 VesselClass * sub = Vessels.Ptr(index); 2732 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2733 if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { 2734 #else 2735 if (*sub == VESSEL_SS) { 2736 #endif 2737 sub->PulseCountDown = 15 * TICKS_PER_SECOND; 2738 sub->Do_Uncloak(); 2739 } 2740 } 2741 } 2742 break; 2743 2744 case SPC_NUCLEAR_BOMB: 2745 if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready()) { 2746 if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) { 2747 BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(cell), 0, 200, WARHEAD_NUKE, MPH_VERY_FAST); 2748 if (bullet) { 2749 int celly = Cell_Y(cell); 2750 celly -= 15; 2751 if (celly < 1) celly = 1; 2752 COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), celly)); 2753 if (!bullet->Unlimbo(start, DIR_S)) { 2754 delete bullet; 2755 } 2756 SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); 2757 IsRecalcNeeded = true; 2758 fired = true; 2759 what = "NUKE"; 2760 if (this == PlayerPtr) { 2761 Map.Column[1].Flag_To_Redraw(); 2762 Map.IsTargettingMode = SPC_NONE; 2763 } 2764 } 2765 } else { 2766 2767 /* 2768 ** Search for a suitable launch site for this missile. 2769 */ 2770 launchsite = Find_Building(STRUCT_MSLO); 2771 2772 /* 2773 ** If a launch site was found, then proceed with the normal launch 2774 ** sequence. 2775 */ 2776 if (launchsite) { 2777 launchsite->Assign_Mission(MISSION_MISSILE); 2778 launchsite->Commence(); 2779 NukeDest = cell; 2780 } 2781 if (this == PlayerPtr) { 2782 Map.IsTargettingMode = SPC_NONE; 2783 } 2784 SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); 2785 IsRecalcNeeded = true; 2786 fired = true; 2787 what = "NUKE"; 2788 } 2789 } 2790 break; 2791 2792 case SPC_PARA_INFANTRY: 2793 if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready()) { 2794 2795 TeamTypeClass * ttype = TeamTypeClass::As_Pointer("@PINF"); 2796 if (ttype == NULL) { 2797 ttype = new TeamTypeClass; 2798 if (ttype != NULL) { 2799 strcpy(ttype->IniName, "@PINF"); 2800 ttype->IsTransient = true; 2801 ttype->IsPrebuilt = false; 2802 ttype->IsReinforcable = false; 2803 ttype->Origin = WAYPT_SPECIAL; 2804 ttype->MissionCount = 1; 2805 ttype->MissionList[0].Mission = TMISSION_ATT_WAYPT; 2806 ttype->MissionList[0].Data.Value = WAYPT_SPECIAL; 2807 ttype->ClassCount = 2; 2808 ttype->Members[0].Quantity = AircraftTypeClass::As_Reference(AIRCRAFT_BADGER).Max_Passengers(); 2809 ttype->Members[0].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); 2810 ttype->Members[1].Quantity = 1; 2811 ttype->Members[1].Class = &AircraftTypeClass::As_Reference(AIRCRAFT_BADGER); 2812 } 2813 } 2814 2815 if (ttype != NULL) { 2816 ttype->House = Class->House; 2817 Scen.Waypoint[WAYPT_SPECIAL] = Map.Nearby_Location(cell, SPEED_FOOT); 2818 Do_Reinforcements(ttype); 2819 } 2820 2821 // Create_Air_Reinforcement(this, AIRCRAFT_BADGER, 1, MISSION_HUNT, ::As_Target(cell), TARGET_NONE, INFANTRY_E1); 2822 if (this == PlayerPtr) { 2823 Map.IsTargettingMode = SPC_NONE; 2824 } 2825 SuperWeapon[SPC_PARA_INFANTRY].Discharged(this == PlayerPtr); 2826 IsRecalcNeeded = true; 2827 fired = true; 2828 what = "PARA"; 2829 } 2830 break; 2831 2832 case SPC_SPY_MISSION: 2833 if (SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { 2834 Create_Air_Reinforcement(this, AIRCRAFT_U2, 1, MISSION_HUNT, ::As_Target(cell), ::As_Target(cell)); 2835 if (this == PlayerPtr) { 2836 Map.IsTargettingMode = SPC_NONE; 2837 } 2838 SuperWeapon[SPC_SPY_MISSION].Discharged(this == PlayerPtr); 2839 IsRecalcNeeded = true; 2840 fired = true; 2841 what = "SPY"; 2842 } 2843 break; 2844 2845 case SPC_PARA_BOMB: 2846 if (SuperWeapon[SPC_PARA_BOMB].Is_Ready()) { 2847 Create_Air_Reinforcement(this, AIRCRAFT_BADGER, Rule.BadgerBombCount, MISSION_HUNT, ::As_Target(cell), TARGET_NONE); 2848 if (this == PlayerPtr) { 2849 Map.IsTargettingMode = SPC_NONE; 2850 } 2851 SuperWeapon[SPC_PARA_BOMB].Discharged(this == PlayerPtr); 2852 IsRecalcNeeded = true; 2853 fired = true; 2854 what = "PARABOMB"; 2855 } 2856 break; 2857 2858 case SPC_IRON_CURTAIN: 2859 if (SuperWeapon[SPC_IRON_CURTAIN].Is_Ready()) { 2860 int x = Keyboard->MouseQX - Map.TacPixelX; 2861 int y = Keyboard->MouseQY - Map.TacPixelY; 2862 TechnoClass * tech = Map[cell].Cell_Techno(x, y); 2863 if (tech) { 2864 switch (tech->What_Am_I()) { 2865 case RTTI_UNIT: 2866 case RTTI_BUILDING: 2867 case RTTI_VESSEL: 2868 case RTTI_AIRCRAFT: 2869 tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; 2870 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2871 if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { 2872 tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_SECOND; 2873 } 2874 #endif 2875 tech->Mark(MARK_CHANGE); 2876 Sound_Effect(VOC_IRON1, tech->Center_Coord()); 2877 if (this == PlayerPtr) { 2878 Map.IsTargettingMode = SPC_NONE; 2879 } 2880 SuperWeapon[SPC_IRON_CURTAIN].Discharged(this == PlayerPtr); 2881 break; 2882 default: 2883 break; 2884 } 2885 } 2886 2887 IsRecalcNeeded = true; 2888 fired = true; 2889 what = "IRON"; 2890 } 2891 break; 2892 2893 case SPC_CHRONOSPHERE: 2894 if (SuperWeapon[SPC_CHRONOSPHERE].Is_Ready()) { 2895 int x = Keyboard->MouseQX - Map.TacPixelX; 2896 int y = Keyboard->MouseQY - Map.TacPixelY; 2897 TechnoClass * tech = Map[cell].Cell_Techno(x, y); 2898 if (tech && Is_Ally(tech)) { 2899 if (tech->What_Am_I() == RTTI_UNIT || 2900 tech->What_Am_I() == RTTI_INFANTRY || 2901 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 2902 (tech->What_Am_I() == RTTI_VESSEL && (*((VesselClass *)tech) != VESSEL_TRANSPORT && *((VesselClass *)tech) != VESSEL_CARRIER) )) { 2903 #else 2904 (tech->What_Am_I() == RTTI_VESSEL && *((VesselClass *)tech) != VESSEL_TRANSPORT)) { 2905 #endif 2906 2907 if (tech->What_Am_I() != RTTI_UNIT || !((UnitClass *)tech)->IsDeploying) { 2908 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2909 bool porthim = true; 2910 if(tech->What_Am_I() == RTTI_UNIT && ((UnitClass *)tech)->Class->Type == UNIT_CHRONOTANK) { 2911 porthim = false; 2912 } 2913 if (porthim) { 2914 #endif 2915 HouseClass* old_player_ptr = PlayerPtr; 2916 Logic_Switch_Player_Context(this); 2917 Map.IsTargettingMode = SPC_CHRONO2; 2918 On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode); 2919 Logic_Switch_Player_Context(old_player_ptr); 2920 UnitToTeleport = tech->As_Target(); 2921 fired = true; 2922 what = "CHRONO"; 2923 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2924 } 2925 #endif 2926 } 2927 } 2928 } 2929 } 2930 break; 2931 2932 case SPC_CHRONO2: 2933 { 2934 TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); 2935 CELL oldcell = cell; 2936 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2937 if (tech != NULL && tech->IsActive && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { 2938 #else 2939 if (tech != NULL && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { 2940 #endif 2941 /* 2942 ** Destroy any infantryman that gets teleported 2943 */ 2944 if (tech->What_Am_I() == RTTI_INFANTRY) { 2945 InfantryClass * inf = (InfantryClass *)tech; 2946 inf->Mark(MARK_UP); 2947 inf->Coord = Cell_Coord(cell); 2948 inf->Mark(MARK_DOWN); 2949 int damage = inf->Strength; 2950 inf->Take_Damage(damage, 0, WARHEAD_FIRE, 0, true); 2951 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2952 } else if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { 2953 tech->Assign_Target(tech->As_Target()); 2954 #endif 2955 } else { 2956 /* 2957 ** Warp the unit to the new location. 2958 */ 2959 DriveClass * drive = (DriveClass *)tech; 2960 drive->MoebiusCell = Coord_Cell(drive->Coord); 2961 oldcell = drive->MoebiusCell; 2962 drive->Teleport_To(cell); 2963 drive->IsMoebius = true; 2964 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2965 if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { 2966 drive->IsMoebius = false; 2967 } 2968 drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; 2969 if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { 2970 drive->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; 2971 } 2972 #else 2973 drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; 2974 #endif 2975 Scen.Do_BW_Fade(); 2976 Sound_Effect(VOC_CHRONO, drive->Coord); 2977 2978 /* 2979 ** Set active animation on Chronospheres. 2980 */ 2981 for (int index = 0; index < Buildings.Count(); ++index) { 2982 BuildingClass* building = Buildings.Ptr(index); 2983 if (building != nullptr && building->IsActive && building->Owner() == Class->House && *building == STRUCT_CHRONOSPHERE) { 2984 building->Begin_Mode(BSTATE_ACTIVE); 2985 } 2986 } 2987 } 2988 } 2989 UnitToTeleport = TARGET_NONE; 2990 if (this == PlayerPtr) { 2991 Map.IsTargettingMode = SPC_NONE; 2992 } 2993 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2994 if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) { 2995 #endif 2996 SuperWeapon[SPC_CHRONOSPHERE].Discharged(this == PlayerPtr); 2997 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2998 } 2999 #endif 3000 IsRecalcNeeded = true; 3001 fired = true; 3002 what = "CHRONO2"; 3003 3004 /* 3005 ** Now set a percentage chance that a time quake will occur. 3006 */ 3007 if (!TimeQuake) { 3008 TimeQuake = Percent_Chance(Rule.QuakeChance * 100); 3009 } 3010 3011 /* 3012 ** Now set a percentage chance that a chronal vortex will appear. It 3013 ** might appear where the object teleported to or it might appear 3014 ** where it teleported from -- random chance. 3015 */ 3016 #ifdef FIXIT_CSII // checked - ajw 9/28/98 3017 // Don't allow a vortex if the teleportation was due to a chrono tank. 3018 if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) 3019 #endif 3020 if (!ChronalVortex.Is_Active() && Percent_Chance(Rule.VortexChance * 100)) { 3021 int x = Random_Pick(0, Map.MapCellWidth-1); 3022 int y = Random_Pick(0, Map.MapCellHeight-1); 3023 ChronalVortex.Appear(Cell_Coord(XY_Cell(Map.MapCellX + x, Map.MapCellY + y))); 3024 3025 // if (Percent_Chance(50)) { 3026 // ChronalVortex.Appear(Cell_Coord(oldcell)); 3027 // } else { 3028 // ChronalVortex.Appear(Cell_Coord(cell)); 3029 // } 3030 } 3031 3032 break; 3033 } 3034 } 3035 3036 /* 3037 ** Maybe trigger an achivement. ST - 12/2/2019 11:25AM 3038 */ 3039 if (IsHuman && fired && what) { 3040 On_Achievement_Event(this, "SUPERWEAPON_FIRED", what); 3041 } 3042 3043 return(true); 3044 } 3045 3046 3047 /*********************************************************************************************** 3048 * HouseClass::Place_Object -- Places the object (building) at location specified. * 3049 * * 3050 * This routine is called when a building has been produced and now must be placed on * 3051 * the map. When the player clicks on the map, this routine is ultimately called when the * 3052 * event passes through the event queue system. * 3053 * * 3054 * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. * 3055 * * 3056 * * 3057 * cell -- The location to place the object on the map. * 3058 * * 3059 * OUTPUT: Was the placement successful? * 3060 * * 3061 * WARNINGS: none * 3062 * * 3063 * HISTORY: * 3064 * 05/18/1995 JLB : Created. * 3065 *=============================================================================================*/ 3066 extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); 3067 3068 bool HouseClass::Place_Object(RTTIType type, CELL cell) 3069 { 3070 assert(Houses.ID(this) == ID); 3071 3072 TechnoClass * tech = 0; 3073 FactoryClass * factory = Fetch_Factory(type); 3074 3075 /* 3076 ** Only if there is a factory active for this type, can it be "placed". 3077 ** In the case of a missing factory, then this request is completely bogus -- 3078 ** ignore it. This might occur if, between two events to exit the same 3079 ** object, the mouse was clicked on the sidebar to start building again. 3080 ** The second placement event should NOT try to place the object that is 3081 ** just starting construction. 3082 */ 3083 if (factory && factory->Has_Completed()) { 3084 tech = factory->Get_Object(); 3085 3086 if (cell == -1) { 3087 TechnoClass * pending = factory->Get_Object(); 3088 if (pending != NULL) { 3089 3090 #ifdef FIXIT_HELI_LANDING 3091 /* 3092 ** Try to find a place for the object to appear from. For helicopters, it has the 3093 ** option of finding a nearby helipad if no helipads are free. 3094 */ 3095 TechnoClass * builder = pending->Who_Can_Build_Me(false, false); 3096 if (builder == NULL && pending->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass *)pending)->Class->IsFixedWing) { 3097 builder = pending->Who_Can_Build_Me(true, false); 3098 3099 } 3100 #else 3101 bool intheory = false; 3102 if (pending->What_Am_I() == RTTI_AIRCRAFT) { 3103 3104 /* 3105 ** BG hack - helicopters don't need a specific building to 3106 ** emerge from, in fact, they'll land next to a building if 3107 ** need be. 3108 */ 3109 if( !((AircraftClass *)pending)->Class->IsFixedWing) { 3110 intheory = true; 3111 } 3112 } 3113 TechnoClass * builder = pending->Who_Can_Build_Me(intheory, false); 3114 #endif 3115 TechnoTypeClass const *object_type = pending->Techno_Type_Class(); 3116 if (builder != NULL && builder->Exit_Object(pending)) { 3117 3118 /* 3119 ** Since the object has left the factory under its own power, delete 3120 ** the production manager tied to this slot in the sidebar. Its job 3121 ** has been completed. 3122 */ 3123 factory->Set_Is_Blocked(false); 3124 factory->Completed(); 3125 Abandon_Production(type); 3126 3127 /* 3128 ** Could be tied to an achievement. ST - 11/11/2019 11:56AM 3129 */ 3130 if (IsHuman) { 3131 if (object_type) { 3132 On_Achievement_Event(this, "UNIT_CONSTRUCTED", object_type->IniName); 3133 } 3134 if (pending->IsActive) { 3135 On_Ping(this, pending->Center_Coord()); 3136 } 3137 } 3138 3139 switch (pending->What_Am_I()) { 3140 case RTTI_UNIT: 3141 JustBuiltUnit = ((UnitClass*)pending)->Class->Type; 3142 IsBuiltSomething = true; 3143 break; 3144 3145 case RTTI_VESSEL: 3146 JustBuiltVessel = ((VesselClass*)pending)->Class->Type; 3147 IsBuiltSomething = true; 3148 break; 3149 3150 case RTTI_INFANTRY: 3151 JustBuiltInfantry = ((InfantryClass*)pending)->Class->Type; 3152 IsBuiltSomething = true; 3153 break; 3154 3155 case RTTI_BUILDING: 3156 JustBuiltStructure = ((BuildingClass*)pending)->Class->Type; 3157 IsBuiltSomething = true; 3158 break; 3159 3160 case RTTI_AIRCRAFT: 3161 JustBuiltAircraft = ((AircraftClass*)pending)->Class->Type; 3162 IsBuiltSomething = true; 3163 break; 3164 } 3165 } else { 3166 /* 3167 ** The object could not leave under it's own power. Just wait 3168 ** until the player tries to place the object again. 3169 */ 3170 3171 /* 3172 ** Flag that it's blocked so we can re-try the exit later. 3173 ** This would have been a bad idea under the old peer-peer code since it would have pumped events into 3174 ** the queue too often. ST - 2/25/2020 11:56AM 3175 */ 3176 factory->Set_Is_Blocked(true); 3177 return(false); 3178 } 3179 } 3180 3181 } else { 3182 if (tech) { 3183 TechnoClass * builder = tech->Who_Can_Build_Me(false, false); 3184 if (builder) { 3185 3186 builder->Transmit_Message(RADIO_HELLO, tech); 3187 if (tech->Unlimbo(Cell_Coord(cell))) { 3188 factory->Completed(); 3189 Abandon_Production(type); 3190 3191 if (PlayerPtr == this) { 3192 Sound_Effect(VOC_PLACE_BUILDING_DOWN); 3193 Map.Set_Cursor_Shape(0); 3194 Map.PendingObjectPtr = 0; 3195 Map.PendingObject = 0; 3196 Map.PendingHouse = HOUSE_NONE; 3197 } 3198 return(true); 3199 } else { 3200 if (this == PlayerPtr) { 3201 Speak(VOX_DEPLOY); 3202 } 3203 } 3204 builder->Transmit_Message(RADIO_OVER_OUT); 3205 } 3206 return(false); 3207 3208 } else { 3209 3210 // Play a bad sound here? 3211 return(false); 3212 } 3213 } 3214 } 3215 3216 return(true); 3217 } 3218 3219 3220 /*********************************************************************************************** 3221 * HouseClass::Manual_Place -- Inform display system of building placement mode. * 3222 * * 3223 * This routine will inform the display system that building placement mode has begun. * 3224 * The cursor will be created that matches the layout of the building shape. * 3225 * * 3226 * INPUT: builder -- The factory that is building this object. * 3227 * * 3228 * object -- The building that is going to be placed down on the map. * 3229 * * 3230 * OUTPUT: Was the building placement mode successfully initiated? * 3231 * * 3232 * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games * 3233 * is affected. * 3234 * * 3235 * HISTORY: * 3236 * 05/04/1995 JLB : Created. * 3237 * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. * 3238 *=============================================================================================*/ 3239 bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object) 3240 { 3241 assert(Houses.ID(this) == ID); 3242 3243 if (this == PlayerPtr && !Map.PendingObject && builder && object) { 3244 /* 3245 ** Ensures that object selection doesn't remain when 3246 ** building placement takes place. 3247 */ 3248 Unselect_All(); 3249 3250 Map.Repair_Mode_Control(0); 3251 Map.Sell_Mode_Control(0); 3252 3253 Map.PendingObject = object->Class; 3254 Map.PendingObjectPtr = object; 3255 Map.PendingHouse = Class->House; 3256 3257 Map.Set_Cursor_Shape(object->Occupy_List(true)); 3258 Map.Set_Cursor_Pos(Coord_Cell(builder->Coord)); 3259 builder->Mark(MARK_CHANGE); 3260 return(true); 3261 } 3262 return(false); 3263 } 3264 3265 3266 /*************************************************************************** 3267 * HouseClass::Clobber_All -- removes all objects for this house * 3268 * * 3269 * INPUT: * 3270 * none. * 3271 * * 3272 * OUTPUT: * 3273 * none. * 3274 * * 3275 * WARNINGS: * 3276 * This routine removes the house itself, so the multiplayer code * 3277 * must not rely on there being "empty" houses lying around. * 3278 * * 3279 * HISTORY: * 3280 * 05/16/1995 BRR : Created. * 3281 * 06/09/1995 JLB : Handles aircraft. * 3282 *=========================================================================*/ 3283 void HouseClass::Clobber_All(void) 3284 { 3285 assert(Houses.ID(this) == ID); 3286 3287 int i; 3288 3289 for (i = 0; i < ::Aircraft.Count(); i++) { 3290 if (::Aircraft.Ptr(i)->House == this) { 3291 delete ::Aircraft.Ptr(i); 3292 i--; 3293 } 3294 } 3295 for (i = 0; i < ::Units.Count(); i++) { 3296 if (::Units.Ptr(i)->House == this) { 3297 delete ::Units.Ptr(i); 3298 i--; 3299 } 3300 } 3301 for (i = 0; i < ::Vessels.Count(); i++) { 3302 if (::Vessels.Ptr(i)->House == this) { 3303 delete ::Vessels.Ptr(i); 3304 i--; 3305 } 3306 } 3307 for (i = 0; i < Infantry.Count(); i++) { 3308 if (Infantry.Ptr(i)->House == this) { 3309 delete Infantry.Ptr(i); 3310 i--; 3311 } 3312 } 3313 for (i = 0; i < Buildings.Count(); i++) { 3314 if (Buildings.Ptr(i)->House == this) { 3315 delete Buildings.Ptr(i); 3316 i--; 3317 } 3318 } 3319 for (i = 0; i < TeamTypes.Count(); i++) { 3320 if (TeamTypes.Ptr(i)->House == Class->House) { 3321 delete TeamTypes.Ptr(i); 3322 i--; 3323 } 3324 } 3325 for (i = 0; i < Triggers.Count(); i++) { 3326 if (Triggers.Ptr(i)->Class->House == Class->House) { 3327 delete Triggers.Ptr(i); 3328 i--; 3329 } 3330 } 3331 for (i = 0; i < TriggerTypes.Count(); i++) { 3332 if (TriggerTypes.Ptr(i)->House == Class->House) { 3333 delete TriggerTypes.Ptr(i); 3334 i--; 3335 } 3336 } 3337 3338 delete this; 3339 } 3340 3341 3342 /*********************************************************************************************** 3343 * HouseClass::Detach -- Removes specified object from house tracking systems. * 3344 * * 3345 * This routine is called when an object is to be removed from the game system. If the * 3346 * specified object is part of the house tracking system, then it will be removed. * 3347 * * 3348 * INPUT: target -- The target value of the object that is to be removed from the game. * 3349 * * 3350 * all -- Is the target going away for good as opposed to just cloaking/hiding? * 3351 * * 3352 * OUTPUT: none * 3353 * * 3354 * WARNINGS: none * 3355 * * 3356 * HISTORY: * 3357 * 05/18/1995 JLB : commented * 3358 *=============================================================================================*/ 3359 void HouseClass::Detach(TARGET target, bool ) 3360 { 3361 assert(Houses.ID(this) == ID); 3362 3363 if (ToCapture == target) { 3364 ToCapture = TARGET_NONE; 3365 } 3366 3367 if (Is_Target_Trigger(target)) { 3368 HouseTriggers[ID].Delete(As_Trigger(target)); 3369 } 3370 } 3371 3372 3373 /*********************************************************************************************** 3374 * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * 3375 * * 3376 * This routine will examine the enemy houses and if there is a building owned by one * 3377 * of those house, true will be returned. * 3378 * * 3379 * INPUT: btype -- The building type to check for. * 3380 * * 3381 * OUTPUT: Does a building of the specified type exist for one of the enemy houses? * 3382 * * 3383 * WARNINGS: none * 3384 * * 3385 * HISTORY: * 3386 * 05/23/1995 JLB : Created. * 3387 *=============================================================================================*/ 3388 bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const 3389 { 3390 assert(Houses.ID(this) == ID); 3391 3392 int bflag = 1L << btype; 3393 for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { 3394 HouseClass * house = HouseClass::As_Pointer(index); 3395 3396 if (house && !Is_Ally(house) && (house->ActiveBScan & bflag) != 0) { 3397 return(true); 3398 } 3399 } 3400 return(false); 3401 } 3402 3403 3404 /*********************************************************************************************** 3405 * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * 3406 * * 3407 * This routine will examine the house status and return with a techno type pointer to * 3408 * the object type that it thinks should be created. The type is restricted to match the * 3409 * type specified. Typical use of this routine is by computer controlled factories. * 3410 * * 3411 * INPUT: objecttype -- The type of object to restrict the scan for. * 3412 * * 3413 * kennel -- Is this from a kennel? There are special hacks to ensure that only * 3414 * dogs can be produced from a kennel. * 3415 * * 3416 * OUTPUT: Returns with a pointer to a techno type for the object type that should be * 3417 * created. If no object should be created, then NULL is returned. * 3418 * * 3419 * WARNINGS: This is a time consuming routine. Only call when necessary. * 3420 * * 3421 * HISTORY: * 3422 * 05/23/1995 JLB : Created. * 3423 *=============================================================================================*/ 3424 TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype, bool kennel) const 3425 { 3426 assert(Houses.ID(this) == ID); 3427 3428 TechnoTypeClass const * techno = NULL; 3429 3430 switch (objecttype) { 3431 case RTTI_AIRCRAFT: 3432 case RTTI_AIRCRAFTTYPE: 3433 if (BuildAircraft != AIRCRAFT_NONE) { 3434 return(&AircraftTypeClass::As_Reference(BuildAircraft)); 3435 } 3436 return(NULL); 3437 3438 case RTTI_VESSEL: 3439 case RTTI_VESSELTYPE: 3440 if (BuildVessel != VESSEL_NONE) { 3441 return(&VesselTypeClass::As_Reference(BuildVessel)); 3442 } 3443 return(NULL); 3444 3445 /* 3446 ** Unit construction is based on the rule that up to twice the number required 3447 ** to fill all teams will be created. 3448 */ 3449 case RTTI_UNIT: 3450 case RTTI_UNITTYPE: 3451 if (BuildUnit != UNIT_NONE) { 3452 return(&UnitTypeClass::As_Reference(BuildUnit)); 3453 } 3454 return(NULL); 3455 3456 /* 3457 ** Infantry construction is based on the rule that up to twice the number required 3458 ** to fill all teams will be created. 3459 */ 3460 case RTTI_INFANTRY: 3461 case RTTI_INFANTRYTYPE: 3462 if (BuildInfantry != INFANTRY_NONE) { 3463 if (kennel && BuildInfantry != INFANTRY_DOG) return(NULL); 3464 if (!kennel && BuildInfantry == INFANTRY_DOG) return(NULL); 3465 return(&InfantryTypeClass::As_Reference(BuildInfantry)); 3466 } 3467 return(NULL); 3468 3469 /* 3470 ** Building construction is based upon the preconstruction list. 3471 */ 3472 case RTTI_BUILDING: 3473 case RTTI_BUILDINGTYPE: 3474 if (BuildStructure != STRUCT_NONE) { 3475 return(&BuildingTypeClass::As_Reference(BuildStructure)); 3476 } 3477 return(NULL); 3478 } 3479 return(techno); 3480 } 3481 3482 3483 /*********************************************************************************************** 3484 * HouseClass::Flag_Remove -- Removes the flag from the specified target. * 3485 * * 3486 * This routine will remove the flag attached to the specified target object or cell. * 3487 * Call this routine before placing the object down. This is called inherently by the * 3488 * the Flag_Attach() functions. * 3489 * * 3490 * INPUT: target -- The target that the flag was attached to but will be removed from. * 3491 * * 3492 * set_home -- if true, clears the flag's waypoint designation * 3493 * * 3494 * OUTPUT: Was the flag successfully removed from the specified target? * 3495 * * 3496 * WARNINGS: none * 3497 * * 3498 * HISTORY: * 3499 * 05/23/1995 JLB : Created. * 3500 *=============================================================================================*/ 3501 bool HouseClass::Flag_Remove(TARGET target, bool set_home) 3502 { 3503 assert(Houses.ID(this) == ID); 3504 3505 bool rc = false; 3506 3507 if (Target_Legal(target)) { 3508 3509 /* 3510 ** Remove the flag from a unit 3511 */ 3512 UnitClass * object = As_Unit(target); 3513 if (object) { 3514 rc = object->Flag_Remove(); 3515 if (rc && FlagLocation == target) { 3516 FlagLocation = TARGET_NONE; 3517 } 3518 3519 } else { 3520 3521 /* 3522 ** Remove the flag from a cell 3523 */ 3524 CELL cell = As_Cell(target); 3525 if (Map.In_Radar(cell)) { 3526 rc = Map[cell].Flag_Remove(); 3527 if (rc && FlagLocation == target) { 3528 FlagLocation = TARGET_NONE; 3529 } 3530 } 3531 } 3532 3533 /* 3534 ** Handle the flag home cell: 3535 ** If 'set_home' is set, clear the home value & the cell's overlay 3536 */ 3537 if (set_home) { 3538 if (FlagHome != 0) { 3539 Map[FlagHome].Overlay = OVERLAY_NONE; 3540 Map.Flag_Cell(FlagHome); 3541 FlagHome = 0; 3542 } 3543 } 3544 } 3545 return(rc); 3546 } 3547 3548 3549 /*********************************************************************************************** 3550 * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * 3551 * * 3552 * This routine will attach the house flag to the location specified. If the location * 3553 * cannot contain the flag, then a suitable nearby location will be selected. * 3554 * * 3555 * INPUT: cell -- The desired cell location to place the flag. * 3556 * * 3557 * set_home -- if true, resets the flag's waypoint designation * 3558 * * 3559 * OUTPUT: Was the flag successfully placed? * 3560 * * 3561 * WARNINGS: The cell picked for the flag might very likely not be the cell requested. * 3562 * Check the FlagLocation value to determine the final cell resting spot. * 3563 * * 3564 * HISTORY: * 3565 * 05/23/1995 JLB : Created. * 3566 * 10/08/1996 JLB : Uses map nearby cell scanning handler. * 3567 *=============================================================================================*/ 3568 bool HouseClass::Flag_Attach(CELL cell, bool set_home) 3569 { 3570 assert(Houses.ID(this) == ID); 3571 3572 bool rc; 3573 bool clockwise; 3574 3575 /* 3576 ** Randomly decide if we're going to search cells clockwise or counter- 3577 ** clockwise 3578 */ 3579 clockwise = Percent_Chance(50); 3580 3581 /* 3582 ** Only continue if this cell is a legal placement cell. 3583 */ 3584 if (Map.In_Radar(cell)) { 3585 3586 /* 3587 ** If the flag already exists, then it must be removed from the object 3588 ** it is attached to. 3589 */ 3590 Flag_Remove(FlagLocation, set_home); 3591 3592 /* 3593 ** Attach the flag to the cell specified. If it can't be placed, then pick 3594 ** a nearby cell where it can be placed. 3595 */ 3596 CELL newcell = cell; 3597 rc = Map[newcell].Flag_Place(Class->House); 3598 if (!rc) { 3599 newcell = Map.Nearby_Location(cell, SPEED_TRACK, -1, MZONE_NORMAL, true); 3600 if (newcell != 0) { 3601 rc = Map[newcell].Flag_Place(Class->House); 3602 } 3603 3604 #ifdef OBSOLETE 3605 /* 3606 ** Loop for increasing distance from the desired cell. 3607 ** For each distance, randomly pick a starting direction. Between 3608 ** this and the clockwise/counterclockwise random value, the flag 3609 ** should appear to be placed fairly randomly. 3610 */ 3611 for (int dist = 1; dist < 32; dist++) { 3612 FacingType fcounter; 3613 FacingType rot; 3614 3615 /* 3616 ** Clockwise search. 3617 */ 3618 if (clockwise) { 3619 rot = Random_Pick(FACING_N, FACING_NW); 3620 for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { 3621 newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); 3622 if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { 3623 dist = 32; 3624 rc = true; 3625 break; 3626 } 3627 rot++; 3628 if (rot > FACING_NW) rot = FACING_N; 3629 } 3630 } else { 3631 3632 /* 3633 ** Counter-clockwise search 3634 */ 3635 rot = Random_Pick(FACING_N, FACING_NW); 3636 for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) { 3637 newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); 3638 if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { 3639 dist = 32; 3640 rc = true; 3641 break; 3642 } 3643 rot--; 3644 if (rot < FACING_N) 3645 rot = FACING_NW; 3646 } 3647 } 3648 } 3649 #endif 3650 } 3651 3652 /* 3653 ** If we've found a spot for the flag, place the flag at the new cell. 3654 ** if 'set_home' is set, OR this house has no current flag home cell, 3655 ** mark that cell as this house's flag home cell. Otherwise fall back 3656 ** on returning the flag to its home. 3657 */ 3658 if (rc) { 3659 FlagLocation = As_Target(newcell); 3660 3661 if (set_home || FlagHome == 0) { 3662 Map[newcell].Overlay = OVERLAY_FLAG_SPOT; 3663 Map[newcell].OverlayData = 0; 3664 Map[newcell].Recalc_Attributes(); 3665 FlagHome = newcell; 3666 } 3667 } 3668 else if (FlagHome != 0) { 3669 rc = Map[FlagHome].Flag_Place(Class->House); 3670 } 3671 3672 return(rc); 3673 } 3674 return(false); 3675 } 3676 3677 3678 /*********************************************************************************************** 3679 * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * 3680 * * 3681 * This routine will attach the house flag to the specified unit. This routine is called * 3682 * when a unit drives over a cell containing a flag. * 3683 * * 3684 * INPUT: object -- Pointer to the object that the house flag is to be attached to. * 3685 * * 3686 * set_home -- if true, clears the flag's waypoint designation * 3687 * * 3688 * OUTPUT: Was the flag attached successfully? * 3689 * * 3690 * WARNINGS: none * 3691 * * 3692 * HISTORY: * 3693 * 05/23/1995 JLB : Created. * 3694 *=============================================================================================*/ 3695 bool HouseClass::Flag_Attach(UnitClass * object, bool set_home) 3696 { 3697 assert(Houses.ID(this) == ID); 3698 3699 if (object && !object->IsInLimbo) { 3700 Flag_Remove(FlagLocation, set_home); 3701 3702 /* 3703 ** Attach the flag to the object. 3704 */ 3705 object->Flag_Attach(Class->House); 3706 FlagLocation = object->As_Target(); 3707 return(true); 3708 } 3709 return(false); 3710 } 3711 3712 extern void On_Defeated_Message(const char* message, float timeout_seconds); 3713 3714 /*************************************************************************** 3715 * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * 3716 * * 3717 * INPUT: * 3718 * none. * 3719 * * 3720 * OUTPUT: * 3721 * none. * 3722 * * 3723 * WARNINGS: * 3724 * none. * 3725 * * 3726 * HISTORY: * 3727 * 05/25/1995 BRR : Created. * 3728 *=========================================================================*/ 3729 void HouseClass::MPlayer_Defeated(void) 3730 { 3731 assert(Houses.ID(this) == ID); 3732 3733 char txt[80]; 3734 int i,j; 3735 unsigned char id; 3736 HouseClass * hptr; 3737 HouseClass * hptr2; 3738 int num_alive; 3739 int num_humans; 3740 int all_allies; 3741 3742 /* 3743 ** Set the defeat flag for this house 3744 */ 3745 IsDefeated = true; 3746 3747 /* 3748 ** If this is a computer controlled house, then all computer controlled 3749 ** houses become paranoid. 3750 */ 3751 if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) { 3752 Computer_Paranoid(); 3753 } 3754 3755 /* 3756 ** Remove this house's flag & flag home cell 3757 */ 3758 if (Special.IsCaptureTheFlag) { 3759 if (FlagLocation) { 3760 Flag_Remove(FlagLocation, true); 3761 } else { 3762 if (FlagHome != 0) { 3763 Flag_Remove(FlagHome, true); 3764 } 3765 } 3766 } 3767 3768 /* 3769 ** Remove any one-time superweapons the player might have. 3770 */ 3771 for (i = SPC_FIRST; i < SPC_COUNT; i++) { 3772 SuperWeapon[i].Remove(true); 3773 } 3774 3775 /* 3776 ** If this is me: 3777 ** - Set MPlayerObiWan, so I can only send messages to all players, and 3778 ** not just one (so I can't be obnoxiously omnipotent) 3779 ** - Reveal the map 3780 ** - Add my defeat message 3781 */ 3782 if (PlayerPtr == this) { 3783 Session.ObiWan = 1; 3784 HidPage.Clear(); 3785 Map.Flag_To_Redraw(true); 3786 3787 /* 3788 ** Pop up a message showing that I was defeated 3789 */ 3790 sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), IniName); 3791 if (Session.Type == GAME_NORMAL) { 3792 Session.Messages.Add_Message(NULL, 0, txt, Session.ColorIdx, 3793 TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 3794 } 3795 Map.Flag_To_Redraw(false); 3796 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 3797 int timeout = Rule.MessageDelay * TICKS_PER_MINUTE; 3798 On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); 3799 Sound_Effect(VOC_INCOMING_MESSAGE); 3800 } 3801 3802 } else { 3803 3804 /* 3805 ** If it wasn't me, find out who was defeated 3806 */ 3807 if (IsHuman) { 3808 sprintf (txt, Text_String(TXT_PLAYER_DEFEATED), IniName); 3809 3810 //Session.Messages.Add_Message(NULL, 0, txt, RemapColor, 3811 // TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 3812 Map.Flag_To_Redraw(false); 3813 RedrawOptionsMenu = true; 3814 3815 int timeout = Rule.MessageDelay * TICKS_PER_MINUTE; 3816 On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); 3817 Sound_Effect(VOC_INCOMING_MESSAGE); 3818 } 3819 } 3820 3821 /* 3822 ** Find out how many players are left alive. 3823 */ 3824 num_alive = 0; 3825 num_humans = 0; 3826 for (i = 0; i < Session.MaxPlayers; i++) { 3827 hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); 3828 if (hptr && !hptr->IsDefeated) { 3829 if (hptr->IsHuman) { 3830 num_humans++; 3831 } 3832 num_alive++; 3833 } 3834 } 3835 3836 /* 3837 ** If all the houses left alive are allied with each other, then in reality 3838 ** there's only one player left: 3839 */ 3840 all_allies = 1; 3841 for (i = 0; i < Session.MaxPlayers; i++) { 3842 3843 /* 3844 ** Get a pointer to this house 3845 */ 3846 hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); 3847 if (!hptr || hptr->IsDefeated) 3848 continue; 3849 3850 /* 3851 ** Loop through all houses; if there's one left alive that this house 3852 ** isn't allied with, then all_allies will be false 3853 */ 3854 for (j = 0; j < Session.MaxPlayers; j++) { 3855 hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j)); 3856 if (!hptr2) { 3857 continue; 3858 } 3859 3860 if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) { 3861 all_allies = 0; 3862 break; 3863 } 3864 } 3865 if (!all_allies) { 3866 break; 3867 } 3868 } 3869 3870 /* 3871 ** If all houses left are allies, set 'num_alive' to 1; game over. 3872 */ 3873 if (all_allies) { 3874 num_alive = 1; 3875 } 3876 3877 /* 3878 ** If there's only one human player left or no humans left, the game is over: 3879 ** - Determine whether this player wins or loses, based on the state of the 3880 ** player's IsDefeated flag 3881 ** - Find all players' indices in the Session.Score array 3882 ** - Tally up scores for this game 3883 */ 3884 if (num_alive == 1 || num_humans == 0) { 3885 if (PlayerPtr->IsDefeated) { 3886 PlayerLoses = true; 3887 } else { 3888 PlayerWins = true; 3889 } 3890 3891 /* 3892 ** Add up the scores 3893 */ 3894 Tally_Score(); 3895 3896 /* 3897 ** Destroy all the IPX connections, since we have to go through the rest 3898 ** of the Main_Loop() before we detect that the game is over, and we'll 3899 ** end up waiting for frame sync packets from the other machines. 3900 */ 3901 if (Session.Type==GAME_IPX || Session.Type == GAME_INTERNET) { 3902 i = 0; 3903 while (Ipx.Num_Connections() && (i++ < 1000) ) { 3904 id = Ipx.Connection_ID(0); 3905 Ipx.Delete_Connection(id); 3906 } 3907 Session.NumPlayers = 0; 3908 } 3909 3910 #if(TEN) 3911 // 3912 // Tell the TEN server who won 3913 // 3914 if (Session.Type == GAME_TEN) { 3915 Send_TEN_Win_Packet(); 3916 } 3917 #endif // TEN 3918 3919 #if(MPATH) 3920 // 3921 // Tell the MPATH server who won 3922 // 3923 if (Session.Type == GAME_MPATH) { 3924 FILE *fp; 3925 3926 fp = fopen("winner.txt","wt"); 3927 if (fp) { 3928 for (i = 0; i < Session.Players.Count(); i++) { 3929 hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); 3930 if (!hptr->IsDefeated) { 3931 fprintf(fp,"%s\n",hptr->IniName); 3932 } 3933 } 3934 fclose(fp); 3935 } 3936 } 3937 #endif // MPATH 3938 3939 } 3940 } 3941 3942 3943 /*************************************************************************** 3944 * HouseClass::Tally_Score -- Fills in the score system for this round * 3945 * * 3946 * INPUT: * 3947 * none. * 3948 * * 3949 * OUTPUT: * 3950 * none. * 3951 * * 3952 * WARNINGS: * 3953 * none. * 3954 * * 3955 * HISTORY: * 3956 * 11/29/1995 BRR : Created. * 3957 *=========================================================================*/ 3958 void HouseClass::Tally_Score(void) 3959 { 3960 HousesType house; 3961 HousesType house2; 3962 HouseClass * hptr; 3963 int score_index; 3964 int i,j,k; 3965 int max_index; 3966 int max_count; 3967 int count; 3968 3969 /* 3970 ** Loop through all houses, tallying up each player's score 3971 */ 3972 for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 3973 hptr = HouseClass::As_Pointer(house); 3974 /* 3975 ** Skip this house if it's not human. 3976 */ 3977 if (!hptr || !hptr->IsHuman) { 3978 continue; 3979 } 3980 /* 3981 ** Now find out where this player is in the score array 3982 */ 3983 score_index = -1; 3984 for (i = 0; i < Session.NumScores; i++) { 3985 if (!stricmp(hptr->IniName, Session.Score[i].Name)) { 3986 score_index = i; 3987 break; 3988 } 3989 } 3990 3991 /* 3992 ** If the index is still -1, the name wasn't found; add a new entry. 3993 */ 3994 if (score_index == -1) { 3995 /* 3996 ** Just add this player to the end of the array, if there's room 3997 */ 3998 if (Session.NumScores < MAX_MULTI_NAMES) { 3999 score_index = Session.NumScores; 4000 Session.NumScores++; 4001 } 4002 /* 4003 ** If there's not room, we have to remove somebody. 4004 ** For each player in the scores array, count the # of '-1' entries 4005 ** from this game backwards; the one with the most is the one that 4006 ** hasn't played the longest; replace him with this new guy. 4007 */ 4008 else { 4009 max_index = 0; 4010 max_count = 0; 4011 for (j = 0; j < Session.NumScores; j++) { 4012 count = 0; 4013 for (k = Session.NumScores - 1; k >= 0; k--) { 4014 if (Session.Score[j].Kills[k]==-1) { 4015 count++; 4016 } 4017 else { 4018 break; 4019 } 4020 } 4021 if (count > max_count) { 4022 max_count = count; 4023 max_index = j; 4024 } 4025 } 4026 score_index = max_index; 4027 } 4028 4029 /* 4030 ** Initialize this new score entry 4031 */ 4032 Session.Score[score_index].Wins = 0; 4033 strcpy (Session.Score[score_index].Name, hptr->IniName); 4034 for (j = 0; j < MAX_MULTI_GAMES; j++) 4035 Session.Score[score_index].Kills[j] = -1; 4036 } 4037 4038 /* 4039 ** Init this player's Kills to 0 (-1 means he didn't play this round; 4040 ** 0 means he played but got no kills). 4041 */ 4042 Session.Score[score_index].Kills[Session.CurGame] = 0; 4043 4044 /* 4045 ** Init this player's color to his last-used color index 4046 */ 4047 Session.Score[score_index].Color = hptr->RemapColor; 4048 4049 /* 4050 ** If this house was undefeated, it must have been the winner. 4051 ** (If no human houses are undefeated, the computer won.) 4052 */ 4053 if (!hptr->IsDefeated) { 4054 Session.Score[score_index].Wins++; 4055 Session.Winner = score_index; 4056 } 4057 4058 /* 4059 ** Tally up all kills for this player 4060 */ 4061 for (house2 = HOUSE_FIRST; house2 < HOUSE_COUNT; house2++) { 4062 Session.Score[score_index].Kills[Session.CurGame] += hptr->UnitsKilled[house2]; 4063 Session.Score[score_index].Kills[Session.CurGame] += hptr->BuildingsKilled[house2]; 4064 } 4065 } 4066 } 4067 4068 4069 /*************************************************************************** 4070 * HouseClass::Blowup_All -- blows up everything * 4071 * * 4072 * INPUT: * 4073 * none. * 4074 * * 4075 * OUTPUT: * 4076 * none. * 4077 * * 4078 * WARNINGS: * 4079 * none. * 4080 * * 4081 * HISTORY: * 4082 * 05/16/1995 BRR : Created. * 4083 * 06/09/1995 JLB : Handles aircraft. * 4084 * 05/07/1996 JLB : Handles ships. * 4085 *=========================================================================*/ 4086 void HouseClass::Blowup_All(void) 4087 { 4088 assert(Houses.ID(this) == ID); 4089 4090 int i; 4091 int damage; 4092 UnitClass * uptr; 4093 InfantryClass * iptr; 4094 BuildingClass * bptr; 4095 int count; 4096 WarheadType warhead; 4097 4098 /* 4099 ** Find everything owned by this house & blast it with a huge amount of damage 4100 ** at zero range. Do units before infantry, so the units' drivers are killed 4101 ** too. Using Explosion_Damage is like dropping a big bomb right on the 4102 ** object; it will also damage anything around it. 4103 */ 4104 for (i = 0; i < ::Units.Count(); i++) { 4105 if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) { 4106 uptr = ::Units.Ptr(i); 4107 4108 /* 4109 ** Some units can't be killed with one shot, so keep damaging them until 4110 ** they're gone. The unit will destroy itself, and put an infantry in 4111 ** its place. When the unit destroys itself, decrement 'i' since 4112 ** its pointer will be removed from the active pointer list. 4113 */ 4114 count = 0; 4115 while (::Units.Ptr(i)==uptr && uptr->Strength) { 4116 4117 // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840 4118 // Likely due to damage biasing based on RA factions and/or difficulty settings 4119 // Applying this to units (vehicles), ships, buildings, and infantry, too 4120 // 4121 // damage = uptr->Strength; // Original 4122 damage = 0x7fff; // Copied from TD 4123 4124 uptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); 4125 count++; 4126 if (count > 5 && uptr->IsActive) { 4127 delete uptr; 4128 break; 4129 } 4130 } 4131 i--; 4132 } 4133 } 4134 4135 /* 4136 ** Destroy all aircraft owned by this house. 4137 */ 4138 for (i = 0; i < ::Aircraft.Count(); i++) { 4139 if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) { 4140 AircraftClass * aptr = ::Aircraft.Ptr(i); 4141 4142 // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840 4143 // Likely due to damage biasing based on RA factions and/or difficulty settings 4144 // Applying this to units (vehicles), ships, buildings, and infantry, too 4145 // 4146 // damage = aptr->Strength; // Original 4147 damage = 0x7fff; // Copied from TD 4148 4149 aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); 4150 if (!aptr->IsActive) { 4151 i--; 4152 } 4153 } 4154 } 4155 4156 /* 4157 ** Destroy all vessels owned by this house. 4158 */ 4159 for (i = 0; i < ::Vessels.Count(); i++) { 4160 if (::Vessels.Ptr(i)->House == this && !::Vessels.Ptr(i)->IsInLimbo) { 4161 VesselClass * vptr = ::Vessels.Ptr(i); 4162 4163 // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840 4164 // Likely due to damage biasing based on RA factions and/or difficulty settings 4165 // Applying this to units (vehicles), ships, buildings, and infantry, too 4166 // 4167 // damage = vptr->Strength; // Original 4168 damage = 0x7fff; // Copied from TD 4169 4170 vptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); 4171 if (!vptr->IsActive) { 4172 i--; 4173 } 4174 } 4175 } 4176 4177 /* 4178 ** Buildings don't delete themselves when they die; they shake the screen 4179 ** and begin a countdown, so don't decrement 'i' when it's destroyed. 4180 */ 4181 for (i = 0; i < Buildings.Count(); i++) { 4182 if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) { 4183 bptr = Buildings.Ptr(i); 4184 4185 count = 0; 4186 while (Buildings.Ptr(i)==bptr && bptr->Strength) { 4187 4188 // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840 4189 // Likely due to damage biasing based on RA factions and/or difficulty settings 4190 // Applying this to units (vehicles), ships, buildings, and infantry, too 4191 // 4192 // damage = bptr->Strength; // Original 4193 damage = 0x7fff; // Copied from TD 4194 4195 bptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); 4196 count++; 4197 if (count > 5) { 4198 delete bptr; 4199 break; 4200 } 4201 } 4202 } 4203 } 4204 4205 /* 4206 ** Infantry don't delete themselves when they die; they go into a death- 4207 ** animation sequence, so there's no need to decrement 'i' when they die. 4208 ** Infantry should die by different types of warheads, so their death 4209 ** anims aren't all synchronized. 4210 */ 4211 for (i = 0; i < Infantry.Count(); i++) { 4212 if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) { 4213 iptr = Infantry.Ptr(i); 4214 4215 count = 0; 4216 while (Infantry.Ptr(i)==iptr && iptr->Strength) { 4217 4218 // MBL 06.22.2020 RA: Not all aircraft die in this case; See https://jaas.ea.com/browse/TDRA-6840 4219 // Likely due to damage biasing based on RA factions and/or difficulty settings 4220 // Applying this to units (vehicles), ships, buildings, and infantry, too 4221 // 4222 // damage = iptr->Strength; // Original 4223 damage = 0x7fff; // Copied from TD 4224 4225 warhead = Random_Pick(WARHEAD_SA, WARHEAD_FIRE); 4226 iptr->Take_Damage(damage, 0, warhead, NULL, true); 4227 4228 count++; 4229 if (count > 5) { 4230 delete iptr; 4231 break; 4232 } 4233 } 4234 } 4235 } 4236 } 4237 4238 4239 /*********************************************************************************************** 4240 * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * 4241 * * 4242 * When this routine is called, the house will blow up after a period of time. Typically * 4243 * this is called when the flag is captured or the HQ destroyed. * 4244 * * 4245 * INPUT: none * 4246 * * 4247 * OUTPUT: Was the house flagged to blow up? * 4248 * * 4249 * WARNINGS: none * 4250 * * 4251 * HISTORY: * 4252 * 06/20/1995 JLB : Created. * 4253 *=============================================================================================*/ 4254 bool HouseClass::Flag_To_Die(void) 4255 { 4256 assert(Houses.ID(this) == ID); 4257 4258 if (!IsToWin && !IsToDie && !IsToLose) { 4259 IsToDie = true; 4260 BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; 4261 } 4262 return(IsToDie); 4263 } 4264 4265 4266 /*********************************************************************************************** 4267 * HouseClass::Flag_To_Win -- Flags the house to win soon. * 4268 * * 4269 * When this routine is called, the house will be declared the winner after a period of * 4270 * time. * 4271 * * 4272 * INPUT: none * 4273 * * 4274 * OUTPUT: Was the house flagged to win? * 4275 * * 4276 * WARNINGS: none * 4277 * * 4278 * HISTORY: * 4279 * 06/20/1995 JLB : Created. * 4280 *=============================================================================================*/ 4281 bool HouseClass::Flag_To_Win(void) 4282 { 4283 assert(Houses.ID(this) == ID); 4284 4285 if (!IsToWin && !IsToDie && !IsToLose) { 4286 IsToWin = true; 4287 BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; 4288 } 4289 return(IsToWin); 4290 } 4291 4292 4293 /*********************************************************************************************** 4294 * HouseClass::Flag_To_Lose -- Flags the house to die soon. * 4295 * * 4296 * When this routine is called, it will spell the doom of this house. In a short while * 4297 * all of the object owned by this house will explode. Typical use of this routine is when * 4298 * the flag has been captured or the command vehicle has been destroyed. * 4299 * * 4300 * INPUT: none * 4301 * * 4302 * OUTPUT: Has the doom been initiated? * 4303 * * 4304 * WARNINGS: none * 4305 * * 4306 * HISTORY: * 4307 * 06/12/1995 JLB : Created. * 4308 *=============================================================================================*/ 4309 bool HouseClass::Flag_To_Lose(void) 4310 { 4311 assert(Houses.ID(this) == ID); 4312 4313 IsToWin = false; 4314 if (!IsToDie && !IsToLose) { 4315 IsToLose = true; 4316 BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; 4317 } 4318 return(IsToLose); 4319 } 4320 4321 4322 /*********************************************************************************************** 4323 * HouseClass::Init_Data -- Initializes the multiplayer color data. * 4324 * * 4325 * This routine is called when initializing the color and remap data for this house. The * 4326 * primary user of this routine is the multiplayer version of the game, especially for * 4327 * saving & loading multiplayer games. * 4328 * * 4329 * INPUT: color -- The color of this house. * 4330 * * 4331 * house -- The house that this should act like. * 4332 * * 4333 * credits -- The initial credits to assign to this house. * 4334 * * 4335 * OUTPUT: none * 4336 * * 4337 * WARNINGS: none * 4338 * * 4339 * HISTORY: * 4340 * 07/29/1995 JLB : Created. * 4341 *=============================================================================================*/ 4342 extern bool NowSavingGame; // TEMP MBL: Need to discuss better solution with Steve 4343 void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits) 4344 { 4345 assert(Houses.ID(this) == ID); 4346 4347 Credits = Control.InitialCredits = credits; 4348 VisibleCredits.Current = Credits; 4349 RemapColor = color; 4350 ActLike = house; 4351 4352 // MBL 03.20.2020 4353 // Attempt to fix Red Alert credit tick-up bug after saving a game that has had harvesting underway 4354 // Note that this code gets called with both game loads and saves 4355 // When this function is called, sometimes credits value has Tiberium (or HarvestedCredits?) variables applied, and sometimes now 4356 // 4357 if (NowSavingGame == true) 4358 { 4359 // At this point VisibleCredits.Current (set above) does not have harvested ore/tiberium applied, but VisibleCredits.Credits does 4360 VisibleCredits.Current = VisibleCredits.Credits; 4361 } 4362 } 4363 4364 4365 /*********************************************************************************************** 4366 * HouseClass::Power_Fraction -- Fetches the current power output rating. * 4367 * * 4368 * Use this routine to fetch the current power output as a fixed point fraction. The * 4369 * value 0x0100 is 100% power. * 4370 * * 4371 * INPUT: none * 4372 * * 4373 * OUTPUT: Returns with power rating as a fixed pointer number. * 4374 * * 4375 * WARNINGS: none * 4376 * * 4377 * HISTORY: * 4378 * 07/22/1995 JLB : Created. * 4379 *=============================================================================================*/ 4380 fixed HouseClass::Power_Fraction(void) const 4381 { 4382 assert(Houses.ID(this) == ID); 4383 4384 if (Power >= Drain || Drain == 0) return(1); 4385 4386 if (Power) { 4387 return(fixed(Power, Drain)); 4388 } 4389 return(0); 4390 } 4391 4392 4393 /*********************************************************************************************** 4394 * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * 4395 * * 4396 * This routine will try to sell the wall at the specified location. If there is a wall * 4397 * present and it is owned by this house, then it can be sold. * 4398 * * 4399 * INPUT: cell -- The cell that wall selling is desired. * 4400 * * 4401 * OUTPUT: none * 4402 * * 4403 * WARNINGS: none * 4404 * * 4405 * HISTORY: * 4406 * 08/05/1995 JLB : Created. * 4407 * 11/02/1996 JLB : Checks unsellable bit for wall type. * 4408 *=============================================================================================*/ 4409 void HouseClass::Sell_Wall(CELL cell) 4410 { 4411 assert(Houses.ID(this) == ID); 4412 4413 if ((unsigned)cell > 0) { 4414 OverlayType overlay = Map[cell].Overlay; 4415 4416 if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) { 4417 OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay); 4418 4419 if (optr.IsWall) { 4420 BuildingTypeClass const * btype = NULL; 4421 switch (overlay) { 4422 case OVERLAY_SANDBAG_WALL: 4423 btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL); 4424 break; 4425 4426 case OVERLAY_CYCLONE_WALL: 4427 btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL); 4428 break; 4429 4430 case OVERLAY_BRICK_WALL: 4431 btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL); 4432 break; 4433 4434 case OVERLAY_BARBWIRE_WALL: 4435 btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL); 4436 break; 4437 4438 case OVERLAY_WOOD_WALL: 4439 btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL); 4440 break; 4441 4442 case OVERLAY_FENCE: 4443 btype = &BuildingTypeClass::As_Reference(STRUCT_FENCE); 4444 break; 4445 4446 default: 4447 break; 4448 } 4449 if (btype != NULL && !btype->IsUnsellable) { 4450 4451 if (PlayerPtr == this) { 4452 Sound_Effect(VOC_CASHTURN); 4453 } 4454 4455 Refund_Money(btype->Raw_Cost() * Rule.RefundPercent); 4456 Map[cell].Overlay = OVERLAY_NONE; 4457 Map[cell].OverlayData = 0; 4458 Map[cell].Owner = HOUSE_NONE; 4459 Map[cell].Wall_Update(); 4460 CellClass * ncell = Map[cell].Adjacent_Cell(FACING_N); 4461 if (ncell) ncell->Wall_Update(); 4462 CellClass * wcell = Map[cell].Adjacent_Cell(FACING_W); 4463 if (wcell) wcell->Wall_Update(); 4464 CellClass * scell = Map[cell].Adjacent_Cell(FACING_S); 4465 if (scell) scell->Wall_Update(); 4466 CellClass * ecell = Map[cell].Adjacent_Cell(FACING_E); 4467 if (ecell) ecell->Wall_Update(); 4468 Map[cell].Recalc_Attributes(); 4469 Map[cell].Redraw_Objects(); 4470 Map.Radar_Pixel(cell); 4471 Detach_This_From_All(::As_Target(cell), true); 4472 4473 if (optr.IsCrushable) { 4474 Map.Zone_Reset(MZONEF_NORMAL); 4475 } else { 4476 Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); 4477 } 4478 } 4479 } 4480 } 4481 } 4482 } 4483 4484 4485 /*********************************************************************************************** 4486 * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * 4487 * * 4488 * This routine is called when a construction yard needs to know what to build next. It * 4489 * will either examine the prebuilt base list or try to figure out what to build next * 4490 * based on the current game situation. * 4491 * * 4492 * INPUT: none * 4493 * * 4494 * OUTPUT: Returns with a pointer to the building type class to build. * 4495 * * 4496 * WARNINGS: none * 4497 * * 4498 * HISTORY: * 4499 * 09/27/1995 JLB : Created. * 4500 *=============================================================================================*/ 4501 BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const 4502 { 4503 assert(Houses.ID(this) == ID); 4504 4505 if (BuildStructure != STRUCT_NONE) { 4506 return(&BuildingTypeClass::As_Reference(BuildStructure)); 4507 } 4508 return(NULL); 4509 } 4510 4511 4512 /*********************************************************************************************** 4513 * HouseClass::Find_Building -- Finds a building of specified type. * 4514 * * 4515 * This routine is used to find a building of the specified type. This is particularly * 4516 * useful for when some event requires a specific building instance. The nuclear missile * 4517 * launch is a good example. * 4518 * * 4519 * INPUT: type -- The building type to scan for. * 4520 * * 4521 * zone -- The zone that the building must be located in. If no zone specific search * 4522 * is desired, then pass ZONE_NONE. * 4523 * * 4524 * OUTPUT: Returns with a pointer to the building type requested. If there is no building * 4525 * of the type requested, then NULL is returned. * 4526 * * 4527 * WARNINGS: none * 4528 * * 4529 * HISTORY: * 4530 * 09/27/1995 JLB : Created. * 4531 * 10/02/1995 JLB : Allows for zone specifics. * 4532 *=============================================================================================*/ 4533 BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const 4534 { 4535 assert(Houses.ID(this) == ID); 4536 4537 /* 4538 ** Only scan if we KNOW there is at least one building of the type 4539 ** requested. 4540 */ 4541 if (BQuantity[type] > 0) { 4542 4543 /* 4544 ** Search for a suitable launch site for this missile. 4545 */ 4546 for (int index = 0; index < Buildings.Count(); index++) { 4547 BuildingClass * b = Buildings.Ptr(index); 4548 if (b && !b->IsInLimbo && b->House == this && *b == type) { 4549 if (zone == ZONE_NONE || Which_Zone(b) == zone) { 4550 return(b); 4551 } 4552 } 4553 } 4554 } 4555 return(NULL); 4556 } 4557 4558 4559 /*********************************************************************************************** 4560 * HouseClass::Find_Build_Location -- Finds a suitable building location. * 4561 * * 4562 * This routine is used to find a suitable building location for the building specified. * 4563 * The auto base building logic uses this when building the base for the computer. * 4564 * * 4565 * INPUT: building -- Pointer to the building that needs to be placed down. * 4566 * * 4567 * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable * 4568 * locations, then NULL is returned. * 4569 * * 4570 * WARNINGS: none * 4571 * * 4572 * HISTORY: * 4573 * 09/27/1995 JLB : Created. * 4574 *=============================================================================================*/ 4575 COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const 4576 { 4577 assert(Houses.ID(this) == ID); 4578 4579 int zonerating[ZONE_COUNT]; 4580 struct { 4581 int AntiAir; // Average air defense for the base. 4582 int AntiArmor; // Average armor defense for the base. 4583 int AntiInfantry; // Average infantry defense for the base. 4584 } zoneinfo = {0,0,0}; 4585 int antiair = building->Anti_Air(); 4586 int antiarmor = building->Anti_Armor(); 4587 int antiinfantry = building->Anti_Infantry(); 4588 bool adj = true; 4589 4590 /* 4591 ** Never place combat buildings adjacent to each other. This is partly 4592 ** because combat buildings don't have a bib and jamming will occur as well 4593 ** as because spacing defensive buildings out will yield a better 4594 ** defense. 4595 */ 4596 if (antiair || antiarmor || antiinfantry) { 4597 adj = false; 4598 } 4599 4600 /* 4601 ** Determine the average zone strengths for the base. This value is 4602 ** used to determine what zones are considered under or over strength. 4603 */ 4604 ZoneType z; 4605 for (z = ZONE_NORTH; z < ZONE_COUNT; z++) { 4606 zoneinfo.AntiAir += ZoneInfo[z].AirDefense; 4607 zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense; 4608 zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense; 4609 } 4610 zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH; 4611 zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH; 4612 zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH; 4613 4614 /* 4615 ** Give each zone a rating for value. The higher the value the more desirable 4616 ** to place the specified building in that zone. Factor the average value of 4617 ** zone defense such that more weight is given to zones that are very under 4618 ** defended. 4619 */ 4620 memset(&zonerating[0], '\0', sizeof(zonerating)); 4621 for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { 4622 int diff; 4623 4624 diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense; 4625 if (z == ZONE_CORE) diff /= 2; 4626 if (diff > 0) { 4627 zonerating[z] += min(antiair, diff); 4628 } 4629 4630 diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense; 4631 if (z == ZONE_CORE) diff /= 2; 4632 if (diff > 0) { 4633 zonerating[z] += min(antiarmor, diff); 4634 } 4635 4636 diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense; 4637 if (z == ZONE_CORE) diff /= 2; 4638 if (diff > 0) { 4639 zonerating[z] += min(antiinfantry, diff); 4640 } 4641 } 4642 4643 /* 4644 ** Now that each zone has been given a desirability rating, find the zone 4645 ** with the greatest value and try to place the building in that zone. 4646 */ 4647 ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST); 4648 int largest = 0; 4649 for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { 4650 if (zonerating[z] > largest) { 4651 zone = z; 4652 largest = zonerating[z]; 4653 } 4654 } 4655 4656 CELL zcell = Find_Cell_In_Zone(building, zone); 4657 if (zcell) { 4658 return(Cell_Coord(zcell)); 4659 } 4660 4661 /* 4662 ** Could not build in preferred zone, so try building in any zone. 4663 */ 4664 static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST}; 4665 int start = Random_Pick(0, ARRAY_SIZE(_zones)-1); 4666 for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) { 4667 ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)]; 4668 zcell = Find_Cell_In_Zone(building, tryzone); 4669 if (zcell) return(zcell); 4670 } 4671 4672 return(NULL); 4673 } 4674 4675 4676 /*********************************************************************************************** 4677 * HouseClass::Recalc_Center -- Recalculates the center point of the base. * 4678 * * 4679 * This routine will average the location of the base and record the center point. The * 4680 * recorded center point is used to determine such things as how far the base is spread * 4681 * out and where to protect the most. This routine should be called whenever a building * 4682 * is created or destroyed. * 4683 * * 4684 * INPUT: none * 4685 * * 4686 * OUTPUT: none * 4687 * * 4688 * WARNINGS: none * 4689 * * 4690 * HISTORY: * 4691 * 09/28/1995 JLB : Created. * 4692 *=============================================================================================*/ 4693 void HouseClass::Recalc_Center(void) 4694 { 4695 assert(Houses.ID(this) == ID); 4696 4697 /* 4698 ** First presume that there is no base. If there is a base, then these values will be 4699 ** properly filled in below. 4700 */ 4701 Center = 0; 4702 Radius = 0; 4703 for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) { 4704 ZoneInfo[zone].AirDefense = 0; 4705 ZoneInfo[zone].ArmorDefense = 0; 4706 ZoneInfo[zone].InfantryDefense = 0; 4707 } 4708 4709 /* 4710 ** Only process the center base size/position calculation if there are buildings to 4711 ** consider. When no buildings for this house are present, then no processing need 4712 ** occur. 4713 */ 4714 if (CurBuildings > 0) { 4715 int x = 0; 4716 int y = 0; 4717 int count = 0; 4718 int index; 4719 4720 for (index = 0; index < Buildings.Count(); index++) { 4721 BuildingClass const * b = Buildings.Ptr(index); 4722 4723 if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { 4724 4725 /* 4726 ** Give more "weight" to buildings that cost more. The presumption is that cheap 4727 ** buildings don't affect the base disposition as much as the more expensive 4728 ** buildings do. 4729 */ 4730 int weight = (b->Class->Cost_Of() / 1000)+1; 4731 for (int i = 0; i < weight; i++) { 4732 x += Coord_X(b->Center_Coord()); 4733 y += Coord_Y(b->Center_Coord()); 4734 count++; 4735 } 4736 } 4737 } 4738 4739 /* 4740 ** This second check for quantity of buildings is necessary because the first 4741 ** check against CurBuildings doesn't take into account if the building is in 4742 ** limbo, but for base calculation, the limbo state disqualifies a building 4743 ** from being processed. Thus, CurBuildings may indicate a base, but count may 4744 ** not match. 4745 */ 4746 if (count > 0) { 4747 x /= count; 4748 y /= count; 4749 4750 #ifdef NEVER 4751 /* 4752 ** Bias the center of the base away from the edges of the map. 4753 */ 4754 LEPTON left = Cell_To_Lepton(Map.MapCellX + 10); 4755 LEPTON top = Cell_To_Lepton(Map.MapCellY + 10); 4756 LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10); 4757 LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10); 4758 if (x < left) x = left; 4759 if (x > right) x = right; 4760 if (y < top) y = top; 4761 if (y > bottom) y = bottom; 4762 #endif 4763 4764 Center = XY_Coord(x, y); 4765 } 4766 4767 /* 4768 ** If there were any buildings discovered as legal to consider as part of the base, 4769 ** then figure out the general average radius of the building disposition as it 4770 ** relates to the center of the base. 4771 */ 4772 if (count > 1) { 4773 int radius = 0; 4774 4775 for (index = 0; index < Buildings.Count(); index++) { 4776 BuildingClass const * b = Buildings.Ptr(index); 4777 4778 if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { 4779 radius += Distance(Center, b->Center_Coord()); 4780 } 4781 } 4782 Radius = max(radius / count, 2 * CELL_LEPTON_W); 4783 4784 /* 4785 ** Determine the relative strength of each base defense zone. 4786 */ 4787 for (index = 0; index < Buildings.Count(); index++) { 4788 BuildingClass const * b = Buildings.Ptr(index); 4789 4790 if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { 4791 ZoneType z = Which_Zone(b); 4792 4793 if (z != ZONE_NONE) { 4794 ZoneInfo[z].ArmorDefense += b->Anti_Armor(); 4795 ZoneInfo[z].AirDefense += b->Anti_Air(); 4796 ZoneInfo[z].InfantryDefense += b->Anti_Infantry(); 4797 } 4798 } 4799 } 4800 4801 } else { 4802 Radius = 0x0200; 4803 } 4804 } 4805 } 4806 4807 4808 /*********************************************************************************************** 4809 * HouseClass::Expert_AI -- Handles expert AI processing. * 4810 * * 4811 * This routine is called when the computer should perform expert AI processing. This * 4812 * method of AI is categorized as an "Expert System" process. * 4813 * * 4814 * INPUT: none * 4815 * * 4816 * OUTPUT: Returns the number of game frames to delay before calling this routine again. * 4817 * * 4818 * WARNINGS: This is relatively time consuming -- call periodically. * 4819 * * 4820 * HISTORY: * 4821 * 09/29/1995 JLB : Created. * 4822 *=============================================================================================*/ 4823 int HouseClass::Expert_AI(void) 4824 { 4825 assert(Houses.ID(this) == ID); 4826 4827 BuildingClass * b = 0; 4828 bool stop = false; 4829 int time = TICKS_PER_SECOND * 10; 4830 4831 /* 4832 ** If the current enemy no longer has a base or is defeated, then don't consider 4833 ** that house a threat anymore. Clear out the enemy record and then try 4834 ** to find a new enemy. 4835 */ 4836 if (Enemy != HOUSE_NONE) { 4837 HouseClass * h = HouseClass::As_Pointer(Enemy); 4838 4839 if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) { 4840 Enemy = HOUSE_NONE; 4841 } 4842 } 4843 4844 /* 4845 ** If there is no enemy assigned to this house, then assign one now. The 4846 ** enemy that is closest is picked. However, don't pick an enemy if the 4847 ** base has not been established yet. 4848 */ 4849 if (ActiveBScan && Center && Attack == 0) { 4850 int close = 0; 4851 HousesType enemy = HOUSE_NONE; 4852 int maxunit = 0; 4853 int maxinfantry = 0; 4854 int maxvessel = 0; 4855 int maxaircraft = 0; 4856 int maxbuilding = 0; 4857 int enemycount = 0; 4858 4859 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 4860 HouseClass * h = HouseClass::As_Pointer(house); 4861 if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) { 4862 4863 /* 4864 ** Perform a special restriction check to ensure that no enemy is chosen if 4865 ** there is even one enemy that has not established a base yet. This will 4866 ** ensure an accurate first pick for enemy since the distance to base 4867 ** value can be determined. 4868 */ 4869 if (!h->IsStarted) { 4870 enemy = HOUSE_NONE; 4871 break; 4872 } 4873 4874 /* 4875 ** Keep track of the number of buildings and units owned by the 4876 ** enemy. This is used to bring up the maximum allowed to match. 4877 */ 4878 maxunit += h->CurUnits; 4879 maxbuilding += h->CurBuildings; 4880 maxinfantry += h->CurInfantry; 4881 maxvessel += h->CurVessels; 4882 maxaircraft += h->CurAircraft; 4883 enemycount++; 4884 4885 /* 4886 ** Determine a priority value based on distance to the center of the 4887 ** candidate base. The higher the value, the better the candidate house 4888 ** is to becoming the preferred enemy for this house. 4889 */ 4890 int value = ((MAP_CELL_W*2)-Distance(Center, h->Center)); 4891 value *= 2; 4892 4893 /* 4894 ** In addition to distance, record the number of kills directed 4895 ** against this house. The enemy that does more damage might be 4896 ** considered a greater threat. 4897 */ 4898 value += h->BuildingsKilled[Class->House]*5; 4899 value += h->UnitsKilled[Class->House]; 4900 4901 /* 4902 ** Factor in the relative sizes of the bases. An enemy that has a 4903 ** larger base will be considered a bigger threat. Conversely, a 4904 ** smaller base is considered a lesser threat. 4905 */ 4906 value += h->CurUnits - CurUnits; 4907 value += h->CurBuildings - CurBuildings; 4908 value += (h->CurInfantry - CurInfantry)/4; 4909 4910 /* 4911 ** Whoever last attacked is given a little more priority as 4912 ** a potential designated enemy. 4913 */ 4914 if (house == LAEnemy) { 4915 value += 100; 4916 } 4917 4918 #ifdef OBSOLETE 4919 /* 4920 ** Human players are a given preference as the target. 4921 */ 4922 if (h->IsHuman) { 4923 value *= 2; 4924 } 4925 #endif 4926 4927 /* 4928 ** Compare the calculated value for this candidate house and if it is 4929 ** greater than the previously recorded maximum, record this house as 4930 ** the prime candidate for enemy. 4931 */ 4932 if (value > close) { 4933 enemy = house; 4934 close = value; 4935 } 4936 } 4937 } 4938 4939 /* 4940 ** Record this closest enemy base as the first enemy to attack. 4941 */ 4942 Enemy = enemy; 4943 4944 /* 4945 ** Up the maximum allowed units and buildings to match a rough average 4946 ** of what the enemies are allowed. 4947 */ 4948 if (enemycount) { 4949 maxunit /= enemycount; 4950 maxbuilding /= enemycount; 4951 maxinfantry /= enemycount; 4952 maxvessel /= enemycount; 4953 maxaircraft /= enemycount; 4954 } 4955 4956 if (Control.MaxBuilding < (unsigned)maxbuilding + 10) { 4957 Control.MaxBuilding = maxbuilding + 10; 4958 } 4959 if (Control.MaxUnit < (unsigned)maxunit + 10) { 4960 Control.MaxUnit = maxunit + 10; 4961 } 4962 if (Control.MaxInfantry < (unsigned)maxinfantry + 10) { 4963 Control.MaxInfantry = maxinfantry + 10; 4964 } 4965 if (Control.MaxVessel < (unsigned)maxvessel + 10) { 4966 Control.MaxVessel = maxvessel + 10; 4967 } 4968 if (Control.MaxAircraft < (unsigned)maxaircraft + 10) { 4969 Control.MaxAircraft = maxaircraft + 10; 4970 } 4971 } 4972 4973 /* 4974 ** House state transition check occurs here. Transitions that occur here are ones 4975 ** that relate to general base condition rather than specific combat events. 4976 ** Typically, this is limited to transitions between normal buildup mode and 4977 ** broke mode. 4978 */ 4979 if (State == STATE_ENDGAME) { 4980 Fire_Sale(); 4981 Do_All_To_Hunt(); 4982 } else { 4983 if (State == STATE_BUILDUP) { 4984 if (Available_Money() < 25) { 4985 State = STATE_BROKE; 4986 } 4987 } 4988 if (State == STATE_BROKE) { 4989 if (Available_Money() >= 25) { 4990 State = STATE_BUILDUP; 4991 } 4992 } 4993 if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) { 4994 State = STATE_BUILDUP; 4995 } 4996 if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) { 4997 State = STATE_ATTACKED; 4998 } 4999 } 5000 5001 /* 5002 ** Records the urgency of all actions possible. 5003 */ 5004 UrgencyType urgency[STRATEGY_COUNT]; 5005 StrategyType strat; 5006 for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { 5007 urgency[strat] = URGENCY_NONE; 5008 5009 switch (strat) { 5010 case STRATEGY_BUILD_POWER: 5011 urgency[strat] = Check_Build_Power(); 5012 break; 5013 5014 case STRATEGY_BUILD_DEFENSE: 5015 urgency[strat] = Check_Build_Defense(); 5016 break; 5017 5018 case STRATEGY_BUILD_INCOME: 5019 urgency[strat] = Check_Build_Income(); 5020 break; 5021 5022 case STRATEGY_FIRE_SALE: 5023 urgency[strat] = Check_Fire_Sale(); 5024 break; 5025 5026 case STRATEGY_BUILD_ENGINEER: 5027 urgency[strat] = Check_Build_Engineer(); 5028 break; 5029 5030 case STRATEGY_BUILD_OFFENSE: 5031 urgency[strat] = Check_Build_Offense(); 5032 break; 5033 5034 case STRATEGY_RAISE_MONEY: 5035 urgency[strat] = Check_Raise_Money(); 5036 break; 5037 5038 case STRATEGY_RAISE_POWER: 5039 urgency[strat] = Check_Raise_Power(); 5040 break; 5041 5042 case STRATEGY_LOWER_POWER: 5043 urgency[strat] = Check_Lower_Power(); 5044 break; 5045 5046 case STRATEGY_ATTACK: 5047 urgency[strat] = Check_Attack(); 5048 break; 5049 5050 default: 5051 urgency[strat] = URGENCY_NONE; 5052 break; 5053 } 5054 } 5055 5056 /* 5057 ** Performs the action required for each of the strategies that share 5058 ** the most urgent category. Stop processing if any strategy at the 5059 ** highest urgency performed any action. This is because higher urgency 5060 ** actions tend to greatly affect the lower urgency actions. 5061 */ 5062 for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) { 5063 bool acted = false; 5064 5065 for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { 5066 if (urgency[strat] == u) { 5067 switch (strat) { 5068 case STRATEGY_BUILD_POWER: 5069 acted |= AI_Build_Power(u); 5070 break; 5071 5072 case STRATEGY_BUILD_DEFENSE: 5073 acted |= AI_Build_Defense(u); 5074 break; 5075 5076 case STRATEGY_BUILD_INCOME: 5077 acted |= AI_Build_Income(u); 5078 break; 5079 5080 case STRATEGY_FIRE_SALE: 5081 acted |= AI_Fire_Sale(u); 5082 break; 5083 5084 case STRATEGY_BUILD_ENGINEER: 5085 acted |= AI_Build_Engineer(u); 5086 break; 5087 5088 case STRATEGY_BUILD_OFFENSE: 5089 acted |= AI_Build_Offense(u); 5090 break; 5091 5092 case STRATEGY_RAISE_MONEY: 5093 acted |= AI_Raise_Money(u); 5094 break; 5095 5096 case STRATEGY_RAISE_POWER: 5097 acted |= AI_Raise_Power(u); 5098 break; 5099 5100 case STRATEGY_LOWER_POWER: 5101 acted |= AI_Lower_Power(u); 5102 break; 5103 5104 case STRATEGY_ATTACK: 5105 acted |= AI_Attack(u); 5106 break; 5107 5108 default: 5109 break; 5110 } 5111 } 5112 } 5113 } 5114 5115 return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2)); 5116 } 5117 5118 5119 UrgencyType HouseClass::Check_Build_Power(void) const 5120 { 5121 assert(Houses.ID(this) == ID); 5122 5123 fixed frac = Power_Fraction(); 5124 UrgencyType urgency = URGENCY_NONE; 5125 5126 if (frac < 1 && Can_Make_Money()) { 5127 urgency = URGENCY_LOW; 5128 5129 /* 5130 ** Very low power condition is considered a higher priority. 5131 */ 5132 if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM; 5133 5134 /* 5135 ** When under attack and there is a need for power in defense, 5136 ** then consider power building a higher priority. 5137 */ 5138 if (State == STATE_THREATENED || State == STATE_ATTACKED) { 5139 if (BScan | (STRUCTF_CHRONOSPHERE)) { 5140 urgency = URGENCY_HIGH; 5141 } 5142 } 5143 5144 } 5145 return(urgency); 5146 } 5147 5148 5149 UrgencyType HouseClass::Check_Build_Defense(void) const 5150 { 5151 assert(Houses.ID(this) == ID); 5152 5153 /* 5154 ** This routine determines what urgency level that base defense 5155 ** should be given. The more vulnerable the base is, the higher 5156 ** the urgency this routine should return. 5157 */ 5158 return(URGENCY_NONE); 5159 } 5160 5161 5162 UrgencyType HouseClass::Check_Build_Offense(void) const 5163 { 5164 assert(Houses.ID(this) == ID); 5165 5166 /* 5167 ** This routine determines what urgency level that offensive 5168 ** weaponry should be given. Surplus money or a very strong 5169 ** defense will cause the offensive urgency to increase. 5170 */ 5171 return(URGENCY_NONE); 5172 } 5173 5174 /* 5175 ** Determines what the attack state of the base is. The higher the state, 5176 ** the greater the immediate threat to base defense is. 5177 */ 5178 UrgencyType HouseClass::Check_Attack(void) const 5179 { 5180 assert(Houses.ID(this) == ID); 5181 5182 if (Frame > TICKS_PER_MINUTE && Attack == 0) { 5183 if (State == STATE_ATTACKED) { 5184 return(URGENCY_LOW); 5185 } 5186 return(URGENCY_CRITICAL); 5187 } 5188 return(URGENCY_NONE); 5189 } 5190 5191 5192 UrgencyType HouseClass::Check_Build_Income(void) const 5193 { 5194 assert(Houses.ID(this) == ID); 5195 5196 /* 5197 ** This routine should determine if income processing buildings 5198 ** should be constructed and at what urgency. The lower the money, 5199 ** the lower the refineries, or recent harvester losses should 5200 ** cause a greater urgency to be returned. 5201 */ 5202 return(URGENCY_NONE); 5203 } 5204 5205 5206 UrgencyType HouseClass::Check_Fire_Sale(void) const 5207 { 5208 assert(Houses.ID(this) == ID); 5209 5210 /* 5211 ** If there are no more factories at all, then sell everything off because the game 5212 ** is basically over at this point. 5213 */ 5214 if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) { 5215 return(URGENCY_CRITICAL); 5216 } 5217 return(URGENCY_NONE); 5218 } 5219 5220 5221 UrgencyType HouseClass::Check_Build_Engineer(void) const 5222 { 5223 assert(Houses.ID(this) == ID); 5224 5225 /* 5226 ** This routine should check to see what urgency that the production of 5227 ** engineers should be. If a friendly building has been captured or the 5228 ** enemy has weak defenses, then building an engineer would be a priority. 5229 */ 5230 return(URGENCY_NONE); 5231 } 5232 5233 5234 /* 5235 ** Checks to see if money is critically low and something must be done 5236 ** to immediately raise cash. 5237 */ 5238 UrgencyType HouseClass::Check_Raise_Money(void) const 5239 { 5240 assert(Houses.ID(this) == ID); 5241 5242 UrgencyType urgency = URGENCY_NONE; 5243 if (Available_Money() < 100) { 5244 urgency = URGENCY_LOW; 5245 } 5246 if (Available_Money() < 2000 && !Can_Make_Money()) { 5247 urgency++; 5248 } 5249 5250 return(urgency); 5251 } 5252 5253 /* 5254 ** Checks to see if power is very low and if so, a greater urgency to 5255 ** build more power is returned. 5256 */ 5257 UrgencyType HouseClass::Check_Lower_Power(void) const 5258 { 5259 assert(Houses.ID(this) == ID); 5260 5261 if (Power > Drain+300) { 5262 return(URGENCY_LOW); 5263 } 5264 return(URGENCY_NONE); 5265 } 5266 5267 /* 5268 ** This routine determines if there is a power emergency. Such an 5269 ** emergency might require selling of structures in order to free 5270 ** up power. This might occur if the base is being attacked and there 5271 ** are defenses that require power, but are just short of having 5272 ** enough. 5273 */ 5274 UrgencyType HouseClass::Check_Raise_Power(void) const 5275 { 5276 assert(Houses.ID(this) == ID); 5277 5278 UrgencyType urgency = URGENCY_NONE; 5279 5280 if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) { 5281 // if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) { 5282 urgency = URGENCY_MEDIUM; 5283 if (State == STATE_ATTACKED) { 5284 urgency++; 5285 } 5286 } 5287 return(urgency); 5288 } 5289 5290 5291 bool HouseClass::AI_Attack(UrgencyType ) 5292 { 5293 assert(Houses.ID(this) == ID); 5294 5295 bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33)); 5296 bool forced = (CurBuildings == 0); 5297 int index; 5298 for (index = 0; index < Aircraft.Count(); index++) { 5299 AircraftClass * a = Aircraft.Ptr(index); 5300 5301 if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) { 5302 if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { 5303 a->Assign_Mission(MISSION_HUNT); 5304 } 5305 } 5306 } 5307 for (index = 0; index < Units.Count(); index++) { 5308 UnitClass * u = Units.Ptr(index); 5309 5310 if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) { 5311 if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { 5312 u->Assign_Mission(MISSION_HUNT); 5313 } else { 5314 5315 /* 5316 ** If this unit is guarding the base, then cause it to shuffle 5317 ** location instead. 5318 */ 5319 if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) { 5320 u->ArchiveTarget = ::As_Target(Where_To_Go(u)); 5321 } 5322 } 5323 } 5324 } 5325 for (index = 0; index < Infantry.Count(); index++) { 5326 InfantryClass * i = Infantry.Ptr(index); 5327 5328 if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) { 5329 if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) { 5330 i->Assign_Mission(MISSION_HUNT); 5331 } else { 5332 5333 /* 5334 ** If this soldier is guarding the base, then cause it to shuffle 5335 ** location instead. 5336 */ 5337 if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) { 5338 i->ArchiveTarget = ::As_Target(Where_To_Go(i)); 5339 } 5340 } 5341 } 5342 } 5343 Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); 5344 return(true); 5345 } 5346 5347 5348 /* 5349 ** Given the specified urgency, build a power structure to meet 5350 ** this need. 5351 */ 5352 bool HouseClass::AI_Build_Power(UrgencyType ) const 5353 { 5354 assert(Houses.ID(this) == ID); 5355 5356 return(false); 5357 } 5358 5359 5360 /* 5361 ** Given the specified urgency, build base defensive structures 5362 ** according to need and according to existing base disposition. 5363 */ 5364 bool HouseClass::AI_Build_Defense(UrgencyType ) const 5365 { 5366 assert(Houses.ID(this) == ID); 5367 5368 return(false); 5369 } 5370 5371 /* 5372 ** Given the specified urgency, build offensive units according 5373 ** to need and according to the opponents base defenses. 5374 */ 5375 bool HouseClass::AI_Build_Offense(UrgencyType ) const 5376 { 5377 assert(Houses.ID(this) == ID); 5378 5379 return(false); 5380 } 5381 5382 /* 5383 ** Given the specified urgency, build income producing 5384 ** structures according to need. 5385 */ 5386 bool HouseClass::AI_Build_Income(UrgencyType ) const 5387 { 5388 assert(Houses.ID(this) == ID); 5389 5390 return(false); 5391 } 5392 5393 5394 bool HouseClass::AI_Fire_Sale(UrgencyType urgency) 5395 { 5396 assert(Houses.ID(this) == ID); 5397 5398 if (CurBuildings && urgency == URGENCY_CRITICAL) { 5399 Fire_Sale(); 5400 Do_All_To_Hunt(); 5401 return(true); 5402 } 5403 return(false); 5404 } 5405 5406 /* 5407 ** Given the specified urgency, build an engineer. 5408 */ 5409 bool HouseClass::AI_Build_Engineer(UrgencyType ) const 5410 { 5411 assert(Houses.ID(this) == ID); 5412 5413 return(false); 5414 } 5415 5416 /* 5417 ** Given the specified urgency, sell of some power since 5418 ** there appears to be excess. 5419 */ 5420 bool HouseClass::AI_Lower_Power(UrgencyType ) const 5421 { 5422 assert(Houses.ID(this) == ID); 5423 5424 BuildingClass * b = Find_Building(STRUCT_POWER); 5425 if (b != NULL) { 5426 b->Sell_Back(1); 5427 return(true); 5428 } 5429 5430 b = Find_Building(STRUCT_ADVANCED_POWER); 5431 if (b != NULL) { 5432 b->Sell_Back(1); 5433 return(true); 5434 } 5435 return(false); 5436 } 5437 5438 5439 /*********************************************************************************************** 5440 * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * 5441 * * 5442 * This routine is called when the computer needs to raise power by selling off buildings. * 5443 * Usually this occurs because of some catastrophe that has lowered power levels to * 5444 * the danger zone. * 5445 * * 5446 * INPUT: urgency -- The urgency that the power needs to be raised. This controls what * 5447 * buildings will be sold. * 5448 * * 5449 * OUTPUT: bool; Was a building sold to raise power? * 5450 * * 5451 * WARNINGS: none * 5452 * * 5453 * HISTORY: * 5454 * 11/02/1996 JLB : Created. * 5455 *=============================================================================================*/ 5456 bool HouseClass::AI_Raise_Power(UrgencyType urgency) const 5457 { 5458 assert(Houses.ID(this) == ID); 5459 5460 /* 5461 ** Sell off structures in this order. 5462 */ 5463 static struct { 5464 StructType Structure; 5465 UrgencyType Urgency; 5466 } _types[] = { 5467 {STRUCT_CHRONOSPHERE, URGENCY_LOW}, 5468 {STRUCT_SHIP_YARD, URGENCY_LOW}, 5469 {STRUCT_SUB_PEN, URGENCY_LOW}, 5470 {STRUCT_ADVANCED_TECH, URGENCY_LOW}, 5471 {STRUCT_FORWARD_COM, URGENCY_LOW}, 5472 {STRUCT_SOVIET_TECH, URGENCY_LOW}, 5473 {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM}, 5474 {STRUCT_RADAR, URGENCY_MEDIUM}, 5475 {STRUCT_REPAIR, URGENCY_MEDIUM}, 5476 {STRUCT_TESLA, URGENCY_HIGH} 5477 }; 5478 5479 /* 5480 ** Find a structure to sell and then sell it. Bail from further scanning until 5481 ** the next time. 5482 */ 5483 for (int i = 0; i < ARRAY_SIZE(_types); i++) { 5484 if (urgency >= _types[i].Urgency) { 5485 BuildingClass * b = Find_Building(_types[i].Structure); 5486 if (b != NULL) { 5487 b->Sell_Back(1); 5488 return(true); 5489 } 5490 } 5491 } 5492 return(false); 5493 } 5494 5495 5496 /*********************************************************************************************** 5497 * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * 5498 * * 5499 * This routine handles the situation where the computer desperately needs cash but cannot * 5500 * wait for normal harvesting to raise it. Buildings must be sold. * 5501 * * 5502 * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, * 5503 * the more important the buildings that can be sold become. * 5504 * * 5505 * OUTPUT: bool; Was a building sold to raise cash? * 5506 * * 5507 * WARNINGS: none * 5508 * * 5509 * HISTORY: * 5510 * 11/02/1996 JLB : Created. * 5511 *=============================================================================================*/ 5512 bool HouseClass::AI_Raise_Money(UrgencyType urgency) const 5513 { 5514 assert(Houses.ID(this) == ID); 5515 5516 /* 5517 ** Sell off structures in this order. 5518 */ 5519 static struct { 5520 StructType Structure; 5521 UrgencyType Urgency; 5522 } _types[] = { 5523 {STRUCT_CHRONOSPHERE, URGENCY_LOW}, 5524 {STRUCT_SHIP_YARD, URGENCY_LOW}, 5525 {STRUCT_SUB_PEN, URGENCY_LOW}, 5526 {STRUCT_ADVANCED_TECH, URGENCY_LOW}, 5527 {STRUCT_FORWARD_COM, URGENCY_LOW}, 5528 {STRUCT_SOVIET_TECH, URGENCY_LOW}, 5529 {STRUCT_STORAGE,URGENCY_LOW}, 5530 {STRUCT_REPAIR,URGENCY_LOW}, 5531 {STRUCT_TESLA,URGENCY_MEDIUM}, 5532 {STRUCT_HELIPAD,URGENCY_MEDIUM}, 5533 {STRUCT_POWER,URGENCY_HIGH}, 5534 {STRUCT_AIRSTRIP,URGENCY_HIGH}, 5535 // {STRUCT_WEAP,URGENCY_HIGH}, 5536 // {STRUCT_BARRACKS,URGENCY_HIGH}, 5537 // {STRUCT_TENT,URGENCY_HIGH}, 5538 {STRUCT_CONST,URGENCY_CRITICAL} 5539 }; 5540 BuildingClass * b = 0; 5541 5542 /* 5543 ** Find a structure to sell and then sell it. Bail from further scanning until 5544 ** the next time. 5545 */ 5546 for (int i = 0; i < ARRAY_SIZE(_types); i++) { 5547 if (urgency >= _types[i].Urgency) { 5548 b = Find_Building(_types[i].Structure); 5549 if (b != NULL) { 5550 b->Sell_Back(1); 5551 return(true); 5552 } 5553 } 5554 } 5555 return(false); 5556 } 5557 5558 5559 #ifdef NEVER 5560 5561 /*********************************************************************************************** 5562 * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * 5563 * * 5564 * This logic is used to maintain a base defense. * 5565 * * 5566 * INPUT: none * 5567 * * 5568 * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * 5569 * * 5570 * WARNINGS: none * 5571 * * 5572 * HISTORY: * 5573 * 09/29/1995 JLB : Created. * 5574 *=============================================================================================*/ 5575 int HouseClass::AI_Base_Defense(void) 5576 { 5577 assert(Houses.ID(this) == ID); 5578 5579 /* 5580 ** Check to find if any zone of the base is over defended. Such zones should have 5581 ** some of their defenses sold off to make better use of the money. 5582 */ 5583 5584 /* 5585 ** Make sure that the core defense is only about 1/2 of the perimeter defense average. 5586 */ 5587 int average = 0; 5588 for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) { 5589 average += ZoneInfo[z].AirDefense; 5590 average += ZoneInfo[z].ArmorDefense; 5591 average += ZoneInfo[z].InfantryDefense; 5592 } 5593 average /= (ZONE_COUNT-ZONE_NORTH); 5594 5595 /* 5596 ** If the core value is greater than the average, then sell off some of the 5597 ** inner defensive structures. 5598 */ 5599 int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense; 5600 if (core >= average) { 5601 static StructType _stype[] = { 5602 STRUCT_GTOWER, 5603 STRUCT_TURRET, 5604 STRUCT_ATOWER, 5605 STRUCT_OBELISK, 5606 STRUCT_TESLA, 5607 STRUCT_SAM 5608 }; 5609 BuildingClass * b; 5610 5611 for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) { 5612 b = Find_Building(_stype[index], ZONE_CORE); 5613 if (b) { 5614 b->Sell_Back(1); 5615 break; 5616 } 5617 } 5618 } 5619 5620 /* 5621 ** If the enemy doesn't have any offensive air capability, then sell off any 5622 ** SAM sites. Only do this when money is moderately low. 5623 */ 5624 if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) { 5625 5626 /* 5627 ** Scan to find if ANY human opponents have aircraft or a helipad. If one 5628 ** is found then consider that opponent to have a valid air threat potential. 5629 ** Don't sell off SAM sites in that case. 5630 */ 5631 bool nothreat = true; 5632 for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 5633 HouseClass * house = HouseClass::As_Pointer(h); 5634 5635 if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) { 5636 if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) { 5637 nothreat = false; 5638 break; 5639 } 5640 } 5641 } 5642 } 5643 5644 return(TICKS_PER_SECOND*5); 5645 } 5646 #endif 5647 5648 5649 /*********************************************************************************************** 5650 * HouseClass::AI_Building -- Determines what building to build. * 5651 * * 5652 * This routine handles the general case of determining what building to build next. * 5653 * * 5654 * INPUT: none * 5655 * * 5656 * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * 5657 * * 5658 * WARNINGS: none * 5659 * * 5660 * HISTORY: * 5661 * 09/29/1995 JLB : Created. * 5662 * 11/03/1996 JLB : Tries to match aircraft of enemy * 5663 *=============================================================================================*/ 5664 int HouseClass::AI_Building(void) 5665 { 5666 assert(Houses.ID(this) == ID); 5667 5668 if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND); 5669 5670 if (Session.Type == GAME_NORMAL && Base.House == Class->House) { 5671 BaseNodeClass * node = Base.Next_Buildable(); 5672 if (node) { 5673 BuildStructure = node->Type; 5674 } 5675 } 5676 5677 if (IsBaseBuilding) { 5678 /* 5679 ** Don't suggest anything to build if the base is already big enough. 5680 */ 5681 unsigned int quant = 0; 5682 for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 5683 HouseClass const * hptr = HouseClass::As_Pointer(h); 5684 5685 if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) { 5686 quant = hptr->CurBuildings; 5687 } 5688 } 5689 quant += Rule.BaseSizeAdd; 5690 5691 // TCTC -- Should multiply largest player base by some rational number. 5692 // if (CurBuildings >= quant) return(TICKS_PER_SECOND); 5693 5694 BuildChoice.Free_All(); 5695 BuildChoiceClass * choiceptr; 5696 StructType stype = STRUCT_NONE; 5697 int money = Available_Money(); 5698 int level = Control.TechLevel; 5699 bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0); 5700 BuildingTypeClass const * b = NULL; 5701 HouseClass const * enemy = NULL; 5702 if (Enemy != HOUSE_NONE) { 5703 enemy = HouseClass::As_Pointer(Enemy); 5704 } 5705 5706 level = Control.TechLevel; 5707 5708 /* 5709 ** Try to build a power plant if there is insufficient power and there is enough 5710 ** money available. 5711 */ 5712 b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER); 5713 if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { 5714 choiceptr = BuildChoice.Alloc(); 5715 if (choiceptr != NULL) { 5716 *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); 5717 } 5718 } else { 5719 b = &BuildingTypeClass::As_Reference(STRUCT_POWER); 5720 if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { 5721 choiceptr = BuildChoice.Alloc(); 5722 if (choiceptr != NULL) { 5723 *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); 5724 } 5725 } 5726 } 5727 5728 /* 5729 ** Build a refinery if there isn't one already available. 5730 */ 5731 unsigned int current = BQuantity[STRUCT_REFINERY]; 5732 if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < (unsigned)Rule.RefineryLimit) { 5733 b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY); 5734 if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) { 5735 choiceptr = BuildChoice.Alloc(); 5736 if (choiceptr != NULL) { 5737 *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); 5738 } 5739 } 5740 } 5741 5742 /* 5743 ** Always make sure there is a barracks available, but only if there 5744 ** will be sufficient money to train troopers. 5745 */ 5746 current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT]; 5747 if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < (unsigned)Rule.BarracksLimit && (money > 300 || hasincome)) { 5748 b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS); 5749 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5750 choiceptr = BuildChoice.Alloc(); 5751 if (choiceptr != NULL) { 5752 *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); 5753 } 5754 } else { 5755 b = &BuildingTypeClass::As_Reference(STRUCT_TENT); 5756 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5757 choiceptr = BuildChoice.Alloc(); 5758 if (choiceptr != NULL) { 5759 *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); 5760 } 5761 } 5762 } 5763 } 5764 5765 /* 5766 ** Try to build one dog house. 5767 */ 5768 current = BQuantity[STRUCT_KENNEL]; 5769 if (current < 1 && (money > 300 || hasincome)) { 5770 b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL); 5771 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5772 choiceptr = BuildChoice.Alloc(); 5773 if (choiceptr != NULL) { 5774 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5775 } 5776 } 5777 } 5778 5779 /* 5780 ** Try to build one gap generator. 5781 */ 5782 current = BQuantity[STRUCT_GAP]; 5783 if (current < 1 && Power_Fraction() >= 1 && hasincome) { 5784 b = &BuildingTypeClass::As_Reference(STRUCT_GAP); 5785 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5786 choiceptr = BuildChoice.Alloc(); 5787 if (choiceptr != NULL) { 5788 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5789 } 5790 } 5791 } 5792 5793 /* 5794 ** A source of combat vehicles is always needed, but only if there will 5795 ** be sufficient money to build vehicles. 5796 */ 5797 current = BQuantity[STRUCT_WEAP]; 5798 if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit && (money > 2000 || hasincome)) { 5799 b = &BuildingTypeClass::As_Reference(STRUCT_WEAP); 5800 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5801 choiceptr = BuildChoice.Alloc(); 5802 if (choiceptr != NULL) { 5803 *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); 5804 } 5805 } 5806 } 5807 5808 /* 5809 ** Always build up some base defense. 5810 */ 5811 current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET]; 5812 if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) { 5813 b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET); 5814 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5815 choiceptr = BuildChoice.Alloc(); 5816 if (choiceptr != NULL) { 5817 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5818 } 5819 } else { 5820 if (Percent_Chance(50)) { 5821 b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX); 5822 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5823 choiceptr = BuildChoice.Alloc(); 5824 if (choiceptr != NULL) { 5825 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5826 } 5827 } 5828 } else { 5829 b = &BuildingTypeClass::As_Reference(STRUCT_TURRET); 5830 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5831 choiceptr = BuildChoice.Alloc(); 5832 if (choiceptr != NULL) { 5833 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5834 } 5835 } 5836 } 5837 } 5838 } 5839 5840 /* 5841 ** Build some air defense. 5842 */ 5843 current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN]; 5844 if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < (unsigned)Rule.AALimit) { 5845 5846 /* 5847 ** Building air defense only makes sense if the opponent has aircraft 5848 ** of some kind. 5849 */ 5850 bool airthreat = false; 5851 int threat_quantity = 0; 5852 if (enemy != NULL && enemy->AScan != 0) { 5853 airthreat = true; 5854 threat_quantity = enemy->CurAircraft; 5855 } 5856 if (!airthreat) { 5857 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 5858 HouseClass * h = HouseClass::As_Pointer(house); 5859 if (h != NULL && !Is_Ally(house) && h->AScan != 0) { 5860 airthreat = true; 5861 break; 5862 } 5863 } 5864 } 5865 5866 if (airthreat) { 5867 5868 if (BQuantity[STRUCT_RADAR] == 0) { 5869 b = &BuildingTypeClass::As_Reference(STRUCT_RADAR); 5870 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5871 choiceptr = BuildChoice.Alloc(); 5872 if (choiceptr != NULL) { 5873 *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type); 5874 } 5875 } 5876 } 5877 5878 b = &BuildingTypeClass::As_Reference(STRUCT_SAM); 5879 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5880 choiceptr = BuildChoice.Alloc(); 5881 if (choiceptr != NULL) { 5882 *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); 5883 } 5884 } else { 5885 b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN); 5886 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5887 choiceptr = BuildChoice.Alloc(); 5888 if (choiceptr != NULL) { 5889 *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); 5890 } 5891 } 5892 } 5893 } 5894 } 5895 5896 /* 5897 ** Advanced base defense would be good. 5898 */ 5899 current = BQuantity[STRUCT_TESLA]; 5900 if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) { 5901 b = &BuildingTypeClass::As_Reference(STRUCT_TESLA); 5902 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { 5903 choiceptr = BuildChoice.Alloc(); 5904 if (choiceptr != NULL) { 5905 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5906 } 5907 } 5908 } 5909 5910 /* 5911 ** Build a tech center as soon as possible. 5912 */ 5913 current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH]; 5914 if (current < 1) { 5915 b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH); 5916 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { 5917 choiceptr = BuildChoice.Alloc(); 5918 if (choiceptr != NULL) { 5919 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5920 } 5921 } else { 5922 b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH); 5923 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { 5924 choiceptr = BuildChoice.Alloc(); 5925 if (choiceptr != NULL) { 5926 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5927 } 5928 } 5929 } 5930 } 5931 5932 /* 5933 ** A helipad would be good. 5934 */ 5935 current = BQuantity[STRUCT_HELIPAD]; 5936 if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) { 5937 b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD); 5938 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5939 choiceptr = BuildChoice.Alloc(); 5940 if (choiceptr != NULL) { 5941 int threat_quantity = 0; 5942 if (enemy != NULL) { 5943 threat_quantity = enemy->CurAircraft; 5944 } 5945 5946 *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); 5947 } 5948 } 5949 } 5950 5951 /* 5952 ** An airstrip would be good. 5953 */ 5954 current = BQuantity[STRUCT_AIRSTRIP]; 5955 if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) { 5956 b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP); 5957 if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { 5958 choiceptr = BuildChoice.Alloc(); 5959 if (choiceptr != NULL) { 5960 int threat_quantity = 0; 5961 if (enemy != NULL) { 5962 threat_quantity = enemy->CurAircraft; 5963 } 5964 5965 *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); 5966 } 5967 } 5968 } 5969 5970 #ifdef OLD 5971 /* 5972 ** Build a repair bay if there isn't one already available. 5973 */ 5974 current = BQuantity[STRUCT_REPAIR]; 5975 if (current == 0) { 5976 b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR); 5977 if (Can_Build(b, ActLike) && b->Cost_Of() < money) { 5978 choiceptr = BuildChoice.Alloc(); 5979 if (choiceptr) { 5980 *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); 5981 } 5982 } 5983 } 5984 #endif 5985 5986 /* 5987 ** Pick the choice that is the most urgent. 5988 */ 5989 UrgencyType best = URGENCY_NONE; 5990 int bestindex; 5991 for (int index = 0; index < BuildChoice.Count(); index++) { 5992 if (BuildChoice.Ptr(index)->Urgency > best) { 5993 bestindex = index; 5994 best = BuildChoice.Ptr(index)->Urgency; 5995 } 5996 } 5997 if (best != URGENCY_NONE) { 5998 BuildStructure = BuildChoice.Ptr(bestindex)->Structure; 5999 } 6000 } 6001 6002 return(TICKS_PER_SECOND); 6003 } 6004 6005 6006 /*********************************************************************************************** 6007 * HouseClass::AI_Unit -- Determines what unit to build next. * 6008 * * 6009 * This routine handles the general case of determining what units to build next. * 6010 * * 6011 * INPUT: none * 6012 * * 6013 * OUTPUT: Returns with the number of games frames to delay before calling this routine again.* 6014 * * 6015 * WARNINGS: none * 6016 * * 6017 * HISTORY: * 6018 * 09/29/1995 JLB : Created. * 6019 *=============================================================================================*/ 6020 int HouseClass::AI_Unit(void) 6021 { 6022 assert(Houses.ID(this) == ID); 6023 6024 if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND); 6025 if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND); 6026 6027 /* 6028 ** A computer controlled house will try to build a replacement 6029 ** harvester if possible. 6030 */ 6031 if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) { 6032 if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)Control.TechLevel) { 6033 BuildUnit = UNIT_HARVESTER; 6034 return(TICKS_PER_SECOND); 6035 } 6036 } 6037 6038 if (Session.Type == GAME_NORMAL) { 6039 6040 int counter[UNIT_COUNT]; 6041 memset(counter, 0x00, sizeof(counter)); 6042 6043 /* 6044 ** Build a list of the maximum of each type we wish to produce. This will be 6045 ** twice the number required to fill all teams. 6046 */ 6047 int index; 6048 for (index = 0; index < Teams.Count(); index++) { 6049 TeamClass * tptr = Teams.Ptr(index); 6050 if (tptr != NULL) { 6051 TeamTypeClass const * team = tptr->Class; 6052 if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { 6053 for (int subindex = 0; subindex < team->ClassCount; subindex++) { 6054 TechnoTypeClass const * memtype = team->Members[subindex].Class; 6055 if (memtype->What_Am_I() == RTTI_UNITTYPE) { 6056 counter[((UnitTypeClass const *)memtype)->Type] = 1; 6057 } 6058 } 6059 } 6060 } 6061 } 6062 6063 /* 6064 ** Team types that are flagged as prebuilt, will always try to produce enough 6065 ** to fill one team of this type regardless of whether there is a team active 6066 ** of that type. 6067 */ 6068 for (index = 0; index < TeamTypes.Count(); index++) { 6069 TeamTypeClass const * team = TeamTypes.Ptr(index); 6070 if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { 6071 for (int subindex = 0; subindex < team->ClassCount; subindex++) { 6072 TechnoTypeClass const * memtype = team->Members[subindex].Class; 6073 6074 if (memtype->What_Am_I() == RTTI_UNITTYPE) { 6075 int subtype = ((UnitTypeClass const *)memtype)->Type; 6076 counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); 6077 } 6078 } 6079 } 6080 } 6081 6082 /* 6083 ** Reduce the theoretical maximum by the actual number of objects currently 6084 ** in play. 6085 */ 6086 for (int uindex = 0; uindex < Units.Count(); uindex++) { 6087 UnitClass * unit = Units.Ptr(uindex); 6088 if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { 6089 counter[unit->Class->Type]--; 6090 } 6091 } 6092 6093 /* 6094 ** Pick to build the most needed object but don't consider those objects that 6095 ** can't be built because of scenario restrictions or insufficient cash. 6096 */ 6097 int bestval = -1; 6098 int bestcount = 0; 6099 UnitType bestlist[UNIT_COUNT]; 6100 for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { 6101 if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { 6102 if (bestval == -1 || bestval < counter[utype]) { 6103 bestval = counter[utype]; 6104 bestcount = 0; 6105 } 6106 bestlist[bestcount++] = utype; 6107 } 6108 } 6109 6110 /* 6111 ** The unit type to build is now known. Fetch a pointer to the techno type class. 6112 */ 6113 if (bestcount) { 6114 BuildUnit = bestlist[Random_Pick(0, bestcount-1)]; 6115 } 6116 } 6117 6118 if (IsBaseBuilding) { 6119 6120 int counter[UNIT_COUNT]; 6121 int total = 0; 6122 UnitType index; 6123 for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { 6124 UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index); 6125 if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) { 6126 if (utype->PrimaryWeapon != NULL) { 6127 counter[index] = 20; 6128 } else { 6129 counter[index] = 1; 6130 } 6131 } else { 6132 counter[index] = 0; 6133 } 6134 total += counter[index]; 6135 } 6136 6137 if (total > 0) { 6138 int choice = Random_Pick(0, total-1); 6139 for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { 6140 if (choice < counter[index]) { 6141 BuildUnit = index; 6142 break; 6143 } 6144 choice -= counter[index]; 6145 } 6146 } 6147 } 6148 6149 return(TICKS_PER_SECOND); 6150 } 6151 6152 6153 int HouseClass::AI_Vessel(void) 6154 { 6155 assert(Houses.ID(this) == ID); 6156 if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND); 6157 6158 if (CurVessels >= Control.MaxVessel) { 6159 return(TICKS_PER_SECOND); 6160 } 6161 6162 if (Session.Type == GAME_NORMAL) { 6163 6164 int counter[VESSEL_COUNT]; 6165 if (Session.Type == GAME_NORMAL) { 6166 memset(counter, 0x00, sizeof(counter)); 6167 } else { 6168 for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { 6169 if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { 6170 counter[index] = 16; 6171 } else { 6172 counter[index] = 0; 6173 } 6174 } 6175 } 6176 6177 /* 6178 ** Build a list of the maximum of each type we wish to produce. This will be 6179 ** twice the number required to fill all teams. 6180 */ 6181 int index; 6182 for (index = 0; index < Teams.Count(); index++) { 6183 TeamClass * tptr = Teams.Ptr(index); 6184 if (tptr) { 6185 TeamTypeClass const * team = tptr->Class; 6186 6187 if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { 6188 for (int subindex = 0; subindex < team->ClassCount; subindex++) { 6189 if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { 6190 counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1; 6191 } 6192 } 6193 } 6194 } 6195 } 6196 6197 /* 6198 ** Team types that are flagged as prebuilt, will always try to produce enough 6199 ** to fill one team of this type regardless of whether there is a team active 6200 ** of that type. 6201 */ 6202 for (index = 0; index < TeamTypes.Count(); index++) { 6203 TeamTypeClass const * team = TeamTypes.Ptr(index); 6204 if (team) { 6205 if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { 6206 for (int subindex = 0; subindex < team->ClassCount; subindex++) { 6207 if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { 6208 int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type; 6209 counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); 6210 } 6211 } 6212 } 6213 } 6214 } 6215 6216 /* 6217 ** Reduce the theoretical maximum by the actual number of objects currently 6218 ** in play. 6219 */ 6220 for (int vindex = 0; vindex < Vessels.Count(); vindex++) { 6221 VesselClass * unit = Vessels.Ptr(vindex); 6222 if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { 6223 counter[unit->Class->Type]--; 6224 } 6225 } 6226 6227 /* 6228 ** Pick to build the most needed object but don't consider those object that 6229 ** can't be built because of scenario restrictions or insufficient cash. 6230 */ 6231 int bestval = -1; 6232 int bestcount = 0; 6233 VesselType bestlist[VESSEL_COUNT]; 6234 for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) { 6235 if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { 6236 if (bestval == -1 || bestval < counter[utype]) { 6237 bestval = counter[utype]; 6238 bestcount = 0; 6239 } 6240 bestlist[bestcount++] = utype; 6241 } 6242 } 6243 6244 /* 6245 ** The unit type to build is now known. Fetch a pointer to the techno type class. 6246 */ 6247 if (bestcount) { 6248 BuildVessel = bestlist[Random_Pick(0, bestcount-1)]; 6249 } 6250 } 6251 6252 if (IsBaseBuilding) { 6253 BuildVessel = VESSEL_NONE; 6254 } 6255 6256 return(TICKS_PER_SECOND); 6257 } 6258 6259 6260 6261 /*********************************************************************************************** 6262 * HouseClass::AI_Infantry -- Determines the infantry unit to build. * 6263 * * 6264 * This routine handles the general case of determining what infantry unit to build * 6265 * next. * 6266 * * 6267 * INPUT: none * 6268 * * 6269 * OUTPUT: Returns with the number of game frames to delay before being called again. * 6270 * * 6271 * WARNINGS: none * 6272 * * 6273 * HISTORY: * 6274 * 09/29/1995 JLB : Created. * 6275 *=============================================================================================*/ 6276 int HouseClass::AI_Infantry(void) 6277 { 6278 assert(Houses.ID(this) == ID); 6279 6280 if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND); 6281 if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND); 6282 6283 if (Session.Type == GAME_NORMAL) { 6284 TechnoTypeClass const * techno = 0; 6285 int counter[INFANTRY_COUNT]; 6286 memset(counter, 0x00, sizeof(counter)); 6287 6288 /* 6289 ** Build a list of the maximum of each type we wish to produce. This will be 6290 ** twice the number required to fill all teams. 6291 */ 6292 int index; 6293 for (index = 0; index < Teams.Count(); index++) { 6294 TeamClass * tptr = Teams.Ptr(index); 6295 if (tptr != NULL) { 6296 TeamTypeClass const * team = tptr->Class; 6297 6298 if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { 6299 for (int subindex = 0; subindex < team->ClassCount; subindex++) { 6300 if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { 6301 counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0); 6302 } 6303 } 6304 } 6305 } 6306 } 6307 6308 /* 6309 ** Team types that are flagged as prebuilt, will always try to produce enough 6310 ** to fill one team of this type regardless of whether there is a team active 6311 ** of that type. 6312 */ 6313 for (index = 0; index < TeamTypes.Count(); index++) { 6314 TeamTypeClass const * team = TeamTypes.Ptr(index); 6315 if (team != NULL) { 6316 if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { 6317 for (int subindex = 0; subindex < team->ClassCount; subindex++) { 6318 if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { 6319 int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type; 6320 // counter[subtype] = 1; 6321 counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); 6322 counter[subtype] = min(counter[subtype], 5); 6323 } 6324 } 6325 } 6326 } 6327 } 6328 6329 /* 6330 ** Reduce the theoretical maximum by the actual number of objects currently 6331 ** in play. 6332 */ 6333 for (int uindex = 0; uindex < Infantry.Count(); uindex++) { 6334 InfantryClass * infantry = Infantry.Ptr(uindex); 6335 if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) { 6336 counter[infantry->Class->Type]--; 6337 } 6338 } 6339 6340 /* 6341 ** Pick to build the most needed object but don't consider those object that 6342 ** can't be built because of scenario restrictions or insufficient cash. 6343 */ 6344 int bestval = -1; 6345 int bestcount = 0; 6346 InfantryType bestlist[INFANTRY_COUNT]; 6347 for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { 6348 6349 if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) { 6350 if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { 6351 if (bestval == -1 || bestval < counter[utype]) { 6352 bestval = counter[utype]; 6353 bestcount = 0; 6354 } 6355 bestlist[bestcount++] = utype; 6356 } 6357 } 6358 } 6359 6360 /* 6361 ** The infantry type to build is now known. Fetch a pointer to the techno type class. 6362 */ 6363 if (bestcount) { 6364 int pick = Random_Pick(0, bestcount-1); 6365 BuildInfantry = bestlist[pick]; 6366 } 6367 6368 } 6369 6370 if (IsBaseBuilding) { 6371 HouseClass const * enemy = NULL; 6372 if (Enemy != HOUSE_NONE) { 6373 enemy = HouseClass::As_Pointer(Enemy); 6374 } 6375 6376 /* 6377 ** This structure is used to keep track of the list of infantry types that should be 6378 ** built. The infantry type and the value assigned to it is recorded. 6379 */ 6380 struct { 6381 InfantryType Type; // Infantry type. 6382 int Value; // Relative value assigned. 6383 } typetrack[INFANTRY_COUNT]; 6384 int count = 0; 6385 int total = 0; 6386 for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { 6387 if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { 6388 typetrack[count].Value = 0; 6389 #ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility. 6390 int clipindex = index; 6391 if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT; 6392 if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { 6393 #else 6394 if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { 6395 #endif 6396 6397 switch (index) { 6398 case INFANTRY_E1: 6399 typetrack[count].Value = 3; 6400 break; 6401 6402 case INFANTRY_E2: 6403 typetrack[count].Value = 5; 6404 break; 6405 6406 case INFANTRY_E3: 6407 typetrack[count].Value = 2; 6408 break; 6409 6410 case INFANTRY_E4: 6411 typetrack[count].Value = 5; 6412 break; 6413 6414 case INFANTRY_RENOVATOR: 6415 if (CurInfantry > 5) { 6416 typetrack[count].Value = 1 - max(IQuantity[index], 0); 6417 } 6418 break; 6419 6420 case INFANTRY_TANYA: 6421 typetrack[count].Value = 1 - max(IQuantity[index], 0); 6422 break; 6423 6424 default: 6425 typetrack[count].Value = 0; 6426 break; 6427 } 6428 } 6429 6430 if (typetrack[count].Value > 0) { 6431 typetrack[count].Type = index; 6432 total += typetrack[count].Value; 6433 count++; 6434 } 6435 } 6436 } 6437 6438 /* 6439 ** If there is at least one choice, then pick it. The object picked 6440 ** is influenced by the weight (value) assigned to it. This is accomplished 6441 ** by picking a number between 0 and the total weight value. The appropriate 6442 ** infantry object that matches the number picked is then selected to be built. 6443 */ 6444 if (count > 0) { 6445 int pick = Random_Pick(0, total-1); 6446 for (int index = 0; index < count; index++) { 6447 if (pick < typetrack[index].Value) { 6448 BuildInfantry = typetrack[index].Type; 6449 break; 6450 } 6451 pick -= typetrack[index].Value; 6452 } 6453 } 6454 } 6455 return(TICKS_PER_SECOND); 6456 } 6457 6458 6459 /*********************************************************************************************** 6460 * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * 6461 * * 6462 * This routine is used to determine the general case of what aircraft to build next. * 6463 * * 6464 * INPUT: none * 6465 * * 6466 * OUTPUT: Returns with the number of frame to delay before calling this routine again. * 6467 * * 6468 * WARNINGS: none * 6469 * * 6470 * HISTORY: * 6471 * 09/29/1995 JLB : Created. * 6472 *=============================================================================================*/ 6473 int HouseClass::AI_Aircraft(void) 6474 { 6475 assert(Houses.ID(this) == ID); 6476 6477 if (!IsHuman && IQ >= Rule.IQAircraft) { 6478 if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND); 6479 if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND); 6480 6481 if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) && 6482 AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)Control.TechLevel && 6483 BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { 6484 BuildAircraft = AIRCRAFT_LONGBOW; 6485 return(TICKS_PER_SECOND); 6486 } 6487 6488 if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) && 6489 AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)Control.TechLevel && 6490 BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { 6491 BuildAircraft = AIRCRAFT_HIND; 6492 return(TICKS_PER_SECOND); 6493 } 6494 6495 if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) && 6496 AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel && 6497 BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { 6498 BuildAircraft = AIRCRAFT_MIG; 6499 return(TICKS_PER_SECOND); 6500 } 6501 6502 if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) && 6503 AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel && 6504 BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { 6505 BuildAircraft = AIRCRAFT_YAK; 6506 return(TICKS_PER_SECOND); 6507 } 6508 } 6509 6510 return(TICKS_PER_SECOND); 6511 } 6512 6513 6514 /*********************************************************************************************** 6515 * HouseClass::Production_Begun -- Records that production has begun. * 6516 * * 6517 * This routine is used to inform the Expert System that production of the specified object * 6518 * has begun. This allows the AI to proceed with picking another object to begin production * 6519 * on. * 6520 * * 6521 * INPUT: product -- Pointer to the object that production has just begun on. * 6522 * * 6523 * OUTPUT: none * 6524 * * 6525 * WARNINGS: none * 6526 * * 6527 * HISTORY: * 6528 * 09/29/1995 JLB : Created. * 6529 *=============================================================================================*/ 6530 void HouseClass::Production_Begun(TechnoClass const * product) 6531 { 6532 assert(Houses.ID(this) == ID); 6533 6534 if (product != NULL) { 6535 switch (product->What_Am_I()) { 6536 case RTTI_UNIT: 6537 if (*((UnitClass*)product) == BuildUnit) { 6538 BuildUnit = UNIT_NONE; 6539 } 6540 break; 6541 6542 case RTTI_VESSEL: 6543 if (*((VesselClass*)product) == BuildVessel) { 6544 BuildVessel = VESSEL_NONE; 6545 } 6546 break; 6547 6548 case RTTI_INFANTRY: 6549 if (*((InfantryClass*)product) == BuildInfantry) { 6550 BuildInfantry = INFANTRY_NONE; 6551 } 6552 break; 6553 6554 case RTTI_BUILDING: 6555 if (*((BuildingClass*)product) == BuildStructure) { 6556 BuildStructure = STRUCT_NONE; 6557 } 6558 break; 6559 6560 case RTTI_AIRCRAFT: 6561 if (*((AircraftClass*)product) == BuildAircraft) { 6562 BuildAircraft = AIRCRAFT_NONE; 6563 } 6564 break; 6565 6566 default: 6567 break; 6568 } 6569 } 6570 } 6571 6572 6573 /*********************************************************************************************** 6574 * HouseClass::Tracking_Remove -- Remove object from house tracking system. * 6575 * * 6576 * This routine informs the Expert System that the specified object is no longer part of * 6577 * this house's inventory. This occurs when the object is destroyed or captured. * 6578 * * 6579 * INPUT: techno -- Pointer to the object to remove from the tracking systems of this * 6580 * house. * 6581 * * 6582 * OUTPUT: none * 6583 * * 6584 * WARNINGS: none * 6585 * * 6586 * HISTORY: * 6587 * 09/29/1995 JLB : Created. * 6588 *=============================================================================================*/ 6589 void HouseClass::Tracking_Remove(TechnoClass const * techno) 6590 { 6591 assert(Houses.ID(this) == ID); 6592 6593 int type; 6594 6595 switch (techno->What_Am_I()) { 6596 case RTTI_BUILDING: 6597 CurBuildings--; 6598 BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--; 6599 break; 6600 6601 case RTTI_AIRCRAFT: 6602 CurAircraft--; 6603 AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--; 6604 break; 6605 6606 case RTTI_INFANTRY: 6607 CurInfantry--; 6608 if (!((InfantryClass *)techno)->IsTechnician) { 6609 type = ((InfantryTypeClass const &)techno->Class_Of()).Type; 6610 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6611 if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT; 6612 #endif 6613 IQuantity[type]--; 6614 } 6615 break; 6616 6617 case RTTI_UNIT: 6618 CurUnits--; 6619 type = ((UnitTypeClass const &)techno->Class_Of()).Type; 6620 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6621 if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT; 6622 #endif 6623 UQuantity[type]--; 6624 break; 6625 6626 case RTTI_VESSEL: 6627 CurVessels--; 6628 type = ((VesselTypeClass const &)techno->Class_Of()).Type; 6629 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6630 if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT; 6631 #endif 6632 VQuantity[type]--; 6633 break; 6634 6635 default: 6636 break; 6637 } 6638 } 6639 6640 6641 /*********************************************************************************************** 6642 * HouseClass::Tracking_Add -- Informs house of new inventory item. * 6643 * * 6644 * This function is called when the specified object is now available as part of the house's* 6645 * inventory. This occurs when the object is newly produced and also when it is captured * 6646 * by this house. * 6647 * * 6648 * INPUT: techno -- Pointer to the object that is now part of the house inventory. * 6649 * * 6650 * OUTPUT: none * 6651 * * 6652 * WARNINGS: none * 6653 * * 6654 * HISTORY: * 6655 * 09/29/1995 JLB : Created. * 6656 *=============================================================================================*/ 6657 void HouseClass::Tracking_Add(TechnoClass const * techno) 6658 { 6659 assert(Houses.ID(this) == ID); 6660 6661 StructType building; 6662 AircraftType aircraft; 6663 InfantryType infantry; 6664 UnitType unit; 6665 VesselType vessel; 6666 int quant; 6667 6668 switch (techno->What_Am_I()) { 6669 case RTTI_BUILDING: 6670 CurBuildings++; 6671 building = ((BuildingTypeClass const &)techno->Class_Of()).Type; 6672 BQuantity[building]++; 6673 BScan |= (1L << building); 6674 if (Session.Type == GAME_INTERNET) { 6675 BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID); 6676 } 6677 break; 6678 6679 case RTTI_AIRCRAFT: 6680 CurAircraft++; 6681 aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type; 6682 AQuantity[aircraft]++; 6683 AScan |= (1L << aircraft); 6684 if (Session.Type == GAME_INTERNET) { 6685 AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID); 6686 } 6687 break; 6688 6689 case RTTI_INFANTRY: 6690 CurInfantry++; 6691 infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type; 6692 if (!((InfantryClass *)techno)->IsTechnician) { 6693 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6694 quant = infantry; 6695 if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT; 6696 IQuantity[quant]++; 6697 #else 6698 IQuantity[infantry]++; 6699 #endif 6700 if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) { 6701 InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID); 6702 } 6703 IScan |= (1L << infantry); 6704 } 6705 break; 6706 6707 case RTTI_UNIT: 6708 CurUnits++; 6709 unit = ((UnitTypeClass const &)techno->Class_Of()).Type; 6710 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6711 quant = unit; 6712 if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT; 6713 UQuantity[quant]++; 6714 #else 6715 UQuantity[unit]++; 6716 #endif 6717 UScan |= (1L << unit); 6718 if (Session.Type == GAME_INTERNET) { 6719 UnitTotals->Increment_Unit_Total(techno->Class_Of().ID); 6720 } 6721 break; 6722 6723 case RTTI_VESSEL: 6724 CurVessels++; 6725 vessel = ((VesselTypeClass const &)techno->Class_Of()).Type; 6726 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6727 quant = vessel; 6728 if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT; 6729 VQuantity[quant]++; 6730 #else 6731 VQuantity[vessel]++; 6732 #endif 6733 VScan |= (1L << vessel); 6734 if (Session.Type == GAME_INTERNET) { 6735 VesselTotals->Increment_Unit_Total(techno->Class_Of().ID); 6736 } 6737 break; 6738 6739 default: 6740 break; 6741 } 6742 } 6743 6744 6745 /*********************************************************************************************** 6746 * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * 6747 * * 6748 * Use this routine to fetch a pointer to the variable that holds the number of factories * 6749 * that can produce the specified object type. This is a helper routine used when * 6750 * examining the number of factories as well as adjusting their number. * 6751 * * 6752 * INPUT: rtti -- The RTTI of the object that could be produced. * 6753 * * 6754 * OUTPUT: Returns with the number of factories owned by this house that could produce the * 6755 * object of the type specified. * 6756 * * 6757 * WARNINGS: none * 6758 * * 6759 * HISTORY: * 6760 * 07/30/1996 JLB : Created. * 6761 *=============================================================================================*/ 6762 int * HouseClass::Factory_Counter(RTTIType rtti) 6763 { 6764 switch (rtti) { 6765 case RTTI_UNITTYPE: 6766 case RTTI_UNIT: 6767 return(&UnitFactories); 6768 6769 case RTTI_VESSELTYPE: 6770 case RTTI_VESSEL: 6771 return(&VesselFactories); 6772 6773 case RTTI_AIRCRAFTTYPE: 6774 case RTTI_AIRCRAFT: 6775 return(&AircraftFactories); 6776 6777 case RTTI_INFANTRYTYPE: 6778 case RTTI_INFANTRY: 6779 return(&InfantryFactories); 6780 6781 case RTTI_BUILDINGTYPE: 6782 case RTTI_BUILDING: 6783 return(&BuildingFactories); 6784 6785 default: 6786 break; 6787 } 6788 return(NULL); 6789 } 6790 6791 6792 /*********************************************************************************************** 6793 * HouseClass::Active_Remove -- Remove this object from active duty for this house. * 6794 * * 6795 * This routine will recognize the specified object as having been removed from active * 6796 * duty. * 6797 * * 6798 * INPUT: techno -- Pointer to the object to remove from active duty. * 6799 * * 6800 * OUTPUT: none * 6801 * * 6802 * WARNINGS: none * 6803 * * 6804 * HISTORY: * 6805 * 07/16/1996 JLB : Created. * 6806 *=============================================================================================*/ 6807 void HouseClass::Active_Remove(TechnoClass const * techno) 6808 { 6809 if (techno == NULL) return; 6810 6811 if (techno->What_Am_I() == RTTI_BUILDING) { 6812 int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); 6813 if (fptr != NULL) { 6814 *fptr = *fptr - 1; 6815 } 6816 } 6817 } 6818 6819 6820 /*********************************************************************************************** 6821 * HouseClass::Active_Add -- Add an object to active duty for this house. * 6822 * * 6823 * This routine will recognize the specified object as having entered active duty. Any * 6824 * abilities granted to the house by that object are now available. * 6825 * * 6826 * INPUT: techno -- Pointer to the object that is entering active duty. * 6827 * * 6828 * OUTPUT: none * 6829 * * 6830 * WARNINGS: none * 6831 * * 6832 * HISTORY: * 6833 * 07/16/1996 JLB : Created. * 6834 *=============================================================================================*/ 6835 void HouseClass::Active_Add(TechnoClass const * techno) 6836 { 6837 if (techno == NULL) return; 6838 6839 if (techno->What_Am_I() == RTTI_BUILDING) { 6840 int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); 6841 if (fptr != NULL) { 6842 *fptr = *fptr + 1; 6843 } 6844 } 6845 } 6846 6847 6848 /*********************************************************************************************** 6849 * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * 6850 * * 6851 * This routine will determine what zone the specified coordinate lies in with respect to * 6852 * this house's base. A location that is too distant from the base, even though it might * 6853 * be a building, is not considered part of the base and returns ZONE_NONE. * 6854 * * 6855 * INPUT: coord -- The coordinate to examine. * 6856 * * 6857 * OUTPUT: Returns with the base zone that the specified coordinate lies in. * 6858 * * 6859 * WARNINGS: none * 6860 * * 6861 * HISTORY: * 6862 * 10/02/1995 JLB : Created. * 6863 *=============================================================================================*/ 6864 ZoneType HouseClass::Which_Zone(COORDINATE coord) const 6865 { 6866 assert(Houses.ID(this) == ID); 6867 6868 if (coord == 0) return(ZONE_NONE); 6869 6870 int distance = Distance(Center, coord); 6871 if (distance <= Radius) return(ZONE_CORE); 6872 if (distance > Radius*4) return(ZONE_NONE); 6873 6874 DirType facing = Direction(Center, coord); 6875 if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH); 6876 if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST); 6877 if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH); 6878 return(ZONE_WEST); 6879 } 6880 6881 6882 /*********************************************************************************************** 6883 * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * 6884 * * 6885 * Use this routine to determine what zone the specified object lies in. * 6886 * * 6887 * INPUT: object -- Pointer to the object that will be checked for zone occupation. * 6888 * * 6889 * OUTPUT: Returns with the base zone that the object lies in. For objects that are too * 6890 * distant from the center of the base, ZONE_NONE is returned. * 6891 * * 6892 * WARNINGS: none * 6893 * * 6894 * HISTORY: * 6895 * 10/02/1995 JLB : Created. * 6896 *=============================================================================================*/ 6897 ZoneType HouseClass::Which_Zone(ObjectClass const * object) const 6898 { 6899 assert(Houses.ID(this) == ID); 6900 6901 if (!object) return(ZONE_NONE); 6902 return(Which_Zone(object->Center_Coord())); 6903 } 6904 6905 6906 /*********************************************************************************************** 6907 * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * 6908 * * 6909 * This routine is used to determine what base zone the specified cell is in. * 6910 * * 6911 * INPUT: cell -- The cell to examine. * 6912 * * 6913 * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far * 6914 * away. * 6915 * * 6916 * WARNINGS: none * 6917 * * 6918 * HISTORY: * 6919 * 10/02/1995 JLB : Created. * 6920 *=============================================================================================*/ 6921 ZoneType HouseClass::Which_Zone(CELL cell) const 6922 { 6923 assert(Houses.ID(this) == ID); 6924 6925 return(Which_Zone(Cell_Coord(cell))); 6926 } 6927 6928 6929 /*********************************************************************************************** 6930 * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * 6931 * * 6932 * This routine will go through all game objects and reset the existence bits for the * 6933 * owning house. This method ensures that if the object exists, then the corresponding * 6934 * existence bit is also set. * 6935 * * 6936 * INPUT: none * 6937 * * 6938 * OUTPUT: none * 6939 * * 6940 * WARNINGS: none * 6941 * * 6942 * HISTORY: * 6943 * 10/02/1995 JLB : Created. * 6944 *=============================================================================================*/ 6945 void HouseClass::Recalc_Attributes(void) 6946 { 6947 /* 6948 ** Clear out all tracking values that will be filled in by this 6949 ** routine. This allows the filling in process to not worry about 6950 ** old existing values. 6951 */ 6952 int index; 6953 for (index = 0; index < Houses.Count(); index++) { 6954 HouseClass * house = Houses.Ptr(index); 6955 6956 if (house != NULL) { 6957 house->BScan = 0; 6958 house->ActiveBScan = 0; 6959 house->IScan = 0; 6960 house->ActiveIScan = 0; 6961 house->UScan = 0; 6962 house->ActiveUScan = 0; 6963 house->AScan = 0; 6964 house->ActiveAScan = 0; 6965 house->VScan = 0; 6966 house->ActiveVScan = 0; 6967 } 6968 } 6969 6970 /* 6971 ** A second pass through the sentient objects is required so that the appropriate scan 6972 ** bits will be set for the owner house. 6973 */ 6974 for (index = 0; index < Units.Count(); index++) { 6975 UnitClass const * unit = Units.Ptr(index); 6976 unit->House->UScan |= (1L << unit->Class->Type); 6977 if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { 6978 if (!unit->IsInLimbo) { 6979 unit->House->ActiveUScan |= (1L << unit->Class->Type); 6980 } 6981 } 6982 } 6983 for (index = 0; index < Infantry.Count(); index++) { 6984 InfantryClass const * infantry = Infantry.Ptr(index); 6985 infantry->House->IScan |= (1L << infantry->Class->Type); 6986 if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { 6987 if (!infantry->IsInLimbo) { 6988 infantry->House->ActiveIScan |= (1L << infantry->Class->Type); 6989 infantry->House->OldIScan |= (1L << infantry->Class->Type); 6990 } 6991 } 6992 } 6993 for (index = 0; index < Aircraft.Count(); index++) { 6994 AircraftClass const * aircraft = Aircraft.Ptr(index); 6995 aircraft->House->AScan |= (1L << aircraft->Class->Type); 6996 if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { 6997 if (!aircraft->IsInLimbo) { 6998 aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type); 6999 aircraft->House->OldAScan |= (1L << aircraft->Class->Type); 7000 } 7001 } 7002 } 7003 for (index = 0; index < Buildings.Count(); index++) { 7004 BuildingClass const * building = Buildings.Ptr(index); 7005 if (building->Class->Type < 32) { 7006 building->House->BScan |= (1L << building->Class->Type); 7007 if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { 7008 if (!building->IsInLimbo) { 7009 building->House->ActiveBScan |= (1L << building->Class->Type); 7010 building->House->OldBScan |= (1L << building->Class->Type); 7011 } 7012 } 7013 } 7014 } 7015 for (index = 0; index < Vessels.Count(); index++) { 7016 VesselClass const * vessel = Vessels.Ptr(index); 7017 vessel->House->VScan |= (1L << vessel->Class->Type); 7018 if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) { 7019 if (!vessel->IsInLimbo) { 7020 vessel->House->ActiveVScan |= (1L << vessel->Class->Type); 7021 vessel->House->OldVScan |= (1L << vessel->Class->Type); 7022 } 7023 } 7024 } 7025 } 7026 7027 7028 /*********************************************************************************************** 7029 * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * 7030 * * 7031 * This routine is used to find the cell that is closest to the center point of the * 7032 * zone specified. Typical use of this routine is for building and unit placement so that * 7033 * they can "cover" the specified zone. * 7034 * * 7035 * INPUT: zone -- The zone that the center point is to be returned. * 7036 * * 7037 * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. * 7038 * * 7039 * WARNINGS: none * 7040 * * 7041 * HISTORY: * 7042 * 10/02/1995 JLB : Created. * 7043 *=============================================================================================*/ 7044 CELL HouseClass::Zone_Cell(ZoneType zone) const 7045 { 7046 assert(Houses.ID(this) == ID); 7047 7048 switch (zone) { 7049 case ZONE_CORE: 7050 return(Coord_Cell(Center)); 7051 7052 case ZONE_NORTH: 7053 return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3))); 7054 7055 case ZONE_EAST: 7056 return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3))); 7057 7058 case ZONE_WEST: 7059 return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3))); 7060 7061 case ZONE_SOUTH: 7062 return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3))); 7063 7064 default: 7065 break; 7066 } 7067 return(0); 7068 } 7069 7070 7071 /*********************************************************************************************** 7072 * HouseClass::Where_To_Go -- Determines where the object should go and wait. * 7073 * * 7074 * This function is called for every new unit produced or delivered in order to determine * 7075 * where the unit should "hang out" to await further orders. The best area for the * 7076 * unit to loiter is returned as a cell location. * 7077 * * 7078 * INPUT: object -- Pointer to the object that needs to know where to go. * 7079 * * 7080 * OUTPUT: Returns with the cell that the unit should move to. * 7081 * * 7082 * WARNINGS: none * 7083 * * 7084 * HISTORY: * 7085 * 10/02/1995 JLB : Created. * 7086 * 11/04/1996 JLB : Simplified to use helper functions * 7087 *=============================================================================================*/ 7088 CELL HouseClass::Where_To_Go(FootClass const * object) const 7089 { 7090 assert(Houses.ID(this) == ID); 7091 assert(object != NULL); 7092 7093 ZoneType zone; // The zone that the object should go to. 7094 if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) { 7095 zone = ZONE_CORE; 7096 } else { 7097 zone = Random_Pick(ZONE_NORTH, ZONE_WEST); 7098 } 7099 7100 CELL cell = Random_Cell_In_Zone(zone); 7101 assert(cell != 0); 7102 7103 return(Map.Nearby_Location(cell, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL)); 7104 } 7105 7106 7107 /*********************************************************************************************** 7108 * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * 7109 * * 7110 * This routine is used to find targets out in the field and away from base defense. * 7111 * Typical of this would be the attack helicopters and the roving attack bands of * 7112 * hunter killers. * 7113 * * 7114 * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. * 7115 * * 7116 * OUTPUT: Returns with a suitable target to attack. * 7117 * * 7118 * WARNINGS: none * 7119 * * 7120 * HISTORY: * 7121 * 10/12/1995 JLB : Created. * 7122 *=============================================================================================*/ 7123 TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const 7124 { 7125 assert(Houses.ID(this) == ID); 7126 7127 UnitClass * best = 0; 7128 int value = 0; 7129 7130 for (int index = 0; index < Units.Count(); index++) { 7131 UnitClass * unit = Units.Ptr(index); 7132 7133 if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) { 7134 int val = Distance(coord, unit->Center_Coord()); 7135 7136 if (unit->Anti_Air()) val *= 2; 7137 7138 if (*unit == UNIT_HARVESTER) val /= 2; 7139 7140 if (value == 0 || val < value) { 7141 value = val; 7142 best = unit; 7143 } 7144 } 7145 } 7146 if (best) { 7147 return(best->As_Target()); 7148 } 7149 return(TARGET_NONE); 7150 } 7151 7152 7153 /*********************************************************************************************** 7154 * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * 7155 * * 7156 * Call this routine to fetch the total quantity of aircraft of the type specified that is * 7157 * owned by this house. * 7158 * * 7159 * INPUT: aircraft -- The aircraft type to check the quantity of. * 7160 * * 7161 * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this * 7162 * house. * 7163 * * 7164 * WARNINGS: none * 7165 * * 7166 * HISTORY: * 7167 * 07/09/1996 JLB : Created. * 7168 *=============================================================================================*/ 7169 int HouseClass::Get_Quantity(AircraftType aircraft) 7170 { 7171 return(AQuantity[aircraft]); 7172 } 7173 7174 7175 /*********************************************************************************************** 7176 * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * 7177 * * 7178 * This is the counterpart to the Set_Factory function. It will return with a factory * 7179 * pointer that is associated with the object type specified. * 7180 * * 7181 * INPUT: rtti -- The RTTI of the object type to find the factory for. * 7182 * * 7183 * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the * 7184 * object type specified. * 7185 * * 7186 * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy * 7187 * producing another unit of that category. * 7188 * * 7189 * HISTORY: * 7190 * 07/09/1996 JLB : Created. * 7191 *=============================================================================================*/ 7192 FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const 7193 { 7194 int factory_index = -1; 7195 7196 switch (rtti) { 7197 case RTTI_INFANTRY: 7198 case RTTI_INFANTRYTYPE: 7199 factory_index = InfantryFactory; 7200 break; 7201 7202 case RTTI_UNIT: 7203 case RTTI_UNITTYPE: 7204 factory_index = UnitFactory; 7205 break; 7206 7207 case RTTI_BUILDING: 7208 case RTTI_BUILDINGTYPE: 7209 factory_index = BuildingFactory; 7210 break; 7211 7212 case RTTI_AIRCRAFT: 7213 case RTTI_AIRCRAFTTYPE: 7214 factory_index = AircraftFactory; 7215 break; 7216 7217 case RTTI_VESSEL: 7218 case RTTI_VESSELTYPE: 7219 factory_index = VesselFactory; 7220 break; 7221 7222 default: 7223 factory_index = -1; 7224 break; 7225 } 7226 7227 /* 7228 ** Fetch the actual pointer to the factory object. If there is 7229 ** no object factory that matches the specified rtti type, then 7230 ** null is returned. 7231 */ 7232 if (factory_index != -1) { 7233 return(Factories.Raw_Ptr(factory_index)); 7234 } 7235 return(NULL); 7236 } 7237 7238 7239 /*********************************************************************************************** 7240 * HouseClass::Set_Factory -- Assign specified factory to house tracking. * 7241 * * 7242 * Call this routine when a factory has been created and it now must be passed on to the * 7243 * house for tracking purposes. The house maintains several factory pointers and this * 7244 * routine will ensure that the factory pointer gets stored correctly. * 7245 * * 7246 * INPUT: rtti -- The RTTI of the object the factory it to manufacture. * 7247 * * 7248 * factory -- The factory object pointer. * 7249 * * 7250 * OUTPUT: none * 7251 * * 7252 * WARNINGS: none * 7253 * * 7254 * HISTORY: * 7255 * 07/09/1996 JLB : Created. * 7256 *=============================================================================================*/ 7257 void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory) 7258 { 7259 int * factory_index = 0; 7260 7261 assert(rtti != RTTI_NONE); 7262 7263 switch (rtti) { 7264 case RTTI_UNIT: 7265 case RTTI_UNITTYPE: 7266 factory_index = &UnitFactory; 7267 break; 7268 7269 case RTTI_INFANTRY: 7270 case RTTI_INFANTRYTYPE: 7271 factory_index = &InfantryFactory; 7272 break; 7273 7274 case RTTI_VESSEL: 7275 case RTTI_VESSELTYPE: 7276 factory_index = &VesselFactory; 7277 break; 7278 7279 case RTTI_BUILDING: 7280 case RTTI_BUILDINGTYPE: 7281 factory_index = &BuildingFactory; 7282 break; 7283 7284 case RTTI_AIRCRAFT: 7285 case RTTI_AIRCRAFTTYPE: 7286 factory_index = &AircraftFactory; 7287 break; 7288 } 7289 7290 assert(factory_index != NULL); 7291 7292 /* 7293 ** Assign the factory to the appropriate slot. For the case of clearing 7294 ** the factory out, then -1 is assigned. 7295 */ 7296 if (factory != NULL) { 7297 *factory_index = factory->ID; 7298 } else { 7299 *factory_index = -1; 7300 } 7301 } 7302 7303 7304 /*********************************************************************************************** 7305 * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * 7306 * * 7307 * This routine will count the number of factories owned by this house that can build * 7308 * objects of the specified type. * 7309 * * 7310 * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. * 7311 * * 7312 * OUTPUT: Returns with the number of factories that can build the object type specified. * 7313 * * 7314 * WARNINGS: none * 7315 * * 7316 * HISTORY: * 7317 * 07/30/1996 JLB : Created. * 7318 *=============================================================================================*/ 7319 int HouseClass::Factory_Count(RTTIType rtti) const 7320 { 7321 int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti); 7322 if (ptr != NULL) { 7323 return(*ptr); 7324 } 7325 return(0); 7326 } 7327 7328 7329 /*********************************************************************************************** 7330 * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * 7331 * * 7332 * This will return the total number of buildings of that type owned by this house. * 7333 * * 7334 * INPUT: building -- The building type to check. * 7335 * * 7336 * OUTPUT: Returns with the number of buildings of that type owned by this house. * 7337 * * 7338 * WARNINGS: none * 7339 * * 7340 * HISTORY: * 7341 * 07/09/1996 JLB : Created. * 7342 *=============================================================================================*/ 7343 int HouseClass::Get_Quantity(StructType building) 7344 { 7345 return(BQuantity[building]); 7346 } 7347 7348 7349 /*********************************************************************************************** 7350 * HouseClass::Read_INI -- Reads house specific data from INI. * 7351 * * 7352 * This routine reads the house specific data for a particular * 7353 * scenario from the scenario INI file. Typical data includes starting * 7354 * credits, maximum unit count, etc. * 7355 * * 7356 * INPUT: buffer -- Pointer to loaded scenario INI file. * 7357 * * 7358 * OUTPUT: none * 7359 * * 7360 * WARNINGS: none * 7361 * * 7362 * HISTORY: * 7363 * 05/24/1994 JLB : Created. * 7364 * 05/18/1995 JLB : Creates all houses. * 7365 *=============================================================================================*/ 7366 void HouseClass::Read_INI(CCINIClass & ini) 7367 { 7368 HouseClass * p; // Pointer to current player data. 7369 char const * hname; // Pointer to house name. 7370 7371 for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { 7372 hname = HouseTypeClass::As_Reference(index).IniName; 7373 7374 p = new HouseClass(index); 7375 p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario); 7376 p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding); 7377 p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit); 7378 p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry); 7379 p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel); 7380 if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit; 7381 p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100; 7382 p->Credits = p->Control.InitialCredits; 7383 7384 int iq = ini.Get_Int(hname, "IQ", 0); 7385 if (iq > Rule.MaxIQ) iq = 1; 7386 p->IQ = p->Control.IQ = iq; 7387 7388 p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH); 7389 p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false); 7390 7391 int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL)); 7392 p->Make_Ally(index); 7393 p->Make_Ally(HOUSE_NEUTRAL); 7394 for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 7395 if ((owners & (1 << h)) != 0) { 7396 p->Make_Ally(h); 7397 } 7398 } 7399 } 7400 } 7401 7402 7403 /*********************************************************************************************** 7404 * HouseClass::Write_INI -- Writes the house data to the INI database. * 7405 * * 7406 * This routine will write out all data necessary to recreate it in anticipation of a * 7407 * new scenario. All houses (that are active) will have their scenario type data written * 7408 * out. * 7409 * * 7410 * INPUT: ini -- Reference to the INI database to write the data to. * 7411 * * 7412 * OUTPUT: none * 7413 * * 7414 * WARNINGS: none * 7415 * * 7416 * HISTORY: * 7417 * 07/09/1996 JLB : Created. * 7418 *=============================================================================================*/ 7419 void HouseClass::Write_INI(CCINIClass & ini) 7420 { 7421 /* 7422 ** The identity house control object. Only if the house value differs from the 7423 ** identity, will the data be written out. 7424 */ 7425 HouseStaticClass control; 7426 7427 for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { 7428 HouseClass * p = As_Pointer(i); 7429 7430 if (p != NULL) { 7431 char const * name = p->Class->IniName; 7432 7433 ini.Clear(name); 7434 if (i >= HOUSE_MULTI1) continue; 7435 7436 if (p->Control.InitialCredits != control.InitialCredits) { 7437 ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100)); 7438 } 7439 7440 if (p->Control.Edge != control.Edge) { 7441 ini.Put_SourceType(name, "Edge", p->Control.Edge); 7442 } 7443 7444 if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) { 7445 ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit); 7446 } 7447 7448 if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) { 7449 ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry); 7450 } 7451 7452 if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) { 7453 ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding); 7454 } 7455 7456 if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) { 7457 ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel); 7458 } 7459 7460 if (p->Control.TechLevel != control.TechLevel) { 7461 ini.Put_Int(name, "TechLevel", p->Control.TechLevel); 7462 } 7463 7464 if (p->Control.IQ != control.IQ) { 7465 ini.Put_Int(name, "IQ", p->Control.IQ); 7466 } 7467 7468 if (p->IsPlayerControl != false && p != PlayerPtr) { 7469 ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl); 7470 } 7471 7472 ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL))); 7473 } 7474 } 7475 } 7476 7477 7478 /*********************************************************************************************** 7479 * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * 7480 * * 7481 * This routine will examine the current yak and mig situation verses airfields. If there * 7482 * are equal aircraft to airfields, then this routine will return TRUE. * 7483 * * 7484 * INPUT: none * 7485 * * 7486 * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? * 7487 * * 7488 * WARNINGS: none * 7489 * * 7490 * HISTORY: * 7491 * 09/23/1996 JLB : Created. * 7492 *=============================================================================================*/ 7493 bool HouseClass::Is_No_YakMig(void) const 7494 { 7495 int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG]; 7496 7497 /* 7498 ** Adjust the quantity down one if there is an aircraft in production. This will 7499 ** allow production to resume after being held. 7500 */ 7501 FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT); 7502 if (factory != NULL && factory->Get_Object() != NULL) { 7503 AircraftClass const * air = (AircraftClass const *)factory->Get_Object(); 7504 if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) { 7505 quantity -= 1; 7506 } 7507 } 7508 7509 if (quantity >= BQuantity[STRUCT_AIRSTRIP]) { 7510 return(true); 7511 } 7512 return(false); 7513 } 7514 7515 7516 /*********************************************************************************************** 7517 * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * 7518 * * 7519 * This is a special hack check routine to see if the object type and id specified is * 7520 * prevented from being produced. The Yak and the Mig are so prevented if there would be * 7521 * insufficient airfields for them to land upon. * 7522 * * 7523 * INPUT: rtti -- The RTTI type of the value specified. * 7524 * * 7525 * value -- The type number (according to the RTTI type specified). * 7526 * * 7527 * OUTPUT: bool; Is production of this object prohibited? * 7528 * * 7529 * WARNINGS: none * 7530 * * 7531 * HISTORY: * 7532 * 09/23/1996 JLB : Created. * 7533 *=============================================================================================*/ 7534 bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const 7535 { 7536 if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) { 7537 return(Is_No_YakMig()); 7538 } 7539 return(false); 7540 } 7541 7542 7543 /*********************************************************************************************** 7544 * HouseClass::Fire_Sale -- Cause all buildings to be sold. * 7545 * * 7546 * This routine will sell back all buildings owned by this house. * 7547 * * 7548 * INPUT: none * 7549 * * 7550 * OUTPUT: bool; Was a fire sale performed? * 7551 * * 7552 * WARNINGS: none * 7553 * * 7554 * HISTORY: * 7555 * 09/23/1996 JLB : Created. * 7556 *=============================================================================================*/ 7557 bool HouseClass::Fire_Sale(void) 7558 { 7559 if (CurBuildings > 0) { 7560 for (int index = 0; index < Buildings.Count(); index++) { 7561 BuildingClass * b = Buildings.Ptr(index); 7562 7563 if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) { 7564 b->Sell_Back(1); 7565 } 7566 } 7567 return(true); 7568 } 7569 return(false); 7570 } 7571 7572 7573 /*********************************************************************************************** 7574 * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * 7575 * * 7576 * This routine will cause all combatants of this house to go into hunt mode. The effect of * 7577 * this is to throw everything this house has to muster at the enemies of this house. * 7578 * * 7579 * INPUT: none * 7580 * * 7581 * OUTPUT: none * 7582 * * 7583 * WARNINGS: none * 7584 * * 7585 * HISTORY: * 7586 * 09/23/1996 JLB : Created. * 7587 * 10/02/1996 JLB : Handles aircraft too. * 7588 *=============================================================================================*/ 7589 void HouseClass::Do_All_To_Hunt(void) const 7590 { 7591 int index; 7592 7593 for (index = 0; index < Units.Count(); index++) { 7594 UnitClass * unit = Units.Ptr(index); 7595 7596 if (unit->House == this && unit->IsDown && !unit->IsInLimbo) { 7597 if (unit->Team) unit->Team->Remove(unit); 7598 unit->Assign_Mission(MISSION_HUNT); 7599 } 7600 } 7601 7602 for (index = 0; index < Infantry.Count(); index++) { 7603 InfantryClass * infantry = Infantry.Ptr(index); 7604 7605 if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) { 7606 if (infantry->Team) infantry->Team->Remove(infantry); 7607 infantry->Assign_Mission(MISSION_HUNT); 7608 } 7609 } 7610 7611 for (index = 0; index < Vessels.Count(); index++) { 7612 VesselClass * vessel = Vessels.Ptr(index); 7613 7614 if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) { 7615 if (vessel->Team) vessel->Team->Remove(vessel); 7616 vessel->Assign_Mission(MISSION_HUNT); 7617 } 7618 } 7619 7620 for (index = 0; index < Aircraft.Count(); index++) { 7621 AircraftClass * aircraft = Aircraft.Ptr(index); 7622 7623 if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) { 7624 if (aircraft->Team) aircraft->Team->Remove(aircraft); 7625 aircraft->Assign_Mission(MISSION_HUNT); 7626 } 7627 } 7628 } 7629 7630 7631 /*********************************************************************************************** 7632 * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * 7633 * * 7634 * Use this routine to determine if this house is legally allowed to ally with the * 7635 * house specified. There are many reason why an alliance is not allowed. Typically, this * 7636 * is when there would be no more opponents left to fight or if this house has been * 7637 * defeated. * 7638 * * 7639 * INPUT: house -- The house that alliance with is desired. * 7640 * * 7641 * OUTPUT: bool; Is alliance with the house specified prohibited? * 7642 * * 7643 * WARNINGS: none * 7644 * * 7645 * HISTORY: * 7646 * 09/23/1996 JLB : Created. * 7647 *=============================================================================================*/ 7648 bool HouseClass::Is_Allowed_To_Ally(HousesType house) const 7649 { 7650 /* 7651 ** Is not allowed to ally with a house that is patently invalid, such 7652 ** as one that is illegally defined. 7653 */ 7654 if (house == HOUSE_NONE) { 7655 return(false); 7656 } 7657 7658 /* 7659 ** One cannot ally twice with the same house. 7660 */ 7661 if (Is_Ally(house)) { 7662 return(false); 7663 } 7664 7665 /* 7666 ** If the scenario is being set up, then alliances are always 7667 ** allowed. No further checking is required. 7668 */ 7669 if (ScenarioInit) { 7670 return(true); 7671 } 7672 7673 /* 7674 ** Alliances (outside of scneario init time) are allowed only if 7675 ** this is a multiplayer game. Otherwise, they are prohibited. 7676 */ 7677 if (Session.Type == GAME_NORMAL) { 7678 return(false); 7679 } 7680 7681 /* 7682 ** When the house is defeated, it can no longer make alliances. 7683 */ 7684 if (IsDefeated) { 7685 return(false); 7686 } 7687 7688 #ifdef FIXIT_VERSION_3 7689 // Fix to prevent ally with computer. 7690 if ( !HouseClass::As_Pointer(house)->IsHuman) { 7691 return(false); 7692 } 7693 #else // FIXIT_VERSION_3 7694 #ifdef FIXIT_NO_COMP_ALLY 7695 // Fix to prevent ally with computer. 7696 if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) { 7697 return(false); 7698 } 7699 #endif 7700 #endif // FIXIT_VERSION_3 7701 7702 /* 7703 ** Count the number of active houses in the game as well as the 7704 ** number of existing allies with this house. 7705 */ 7706 int housecount = 0; 7707 int allycount = 0; 7708 for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { 7709 HouseClass * hptr = HouseClass::As_Pointer(house2); 7710 if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) { 7711 housecount++; 7712 if (Is_Ally(hptr)) { 7713 allycount++; 7714 } 7715 } 7716 } 7717 7718 /* 7719 ** Alliance is not allowed if there wouldn't be any enemies left to 7720 ** fight. 7721 */ 7722 if (housecount == allycount+1) { 7723 return(false); 7724 } 7725 7726 return(true); 7727 } 7728 7729 7730 /*********************************************************************************************** 7731 * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * 7732 * * 7733 * This routine will cause the computer players to become suspicious of the human * 7734 * players and thus the computer players will band together in order to defeat the * 7735 * human players. * 7736 * * 7737 * INPUT: none * 7738 * * 7739 * OUTPUT: none * 7740 * * 7741 * WARNINGS: none * 7742 * * 7743 * HISTORY: * 7744 * 09/23/1996 JLB : Created. * 7745 *=============================================================================================*/ 7746 void HouseClass::Computer_Paranoid(void) 7747 { 7748 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { // Re-enable this for multiplayer if we support classic team/ally mode. ST - 10/29/2019 7749 7750 /* 7751 ** Loop through every computer controlled house and make allies with all other computer 7752 ** controlled houses and then make enemies with all other human controlled houses. 7753 */ 7754 for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { 7755 HouseClass * hptr = HouseClass::As_Pointer(house); 7756 if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) { 7757 hptr->IsParanoid = true; 7758 7759 /* 7760 ** Break alliance with every human it is allied with and make friends with 7761 ** any other computer players. 7762 */ 7763 for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { 7764 HouseClass * hptr2 = HouseClass::As_Pointer(house2); 7765 if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) { 7766 if (hptr2->IsHuman) { 7767 hptr->Make_Enemy(house2); 7768 } else { 7769 hptr->Make_Ally(house2); 7770 } 7771 } 7772 } 7773 } 7774 } 7775 } 7776 } 7777 7778 7779 /*********************************************************************************************** 7780 * HouseClass::Adjust_Power -- Adjust the power value of the house. * 7781 * * 7782 * This routine will update the power output value of the house. It will cause any buildgins* 7783 * that need to be redrawn to do so. * 7784 * * 7785 * INPUT: adjust -- The amount to adjust the power output value. * 7786 * * 7787 * OUTPUT: none * 7788 * * 7789 * WARNINGS: none * 7790 * * 7791 * HISTORY: * 7792 * 11/01/1996 BWG : Created. * 7793 *=============================================================================================*/ 7794 void HouseClass::Adjust_Power(int adjust) 7795 { 7796 Power += adjust; 7797 7798 Update_Spied_Power_Plants(); 7799 } 7800 7801 7802 /*********************************************************************************************** 7803 * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * 7804 * * 7805 * This routine will update the drain value of the house. It will cause any buildings that * 7806 * need to be redraw to do so. * 7807 * * 7808 * INPUT: adjust -- The amount to adjust the drain (positive means more drain). * 7809 * * 7810 * OUTPUT: none * 7811 * * 7812 * WARNINGS: none * 7813 * * 7814 * HISTORY: * 7815 * 11/01/1996 BWG : Created. * 7816 *=============================================================================================*/ 7817 void HouseClass::Adjust_Drain(int adjust) 7818 { 7819 Drain += adjust; 7820 Update_Spied_Power_Plants(); 7821 } 7822 7823 7824 /*********************************************************************************************** 7825 * HouseClass::Update_Spied_Power_Plants -- Redraw power graphs on spied-upon power plants. * 7826 * * 7827 * INPUT: none * 7828 * * 7829 * OUTPUT: none * 7830 * * 7831 * WARNINGS: none * 7832 * * 7833 * HISTORY: * 7834 * 10/11/1996 BWG : Created. * 7835 *=============================================================================================*/ 7836 void HouseClass::Update_Spied_Power_Plants(void) 7837 { 7838 int count = CurrentObject.Count(); 7839 if (count) { 7840 for (int index = 0; index < count; index++) { 7841 ObjectClass const * tech = CurrentObject[index]; 7842 if (tech && tech->What_Am_I()==RTTI_BUILDING) { 7843 BuildingClass *bldg = (BuildingClass *)tech; 7844 if (!bldg->IsOwnedByPlayer && *bldg == STRUCT_POWER || *bldg == STRUCT_ADVANCED_POWER) { 7845 if ( bldg->Spied_By() & (1<<(PlayerPtr->Class->House)) ) { 7846 bldg->Mark(MARK_CHANGE); 7847 } 7848 } 7849 } 7850 } 7851 } 7852 } 7853 7854 7855 /*********************************************************************************************** 7856 * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * 7857 * * 7858 * Use this routine to determine where the specified object should go if it were to go * 7859 * some random (but legal) location within the zone specified. * 7860 * * 7861 * INPUT: techno -- The object that is desirous of going into the zone specified. * 7862 * * 7863 * zone -- The zone to find a location within. * 7864 * * 7865 * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If * 7866 * no valid location could be found, then 0 is returned. * 7867 * * 7868 * WARNINGS: none * 7869 * * 7870 * HISTORY: * 7871 * 11/01/1996 JLB : Created. * 7872 * 11/04/1996 JLB : Not so strict on zone requirement. * 7873 *=============================================================================================*/ 7874 CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const 7875 { 7876 if (techno == NULL) return(0); 7877 7878 int bestval = -1; 7879 int bestcell = 0; 7880 TechnoTypeClass const * ttype = techno->Techno_Type_Class(); 7881 7882 /* 7883 ** Pick a random location within the zone specified. 7884 */ 7885 CELL trycell = Random_Cell_In_Zone(zone); 7886 7887 short const * list = NULL; 7888 if (techno->What_Am_I() == RTTI_BUILDING) { 7889 list = techno->Occupy_List(true); 7890 } 7891 7892 /* 7893 ** Find a legal placement position as close as possible to the picked location while still 7894 ** remaining within the zone. 7895 */ 7896 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 7897 // if (Map.In_Radar(cell)) { 7898 if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) { 7899 bool ok = ttype->Legal_Placement(cell); 7900 7901 /* 7902 ** Another (adjacency) check is required for buildings. 7903 */ 7904 if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) { 7905 ok = false; 7906 } 7907 7908 if (ok) { 7909 int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell)); 7910 if (bestval == -1 || dist < bestval) { 7911 bestval = dist; 7912 bestcell = cell; 7913 } 7914 } 7915 } 7916 } 7917 7918 /* 7919 ** Return the best location to move to. 7920 */ 7921 return(bestcell); 7922 } 7923 7924 7925 /*********************************************************************************************** 7926 * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * 7927 * * 7928 * This routine will pick a random cell within the zone specified. The pick will be * 7929 * clipped to the map edge when necessary. * 7930 * * 7931 * INPUT: zone -- The zone to pick a cell from. * 7932 * * 7933 * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the * 7934 * map, then a cell in the core zone is returned instead. * 7935 * * 7936 * WARNINGS: none * 7937 * * 7938 * HISTORY: * 7939 * 11/04/1996 JLB : Created. * 7940 *=============================================================================================*/ 7941 CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const 7942 { 7943 COORDINATE coord = 0; 7944 int maxdist = 0; 7945 switch (zone) { 7946 case ZONE_CORE: 7947 coord = Coord_Scatter(Center, Random_Pick(0, Radius), true); 7948 break; 7949 7950 case ZONE_NORTH: 7951 maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H); 7952 if (maxdist < 0) break; 7953 coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); 7954 break; 7955 7956 case ZONE_EAST: 7957 maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W); 7958 if (maxdist < 0) break; 7959 coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); 7960 break; 7961 7962 case ZONE_SOUTH: 7963 maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H); 7964 if (maxdist < 0) break; 7965 coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); 7966 break; 7967 7968 case ZONE_WEST: 7969 maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W); 7970 if (maxdist < 0) break; 7971 coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); 7972 break; 7973 } 7974 7975 /* 7976 ** Double check that the location is valid and if so, convert it into a cell 7977 ** number. 7978 */ 7979 CELL cell; 7980 if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) { 7981 if (zone == ZONE_CORE) { 7982 7983 /* 7984 ** Finding a cell within the core failed, so just pick the center 7985 ** cell. This cell is guaranteed to be valid. 7986 */ 7987 cell = Coord_Cell(Center); 7988 } else { 7989 7990 /* 7991 ** If the edge fails, then try to find a cell within the core. 7992 */ 7993 cell = Random_Cell_In_Zone(ZONE_CORE); 7994 } 7995 } else { 7996 cell = Coord_Cell(coord); 7997 } 7998 7999 /* 8000 ** If the randomly picked location is not in the legal map area, then clip it to 8001 ** the legal map area. 8002 */ 8003 if (!Map.In_Radar(cell)) { 8004 int x = Cell_X(cell); 8005 int y = Cell_Y(cell); 8006 8007 if (x < Map.MapCellX) x = Map.MapCellX; 8008 if (y < Map.MapCellY) y = Map.MapCellY; 8009 if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1; 8010 if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1; 8011 cell = XY_Cell(x, y); 8012 } 8013 return(cell); 8014 } 8015 8016 /*********************************************************************************************** 8017 * HouseClass::Get_Ally_Flags -- Get the bit flags denoting the allies this house has. * 8018 * * 8019 * INPUT: none * 8020 * * 8021 * OUTPUT: Returns the bit field storing which houses this house is allied with. * 8022 * * 8023 * WARNINGS: none * 8024 * * 8025 * HISTORY: * 8026 * 09/12/2019 JAS : Created. * 8027 *=============================================================================================*/ 8028 unsigned HouseClass::Get_Ally_Flags() 8029 { 8030 return Allies; 8031 } 8032 8033 8034 8035 /*********************************************************************************************** 8036 * HouseClass::Check_Pertinent_Structures -- See if any useful structures remain * 8037 * * 8038 * INPUT: none * 8039 * * 8040 * OUTPUT: none * 8041 * * 8042 * WARNINGS: none * 8043 * * 8044 * HISTORY: * 8045 * 1/31/2020 3:34PM ST : Created. * 8046 *=============================================================================================*/ 8047 void HouseClass::Check_Pertinent_Structures(void) 8048 { 8049 /* 8050 ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM 8051 ** 8052 ** Game is over when no pertinent structures remain 8053 */ 8054 8055 if (!Special.IsEarlyWin) { 8056 return; 8057 } 8058 8059 if (IsToDie || IsToWin || IsToLose) { 8060 return; 8061 } 8062 8063 // MBL 07.15.2020 - Prevention of recent issue with constant "player defeated logic" and message to client spamming 8064 // Per https://jaas.ea.com/browse/TDRA-7433 8065 // 8066 if (IsDefeated) { 8067 return; 8068 } 8069 8070 bool any_good_buildings = false; 8071 8072 for (int index = 0; index < Buildings.Count(); index++) { 8073 BuildingClass *b = Buildings.Ptr(index); 8074 8075 if (b && b->IsActive && b->House == this) { 8076 if (!b->Class->IsWall && *b != STRUCT_APMINE && *b != STRUCT_AVMINE) { 8077 if (!Special.ModernBalance || (*b != STRUCT_SHIP_YARD && *b != STRUCT_FAKE_YARD && *b != STRUCT_SUB_PEN && *b != STRUCT_FAKE_PEN)) { 8078 if (!b->IsInLimbo && b->Strength > 0) { 8079 any_good_buildings = true; 8080 break; 8081 } 8082 } 8083 } 8084 } 8085 } 8086 8087 if (!any_good_buildings) { 8088 for (int index = 0; index < Units.Count(); index++) { 8089 UnitClass * unit = Units.Ptr(index); 8090 8091 if (unit && unit->IsActive && *unit == UNIT_MCV && unit->House == this) { 8092 if (!unit->IsInLimbo && unit->Strength > 0) { 8093 any_good_buildings = true; 8094 break; 8095 } 8096 } 8097 } 8098 } 8099 8100 if (!any_good_buildings) { 8101 Flag_To_Die(); 8102 } 8103 } 8104 8105 8106 8107 8108 8109 /*********************************************************************************************** 8110 * HouseClass::Init_Unit_Trackers -- Allocate the unit trackers for the house * 8111 * * 8112 * INPUT: none * 8113 * * 8114 * OUTPUT: none * 8115 * * 8116 * WARNINGS: none * 8117 * * 8118 * HISTORY: * 8119 * 4/23/2020 11:06PM ST : Created. * 8120 *=============================================================================================*/ 8121 void HouseClass::Init_Unit_Trackers(void) 8122 { 8123 if (Session.Type == GAME_INTERNET || Session.Type == GAME_GLYPHX_MULTIPLAYER) { 8124 AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT); 8125 InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT); 8126 UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT); 8127 BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT); 8128 VesselTotals = new UnitTrackerClass ( (int) VESSEL_COUNT); 8129 8130 DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT); 8131 DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT); 8132 DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT); 8133 DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); 8134 DestroyedVessels = new UnitTrackerClass ( (int) VESSEL_COUNT); 8135 8136 CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); 8137 TotalCrates = new UnitTrackerClass ( CRATE_COUNT ); 8138 } else { 8139 8140 AircraftTotals = NULL; 8141 InfantryTotals = NULL; 8142 UnitTotals = NULL; 8143 BuildingTotals = NULL; 8144 VesselTotals = NULL; 8145 8146 DestroyedAircraft = NULL; 8147 DestroyedInfantry = NULL; 8148 DestroyedUnits = NULL; 8149 DestroyedBuildings = NULL; 8150 DestroyedVessels = NULL; 8151 8152 CapturedBuildings = NULL; 8153 TotalCrates = NULL; 8154 } 8155 } 8156 8157 8158 8159 /*********************************************************************************************** 8160 * HouseClass::Free_Unit_Trackers -- Free the unit trackers for the house * 8161 * * 8162 * INPUT: none * 8163 * * 8164 * OUTPUT: none * 8165 * * 8166 * WARNINGS: none * 8167 * * 8168 * HISTORY: * 8169 * 4/23/2020 11:06PM ST : Created. * 8170 *=============================================================================================*/ 8171 void HouseClass::Free_Unit_Trackers(void) 8172 { 8173 if (AircraftTotals) { 8174 delete AircraftTotals; 8175 AircraftTotals = NULL; 8176 } 8177 8178 if (InfantryTotals) { 8179 delete InfantryTotals; 8180 InfantryTotals = NULL; 8181 } 8182 8183 if (UnitTotals) { 8184 delete UnitTotals; 8185 UnitTotals = NULL; 8186 } 8187 8188 if (BuildingTotals) { 8189 delete BuildingTotals; 8190 BuildingTotals = NULL; 8191 } 8192 8193 if (VesselTotals) { 8194 delete VesselTotals; 8195 VesselTotals = NULL; 8196 } 8197 8198 if (DestroyedAircraft) { 8199 delete DestroyedAircraft; 8200 DestroyedAircraft = NULL; 8201 } 8202 8203 if (DestroyedInfantry) { 8204 delete DestroyedInfantry; 8205 DestroyedInfantry = NULL; 8206 } 8207 8208 if (DestroyedUnits) { 8209 delete DestroyedUnits; 8210 DestroyedUnits = NULL; 8211 } 8212 8213 if (DestroyedBuildings) { 8214 delete DestroyedBuildings; 8215 DestroyedBuildings = NULL; 8216 } 8217 8218 if (DestroyedVessels) { 8219 delete DestroyedVessels; 8220 DestroyedVessels = NULL; 8221 } 8222 8223 if (CapturedBuildings) { 8224 delete CapturedBuildings; 8225 CapturedBuildings = NULL; 8226 } 8227 8228 if (TotalCrates) { 8229 delete TotalCrates; 8230 TotalCrates = NULL; 8231 } 8232 }