FACTORY.CPP (42942B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\factory.cpv 2.18 16 Oct 1995 16:51:26 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : FACTORY.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : 12/26/94 * 28 * * 29 * Last Update : May 22, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * FactoryClass::AI -- Process factory production logic. * 34 * FactoryClass::Abandon -- Abandons current construction with money refunded. * 35 * FactoryClass::Completed -- Clears factory object after a completed production process. * 36 * FactoryClass::Completion -- Fetchs the completion step for this factory. * 37 * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into managable chunks. * 38 * FactoryClass::FactoryClass -- Default constructor for factory objects. * 39 * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * 40 * FactoryClass::Get_Special_Item -- gets factorys spc prod item * 41 * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * 42 * FactoryClass::Has_Completed -- Checks to see if object has completed production. * 43 * FactoryClass::Set -- Assigns a factory to produce an object. * 44 * FactoryClass::Set -- Fills a factory with an already completed object. * 45 * FactoryClass::Set -- Force factory to "produce" special object. * 46 * FactoryClass::Start -- Resumes production after suspension or creation. * 47 * FactoryClass::Suspend -- Temporarily stop production. * 48 * FactoryClass::operator delete -- Returns a factory to the free factory pool. * 49 * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * 50 * FactoryClass::~FactoryClass -- Default destructor for factory objects. * 51 * FactoryClass::Validate -- validates factory pointer * 52 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 53 54 #include "function.h" 55 56 57 /*********************************************************************************************** 58 * FactoryClass::Validate -- validates factory pointer * 59 * * 60 * INPUT: * 61 * none. * 62 * * 63 * OUTPUT: * 64 * 1 = ok, 0 = error * 65 * * 66 * WARNINGS: * 67 * none. * 68 * * 69 * HISTORY: * 70 * 08/09/1995 BRR : Created. * 71 *=============================================================================================*/ 72 #ifdef CHEAT_KEYS 73 int FactoryClass::Validate(void) const 74 { 75 int num; 76 77 num = Factories.ID(this); 78 if (num < 0 || num >= FACTORY_MAX) { 79 Validate_Error("FACTORY"); 80 return (0); 81 } 82 else 83 return (1); 84 } 85 #else 86 #define Validate() 87 #endif 88 89 90 /*********************************************************************************************** 91 * FactoryClass::FactoryClass -- Default constructor for factory objects. * 92 * * 93 * This brings the factory into a null state. It is called when a factory object is * 94 * created. * 95 * * 96 * INPUT: none * 97 * * 98 * OUTPUT: none * 99 * * 100 * WARNINGS: none * 101 * * 102 * HISTORY: * 103 * 12/26/1994 JLB : Created. * 104 *=============================================================================================*/ 105 FactoryClass::FactoryClass(void) 106 { 107 IsSuspended = false; 108 IsDifferent = false; 109 IsBlocked = false; 110 Balance = 0; 111 SpecialItem = SPC_NONE; 112 Object = NULL; 113 House = NULL; 114 Set_Rate(0); 115 Set_Stage(0); 116 } 117 118 119 /*********************************************************************************************** 120 * FactoryClass::~FactoryClass -- Default destructor for factory objects. * 121 * * 122 * This cleans up a factory object in preparation for deletion. If there is currently * 123 * an object in production, it is abandoned and money is refunded. * 124 * * 125 * INPUT: none * 126 * * 127 * OUTPUT: none * 128 * * 129 * WARNINGS: none * 130 * * 131 * HISTORY: * 132 * 12/26/1994 JLB : Created. * 133 *=============================================================================================*/ 134 FactoryClass::~FactoryClass(void) 135 { 136 if (GameActive) { 137 Abandon(); 138 } 139 } 140 141 142 /*********************************************************************************************** 143 * FactoryClass::Init -- Clears all units for scenario preparation. * 144 * * 145 * This routine will zero out the factory list and objects. This routine is typically * 146 * used in preparation for a new scenario load. All factorys are guaranteed to be eliminated* 147 * by this routine. * 148 * * 149 * INPUT: none * 150 * * 151 * OUTPUT: none * 152 * * 153 * WARNINGS: none * 154 * * 155 * HISTORY: * 156 * 08/15/1994 JLB : Created. * 157 *=============================================================================================*/ 158 void FactoryClass::Init(void) 159 { 160 Factories.Free_All(); 161 } 162 163 164 /*********************************************************************************************** 165 * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * 166 * * 167 * This routine allocates a factory from the free factory pool. If there is no more room * 168 * to allocate a factory, then NULL is returned. * 169 * * 170 * INPUT: none * 171 * * 172 * OUTPUT: Returns with pointer to the newly allocated factory object. * 173 * * 174 * WARNINGS: none * 175 * * 176 * HISTORY: * 177 * 12/26/1994 JLB : Created. * 178 *=============================================================================================*/ 179 void * FactoryClass::operator new(size_t) 180 { 181 void * ptr = Factories.Allocate(); 182 if (ptr) { 183 ((FactoryClass *)ptr)->IsActive = true; 184 } 185 return(ptr); 186 } 187 188 189 /*********************************************************************************************** 190 * FactoryClass::operator delete -- Returns a factory to the free factory pool. * 191 * * 192 * This returns the factory object back to the factory allocation pool. The factory is then * 193 * available to be allocated. * 194 * * 195 * INPUT: ptr -- Pointer to the factory object to delete. * 196 * * 197 * OUTPUT: none * 198 * * 199 * WARNINGS: none * 200 * * 201 * HISTORY: * 202 * 12/26/1994 JLB : Created. * 203 *=============================================================================================*/ 204 void FactoryClass::operator delete(void *ptr) 205 { 206 if (ptr) { 207 ((FactoryClass *)ptr)->IsActive = false; 208 } 209 Factories.Free((FactoryClass *)ptr); 210 } 211 212 213 /*********************************************************************************************** 214 * FactoryClass::AI -- Process factory production logic. * 215 * * 216 * This routine should be called once per game tick. It handles the production process. * 217 * As production proceeds, money is deducted from the owner object's house. When production * 218 * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * 219 * required after that point. * 220 * * 221 * INPUT: none * 222 * * 223 * OUTPUT: none * 224 * * 225 * WARNINGS: none * 226 * * 227 * HISTORY: * 228 * 12/26/1994 JLB : Created. * 229 * 01/04/1995 JLB : Uses exact installment payment method. * 230 *=============================================================================================*/ 231 void FactoryClass::AI(void) 232 { 233 Validate(); 234 if (!IsSuspended && (Object != NULL || SpecialItem)) { 235 int stages = 1; 236 237 /* 238 ** Determine the acceleration factor for factory production. 239 ** This applies only to human players. The computer builds 240 ** units on a building by building basis -- quantity of building 241 ** factory types doesn't affect individual factories. 242 */ 243 if (Object && House->IsHuman) { 244 switch (Object->What_Am_I()) { 245 case RTTI_AIRCRAFT: 246 stages = House->AircraftFactories; 247 break; 248 249 case RTTI_INFANTRY: 250 stages = House->InfantryFactories; 251 break; 252 253 case RTTI_UNIT: 254 stages = House->UnitFactories; 255 break; 256 257 case RTTI_BUILDING: 258 stages = House->BuildingFactories; 259 break; 260 } 261 stages = MAX(stages, 1); 262 } 263 264 265 for (int index = 0; index < stages; index++) { 266 if (!Has_Completed() && Graphic_Logic()) { 267 IsDifferent = true; 268 269 int cost = Cost_Per_Tick(); 270 271 cost = MIN(cost, Balance); 272 273 /* 274 ** Enough time has expired so that another production step can occur. 275 ** If there is insufficient funds, then go back one production step and 276 ** continue the countdown. The idea being that by the time the next 277 ** production step occurs, there may be sufficient funds available. 278 */ 279 if (cost > House->Available_Money()) { 280 Set_Stage(Fetch_Stage()-1); 281 } else { 282 House->Spend_Money(cost); 283 Balance -= cost; 284 } 285 if ( Debug_Instant_Build ) { 286 Set_Stage(STEP_COUNT); 287 } 288 /* 289 ** If the production has completed, then suspend further production. 290 */ 291 if (Fetch_Stage() == STEP_COUNT) { 292 IsSuspended = true; 293 Set_Rate(0); 294 House->Spend_Money(Balance); 295 Balance = 0; 296 } 297 } 298 } 299 } 300 } 301 302 303 304 /*********************************************************************************************** 305 * FactoryClass::Force_Complete -- Force the factory to finish what it's building * 306 * * 307 * For debugging/testing support * 308 * * 309 * * 310 * INPUT: none * 311 * * 312 * OUTPUT: none * 313 * * 314 * WARNINGS: * 315 * * 316 * HISTORY: * 317 * 8/23/2019 3:54PM ST : Created. * 318 *=============================================================================================*/ 319 void FactoryClass::Force_Complete(void) 320 { 321 Validate(); 322 if (!IsSuspended && (Object != NULL || SpecialItem)) { 323 Set_Stage(STEP_COUNT); 324 IsSuspended = true; 325 Set_Rate(0); 326 Balance = 0; 327 IsDifferent = true; 328 } 329 } 330 331 332 /*********************************************************************************************** 333 * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * 334 * * 335 * Use this routine to determine if production has advanced at least one step. By using * 336 * this function, intelligent rendering may be performed. * 337 * * 338 * INPUT: none * 339 * * 340 * OUTPUT: bool; Has the production process advanced one step since the last time this * 341 * function was called? * 342 * * 343 * WARNINGS: This function clears the changed status flag as a side effect. * 344 * * 345 * HISTORY: * 346 * 12/26/1994 JLB : Created. * 347 *=============================================================================================*/ 348 bool FactoryClass::Has_Changed(void) 349 { 350 Validate(); 351 bool changed = IsDifferent; 352 IsDifferent = false; 353 return(changed); 354 } 355 356 357 /*********************************************************************************************** 358 * FactoryClass::Set -- Assigns a factory to produce an object. * 359 * * 360 * This routine initializes a factory to produce the object specified. The desired object * 361 * type is created and placed in suspended animation (limbo) until such time as production * 362 * completes. Production is not actually started by this routine. An explicit call to * 363 * Start() is required to begin production. * 364 * * 365 * INPUT: object -- Reference to the object type class that is to be produced. * 366 * * 367 * house -- Reference to the owner of the object to be produced. * 368 * * 369 * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * 370 * that the object could not be created. This is catastrophic and in such * 371 * cases, the factory object should be deleted. * 372 * * 373 * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * 374 * the factory means that the factory is useless and should be deleted. * 375 * * 376 * HISTORY: * 377 * 12/26/1994 JLB : Created. * 378 *=============================================================================================*/ 379 bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) 380 { 381 Validate(); 382 /* 383 ** If there is any production currently in progress, abandon it. 384 */ 385 Abandon(); 386 387 /* 388 ** Set up the factory for the new production process. 389 */ 390 IsDifferent = true; 391 IsSuspended = true; 392 Set_Rate(0); 393 Set_Stage(0); 394 395 /* 396 ** Create an object of the type requested. 397 */ 398 Object = (TechnoClass *)object.Create_One_Of(&house); 399 400 if (Object) { 401 House = Object->House; 402 Balance = object.Cost_Of() * house.CostBias; 403 Object->PurchasePrice = Balance; 404 } 405 406 /* 407 ** If all was set up successfully, then return true. 408 */ 409 return(Object != NULL); 410 } 411 412 413 /*********************************************************************************************** 414 * FactoryClass::Set -- Force factory to "produce" special object. * 415 * * 416 * Use this routine to force the factory into special production mode. Such production is * 417 * used for the ion cannon and other timed special weapon events. * 418 * * 419 * INPUT: type -- The special weapon type to begin "production" of. * 420 * * 421 * house -- The owner of this production object. * 422 * * 423 * OUTPUT: Was the assignment successful? * 424 * * 425 * WARNINGS: none * 426 * * 427 * HISTORY: * 428 * 05/22/1995 JLB : Created. * 429 *=============================================================================================*/ 430 bool FactoryClass::Set(int const & type, HouseClass & house) 431 { 432 Validate(); 433 /* 434 ** If there is any production currently in progress, abandon it. 435 */ 436 Abandon(); 437 438 /* 439 ** Set up the factory for the new production process. 440 */ 441 IsDifferent = true; 442 IsSuspended = true; 443 Set_Rate(0); 444 Set_Stage(0); 445 446 /* 447 ** Create an object of the type requested. 448 */ 449 SpecialItem = type; 450 House = &house; 451 Balance = 0; 452 453 /* 454 ** If all was set up successfully, then return true. 455 */ 456 return(SpecialItem != SPC_NONE); 457 } 458 459 460 /*********************************************************************************************** 461 * FactoryClass::Set -- Fills a factory with an already completed object. * 462 * * 463 * This routine is called when a produced object is in placement mode but then placement * 464 * is suspended. The object must then return to the factory as if it were newly completed * 465 * and awaiting removal. * 466 * * 467 * INPUT: object -- The object to return to the factory. * 468 * * 469 * OUTPUT: none * 470 * * 471 * WARNINGS: This will abandon any current object being produced at the factory in order * 472 * to set the new object into it. * 473 * * 474 * HISTORY: * 475 * 12/26/1994 JLB : Created. * 476 *=============================================================================================*/ 477 void FactoryClass::Set(TechnoClass & object) 478 { 479 Validate(); 480 Abandon(); 481 Object = &object; 482 House = Object->House; 483 Balance = 0; 484 Set_Rate(0); 485 Set_Stage(STEP_COUNT); 486 IsDifferent = true; 487 IsSuspended = true; 488 } 489 490 491 /*********************************************************************************************** 492 * FactoryClass::Suspend -- Temporarily stop production. * 493 * * 494 * This routine will suspend production until a subsiquent call to Start() or Abandon(). * 495 * Typical use of this function is when the player puts production on hold or when there * 496 * is insufficient funds. * 497 * * 498 * INPUT: none * 499 * * 500 * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * 501 * factory was empty or production was already stopped (or never started). * 502 * * 503 * WARNINGS: none * 504 * * 505 * HISTORY: * 506 * 12/26/1994 JLB : Created. * 507 *=============================================================================================*/ 508 bool FactoryClass::Suspend(void) 509 { 510 Validate(); 511 if (!IsSuspended) { 512 IsSuspended = true; 513 Set_Rate(0); 514 return(true); 515 } 516 return(false); 517 } 518 519 520 /*********************************************************************************************** 521 * FactoryClass::Start -- Resumes production after suspension or creation. * 522 * * 523 * This function will start the production process. It works for newly created factory * 524 * objects, as well as if production had been suspended previously. * 525 * * 526 * INPUT: none * 527 * * 528 * OUTPUT: bool; Was production started? A false return value means that the factory is * 529 * empty or there is unsufficient credits to begin production. * 530 * * 531 * WARNINGS: none * 532 * * 533 * HISTORY: * 534 * 12/26/1994 JLB : Created. * 535 *=============================================================================================*/ 536 bool FactoryClass::Start(void) 537 { 538 Validate(); 539 if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { 540 if (House->IsHuman || House->Available_Money() >= Cost_Per_Tick()) { 541 int time; 542 543 if (Object) { 544 time = Object->Class_Of().Time_To_Build(House->Class->House); 545 } else { 546 time = TICKS_PER_MINUTE * 5; 547 } 548 549 time /= STEP_COUNT; 550 time = Bound(time, 1, 255); 551 552 Set_Rate(time); 553 IsSuspended = false; 554 return(true); 555 } 556 } 557 return(false); 558 } 559 560 561 /*********************************************************************************************** 562 * FactoryClass::Abandon -- Abandons current construction with money refunded. * 563 * * 564 * This routine is used when construction is to be abandoned and current money spend is * 565 * to be refunded. This function effectively clears out this factory of all record of the * 566 * producing object so that it may either be deleted or started anew with the Set() * 567 * function. * 568 * * 569 * INPUT: none * 570 * * 571 * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * 572 * factory was not producing any object. * 573 * * 574 * WARNINGS: none * 575 * * 576 * HISTORY: * 577 * 12/26/1994 JLB : Created. * 578 *=============================================================================================*/ 579 bool FactoryClass::Abandon(void) 580 { 581 Validate(); 582 if (Object) { 583 584 if (Object) { 585 /* 586 ** Refund all money expended so far, back to the owner of the object under construction. 587 */ 588 int money = Object->Class_Of().Cost_Of() * Object->House->CostBias; 589 House->Refund_Money(money - Balance); 590 Balance = 0; 591 592 /* 593 ** Delete the object under construction. 594 */ 595 ScenarioInit++; 596 delete Object; 597 Object = NULL; 598 ScenarioInit--; 599 } 600 if (SpecialItem) { 601 SpecialItem = SPC_NONE; 602 } 603 604 /* 605 ** Set the factory back to the idle and empty state. 606 */ 607 Set_Rate(0); 608 Set_Stage(0); 609 IsSuspended = true; 610 IsDifferent = true; 611 612 return(true); 613 } 614 return(false); 615 } 616 617 618 /*********************************************************************************************** 619 * FactoryClass::Completion -- Fetchs the completion step for this factory. * 620 * * 621 * Use this routine to determine what animation (or completion step) the factory is * 622 * currently on. * 623 * * 624 * INPUT: none * 625 * * 626 * OUTPUT: Returns a completion step number beteen 0 (uncompleted), to STEP_COUNT (completed) * 627 * * 628 * WARNINGS: none * 629 * * 630 * HISTORY: * 631 * 12/26/1994 JLB : Created. * 632 *=============================================================================================*/ 633 int FactoryClass::Completion(void) 634 { 635 Validate(); 636 return(Fetch_Stage()); 637 } 638 639 640 /*********************************************************************************************** 641 * FactoryClass::Has_Completed -- Checks to see if object has completed production. * 642 * * 643 * Use this routine to examine the factory object in order to determine if the associated * 644 * object has completed production and is awaiting placement. * 645 * * 646 * INPUT: none * 647 * * 648 * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * 649 * * 650 * WARNINGS: none * 651 * * 652 * HISTORY: * 653 * 12/26/1994 JLB : Created. * 654 *=============================================================================================*/ 655 bool FactoryClass::Has_Completed(void) 656 { 657 Validate(); 658 if (Object && Fetch_Stage() == STEP_COUNT) { 659 return(true); 660 } 661 if (SpecialItem && Fetch_Stage() == STEP_COUNT) { 662 return(true); 663 } 664 return(false); 665 } 666 667 668 /*********************************************************************************************** 669 * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * 670 * * 671 * This routine gets the pointer to the currently constructing object. * 672 * * 673 * INPUT: none * 674 * * 675 * OUTPUT: Returns with a pointer to the object undergoing construction. * 676 * * 677 * WARNINGS: none * 678 * * 679 * HISTORY: * 680 * 12/26/1994 JLB : Created. * 681 *=============================================================================================*/ 682 TechnoClass * FactoryClass::Get_Object(void) const 683 { 684 Validate(); 685 return(Object); 686 } 687 688 689 /*************************************************************************** 690 * FactoryClass::Get_Special_Item -- gets factorys spc prod item * 691 * * 692 * INPUT: none * 693 * * 694 * OUTPUT: int the item the factory is currently working on * 695 * * 696 * HISTORY: * 697 * 05/05/1995 PWG : Created. * 698 *=========================================================================*/ 699 int FactoryClass::Get_Special_Item(void) const 700 { 701 Validate(); 702 return(SpecialItem); 703 } 704 705 706 /*********************************************************************************************** 707 * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into managable chunks. * 708 * * 709 * Use this routine to determine the cost per game "tick" to produce the object. * 710 * * 711 * INPUT: none * 712 * * 713 * OUTPUT: Returns the number of credits necessary to advance production one game tick. * 714 * * 715 * WARNINGS: none * 716 * * 717 * HISTORY: * 718 * 12/26/1994 JLB : Created. * 719 *=============================================================================================*/ 720 int FactoryClass::Cost_Per_Tick(void) 721 { 722 Validate(); 723 if (Object) { 724 int steps = STEP_COUNT - Fetch_Stage(); 725 if (steps) { 726 return(Balance / steps); 727 } 728 return(Balance); 729 } 730 return(0); 731 } 732 733 734 /*********************************************************************************************** 735 * FactoryClass::Completed -- Clears factory object after a completed production process. * 736 * * 737 * This routine is called after production completes, and the object produced has been * 738 * placed into the game. It resets the factory for deletion or starting of new production. * 739 * * 740 * INPUT: none * 741 * * 742 * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * 743 * any completed object. An immediate second call to this routine will also * 744 * yield false. * 745 * * 746 * WARNINGS: none * 747 * * 748 * HISTORY: * 749 * 12/26/1994 JLB : Created. * 750 *=============================================================================================*/ 751 bool FactoryClass::Completed(void) 752 { 753 Validate(); 754 if (Object && Fetch_Stage() == STEP_COUNT) { 755 Object = NULL; 756 IsSuspended = true; 757 IsDifferent = true; 758 Set_Stage(0); 759 Set_Rate(0); 760 return(true); 761 } 762 763 if (SpecialItem && Fetch_Stage() == STEP_COUNT) { 764 SpecialItem = SPC_NONE; 765 IsSuspended = true; 766 IsDifferent = true; 767 Set_Stage(0); 768 Set_Rate(0); 769 return(true); 770 } 771 return(false); 772 }