FACTORY.CPP (38936B)
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/FACTORY.CPP 1 3/03/97 10:24a 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 -- Fetches the completion step for this factory. * 37 * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable 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 factory's 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 -- Force factory to "produce" special object. * 45 * FactoryClass::Start -- Resumes production after suspension or creation. * 46 * FactoryClass::Suspend -- Temporarily stop production. * 47 * FactoryClass::operator delete -- Returns a factory to the free factory pool. * 48 * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * 49 * FactoryClass::~FactoryClass -- Default destructor for factory objects. * 50 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 51 52 #include "function.h" 53 54 55 /*********************************************************************************************** 56 * FactoryClass::FactoryClass -- Default constructor for factory objects. * 57 * * 58 * This brings the factory into a null state. It is called when a factory object is * 59 * created. * 60 * * 61 * INPUT: none * 62 * * 63 * OUTPUT: none * 64 * * 65 * WARNINGS: none * 66 * * 67 * HISTORY: * 68 * 12/26/1994 JLB : Created. * 69 *=============================================================================================*/ 70 FactoryClass::FactoryClass(void) : 71 RTTI(RTTI_FACTORY), 72 ID(Factories.ID(this)), 73 IsSuspended(false), 74 IsDifferent(false), 75 IsBlocked(false), 76 Balance(0), 77 OriginalBalance(0), 78 Object(0), 79 SpecialItem(SPC_NONE), 80 House(0) 81 { 82 Set_Rate(0); 83 Set_Stage(0); 84 } 85 86 87 /*********************************************************************************************** 88 * FactoryClass::~FactoryClass -- Default destructor for factory objects. * 89 * * 90 * This cleans up a factory object in preparation for deletion. If there is currently * 91 * an object in production, it is abandoned and money is refunded. * 92 * * 93 * INPUT: none * 94 * * 95 * OUTPUT: none * 96 * * 97 * WARNINGS: none * 98 * * 99 * HISTORY: * 100 * 12/26/1994 JLB : Created. * 101 *=============================================================================================*/ 102 FactoryClass::~FactoryClass(void) 103 { 104 if (GameActive) { 105 Abandon(); 106 } 107 } 108 109 110 /*********************************************************************************************** 111 * FactoryClass::Init -- Clears all units for scenario preparation. * 112 * * 113 * This routine will zero out the factory list and objects. This routine is typically * 114 * used in preparation for a new scenario load. All factory are guaranteed to be eliminated * 115 * by this routine. * 116 * * 117 * INPUT: none * 118 * * 119 * OUTPUT: none * 120 * * 121 * WARNINGS: none * 122 * * 123 * HISTORY: * 124 * 08/15/1994 JLB : Created. * 125 *=============================================================================================*/ 126 void FactoryClass::Init(void) 127 { 128 Factories.Free_All(); 129 } 130 131 132 /*********************************************************************************************** 133 * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * 134 * * 135 * This routine allocates a factory from the free factory pool. If there is no more room * 136 * to allocate a factory, then NULL is returned. * 137 * * 138 * INPUT: none * 139 * * 140 * OUTPUT: Returns with pointer to the newly allocated factory object. * 141 * * 142 * WARNINGS: none * 143 * * 144 * HISTORY: * 145 * 12/26/1994 JLB : Created. * 146 *=============================================================================================*/ 147 void * FactoryClass::operator new(size_t) 148 { 149 void * ptr = Factories.Allocate(); 150 if (ptr) { 151 ((FactoryClass *)ptr)->IsActive = true; 152 } 153 return(ptr); 154 } 155 156 157 /*********************************************************************************************** 158 * FactoryClass::operator delete -- Returns a factory to the free factory pool. * 159 * * 160 * This returns the factory object back to the factory allocation pool. The factory is then * 161 * available to be allocated. * 162 * * 163 * INPUT: ptr -- Pointer to the factory object to delete. * 164 * * 165 * OUTPUT: none * 166 * * 167 * WARNINGS: none * 168 * * 169 * HISTORY: * 170 * 12/26/1994 JLB : Created. * 171 *=============================================================================================*/ 172 void FactoryClass::operator delete(void * ptr) 173 { 174 if (ptr) { 175 ((FactoryClass *)ptr)->IsActive = false; 176 } 177 Factories.Free((FactoryClass *)ptr); 178 } 179 180 181 /*********************************************************************************************** 182 * FactoryClass::AI -- Process factory production logic. * 183 * * 184 * This routine should be called once per game tick. It handles the production process. * 185 * As production proceeds, money is deducted from the owner object's house. When production * 186 * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * 187 * required after that point. * 188 * * 189 * INPUT: none * 190 * * 191 * OUTPUT: none * 192 * * 193 * WARNINGS: none * 194 * * 195 * HISTORY: * 196 * 12/26/1994 JLB : Created. * 197 * 01/04/1995 JLB : Uses exact installment payment method. * 198 *=============================================================================================*/ 199 void FactoryClass::AI(void) 200 { 201 assert(Factories.ID(this) == ID); 202 203 if (!IsSuspended && (Object != NULL || SpecialItem)) { 204 for (int index = 0; index < 1; index++) { 205 if (!Has_Completed() && Graphic_Logic() ) { 206 IsDifferent = true; 207 208 int cost = Cost_Per_Tick(); 209 210 cost = min(cost, Balance); 211 212 /* 213 ** Enough time has expired so that another production step can occur. 214 ** If there is insufficient funds, then go back one production step and 215 ** continue the countdown. The idea being that by the time the next 216 ** production step occurs, there may be sufficient funds available. 217 */ 218 if (cost > House->Available_Money()) { 219 Set_Stage(Fetch_Stage()-1); 220 } else { 221 House->Spend_Money(cost); 222 Balance -= cost; 223 } 224 225 /* 226 ** If the production has completed, then suspend further production. 227 */ 228 if (Fetch_Stage() == STEP_COUNT) { 229 IsSuspended = true; 230 Set_Rate(0); 231 House->Spend_Money(Balance); 232 Balance = 0; 233 } 234 } 235 } 236 } 237 } 238 239 240 241 /*********************************************************************************************** 242 * FactoryClass::Force_Complete -- Force the factory to finish what it's building * 243 * * 244 * For debugging/testing support * 245 * * 246 * * 247 * INPUT: none * 248 * * 249 * OUTPUT: none * 250 * * 251 * WARNINGS: * 252 * * 253 * HISTORY: * 254 * 8/23/2019 3:54PM ST : Created. * 255 *=============================================================================================*/ 256 void FactoryClass::Force_Complete(void) 257 { 258 assert(Factories.ID(this) == ID); 259 260 if (!IsSuspended && (Object != NULL || SpecialItem)) { 261 Set_Stage(STEP_COUNT); 262 IsSuspended = true; 263 Set_Rate(0); 264 Balance = 0; 265 IsDifferent = true; 266 } 267 } 268 269 270 /*********************************************************************************************** 271 * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * 272 * * 273 * Use this routine to determine if production has advanced at least one step. By using * 274 * this function, intelligent rendering may be performed. * 275 * * 276 * INPUT: none * 277 * * 278 * OUTPUT: bool; Has the production process advanced one step since the last time this * 279 * function was called? * 280 * * 281 * WARNINGS: This function clears the changed status flag as a side effect. * 282 * * 283 * HISTORY: * 284 * 12/26/1994 JLB : Created. * 285 *=============================================================================================*/ 286 bool FactoryClass::Has_Changed(void) 287 { 288 assert(Factories.ID(this) == ID); 289 290 bool changed = IsDifferent; 291 IsDifferent = false; 292 return(changed); 293 } 294 295 296 /*********************************************************************************************** 297 * FactoryClass::Set -- Assigns a factory to produce an object. * 298 * * 299 * This routine initializes a factory to produce the object specified. The desired object * 300 * type is created and placed in suspended animation (limbo) until such time as production * 301 * completes. Production is not actually started by this routine. An explicit call to * 302 * Start() is required to begin production. * 303 * * 304 * INPUT: object -- Reference to the object type class that is to be produced. * 305 * * 306 * house -- Reference to the owner of the object to be produced. * 307 * * 308 * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * 309 * that the object could not be created. This is catastrophic and in such * 310 * cases, the factory object should be deleted. * 311 * * 312 * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * 313 * the factory means that the factory is useless and should be deleted. * 314 * * 315 * HISTORY: * 316 * 12/26/1994 JLB : Created. * 317 *=============================================================================================*/ 318 bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) 319 { 320 assert(Factories.ID(this) == ID); 321 322 /* 323 ** If there is any production currently in progress, abandon it. 324 */ 325 Abandon(); 326 327 /* 328 ** Set up the factory for the new production process. 329 */ 330 IsDifferent = true; 331 IsSuspended = true; 332 Set_Rate(0); 333 Set_Stage(0); 334 335 /* 336 ** Create an object of the type requested. 337 */ 338 Object = (TechnoClass *)object.Create_One_Of(&house); 339 340 /* 341 ** Buildings that are constructed, will default to rebuilding on so that 342 ** repair can commence and base rebuilding can occur. 343 */ 344 if (!house.IsHuman && Object != NULL && Object->What_Am_I() == RTTI_BUILDING) { 345 ((BuildingClass *)Object)->IsToRebuild = true; 346 } 347 348 if (Object) { 349 House = Object->House; 350 Balance = object.Cost_Of() * house.CostBias; 351 Object->PurchasePrice = Balance; 352 } 353 354 /* 355 ** If all was set up successfully, then return true. 356 */ 357 return(Object != NULL); 358 } 359 360 361 /*********************************************************************************************** 362 * FactoryClass::Set -- Fills a factory with an already completed object. * 363 * * 364 * This routine is called when a produced object is in placement mode but then placement * 365 * is suspended. The object must then return to the factory as if it were newly completed * 366 * and awaiting removal. * 367 * * 368 * INPUT: object -- The object to return to the factory. * 369 * * 370 * OUTPUT: none * 371 * * 372 * WARNINGS: This will abandon any current object being produced at the factory in order * 373 * to set the new object into it. * 374 * * 375 * HISTORY: * 376 * 12/26/1994 JLB : Created. * 377 *=============================================================================================*/ 378 void FactoryClass::Set(TechnoClass & object) 379 { 380 assert(Factories.ID(this) == ID); 381 382 Abandon(); 383 Object = &object; 384 House = Object->House; 385 Balance = 0; 386 Set_Rate(0); 387 Set_Stage(STEP_COUNT); 388 IsDifferent = true; 389 IsSuspended = true; 390 } 391 392 393 /*********************************************************************************************** 394 * FactoryClass::Suspend -- Temporarily stop production. * 395 * * 396 * This routine will suspend production until a subsequent call to Start() or Abandon(). * 397 * Typical use of this function is when the player puts production on hold or when there * 398 * is insufficient funds. * 399 * * 400 * INPUT: none * 401 * * 402 * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * 403 * factory was empty or production was already stopped (or never started). * 404 * * 405 * WARNINGS: none * 406 * * 407 * HISTORY: * 408 * 12/26/1994 JLB : Created. * 409 *=============================================================================================*/ 410 bool FactoryClass::Suspend(void) 411 { 412 assert(Factories.ID(this) == ID); 413 414 if (!IsSuspended) { 415 IsSuspended = true; 416 Set_Rate(0); 417 return(true); 418 } 419 return(false); 420 } 421 422 423 /*********************************************************************************************** 424 * FactoryClass::Start -- Resumes production after suspension or creation. * 425 * * 426 * This function will start the production process. It works for newly created factory * 427 * objects, as well as if production had been suspended previously. * 428 * * 429 * INPUT: none * 430 * * 431 * OUTPUT: bool; Was production started? A false return value means that the factory is * 432 * empty or there is insufficient credits to begin production. * 433 * * 434 * WARNINGS: none * 435 * * 436 * HISTORY: * 437 * 12/26/1994 JLB : Created. * 438 *=============================================================================================*/ 439 bool FactoryClass::Start(void) 440 { 441 assert(Factories.ID(this) == ID); 442 443 if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { 444 if (House->IsHuman || House->Available_Money() >= Cost_Per_Tick()) { 445 int time; 446 447 if (Object) { 448 time = Object->Class_Of().Time_To_Build(House->Class->House); 449 } 450 451 time /= STEP_COUNT; 452 time = Bound(time, 1, 255); 453 454 Set_Rate(time); 455 IsSuspended = false; 456 return(true); 457 } 458 } 459 return(false); 460 } 461 462 463 /*********************************************************************************************** 464 * FactoryClass::Abandon -- Abandons current construction with money refunded. * 465 * * 466 * This routine is used when construction is to be abandoned and current money spend is * 467 * to be refunded. This function effectively clears out this factory of all record of the * 468 * producing object so that it may either be deleted or started anew with the Set() * 469 * function. * 470 * * 471 * INPUT: none * 472 * * 473 * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * 474 * factory was not producing any object. * 475 * * 476 * WARNINGS: none * 477 * * 478 * HISTORY: * 479 * 12/26/1994 JLB : Created. * 480 *=============================================================================================*/ 481 bool FactoryClass::Abandon(void) 482 { 483 assert(Factories.ID(this) == ID); 484 485 if (Object) { 486 487 if (Object) { 488 /* 489 ** Refund all money expended so far, back to the owner of the object under construction. 490 */ 491 int money = Object->Class_Of().Cost_Of() * Object->House->CostBias; 492 House->Refund_Money(money - Balance); 493 Balance = 0; 494 495 /* 496 ** Delete the object under construction. 497 */ 498 ScenarioInit++; 499 delete Object; 500 Object = NULL; 501 ScenarioInit--; 502 } 503 if (SpecialItem) { 504 SpecialItem = SPC_NONE; 505 } 506 507 /* 508 ** Set the factory back to the idle and empty state. 509 */ 510 Set_Rate(0); 511 Set_Stage(0); 512 IsSuspended = true; 513 IsDifferent = true; 514 515 return(true); 516 } 517 return(false); 518 } 519 520 521 /*********************************************************************************************** 522 * FactoryClass::Completion -- Fetches the completion step for this factory. * 523 * * 524 * Use this routine to determine what animation (or completion step) the factory is * 525 * currently on. * 526 * * 527 * INPUT: none * 528 * * 529 * OUTPUT: Returns a completion step number between 0 (uncompleted), to STEP_COUNT (completed)* 530 * * 531 * WARNINGS: none * 532 * * 533 * HISTORY: * 534 * 12/26/1994 JLB : Created. * 535 *=============================================================================================*/ 536 int FactoryClass::Completion(void) 537 { 538 assert(Factories.ID(this) == ID); 539 540 return(Fetch_Stage()); 541 } 542 543 544 /*********************************************************************************************** 545 * FactoryClass::Has_Completed -- Checks to see if object has completed production. * 546 * * 547 * Use this routine to examine the factory object in order to determine if the associated * 548 * object has completed production and is awaiting placement. * 549 * * 550 * INPUT: none * 551 * * 552 * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * 553 * * 554 * WARNINGS: none * 555 * * 556 * HISTORY: * 557 * 12/26/1994 JLB : Created. * 558 *=============================================================================================*/ 559 bool FactoryClass::Has_Completed(void) 560 { 561 assert(Factories.ID(this) == ID); 562 563 if (Object && Fetch_Stage() == STEP_COUNT) { 564 return(true); 565 } 566 if (SpecialItem && Fetch_Stage() == STEP_COUNT) { 567 return(true); 568 } 569 return(false); 570 } 571 572 573 /*********************************************************************************************** 574 * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * 575 * * 576 * This routine gets the pointer to the currently constructing object. * 577 * * 578 * INPUT: none * 579 * * 580 * OUTPUT: Returns with a pointer to the object undergoing construction. * 581 * * 582 * WARNINGS: none * 583 * * 584 * HISTORY: * 585 * 12/26/1994 JLB : Created. * 586 *=============================================================================================*/ 587 TechnoClass * FactoryClass::Get_Object(void) const 588 { 589 assert(Factories.ID(this) == ID); 590 591 return(Object); 592 } 593 594 595 /*************************************************************************** 596 * FactoryClass::Get_Special_Item -- gets factory spc prod item * 597 * * 598 * INPUT: none * 599 * * 600 * OUTPUT: int the item the factory is currently working on * 601 * * 602 * HISTORY: * 603 * 05/05/1995 PWG : Created. * 604 *=========================================================================*/ 605 int FactoryClass::Get_Special_Item(void) const 606 { 607 assert(Factories.ID(this) == ID); 608 609 return(SpecialItem); 610 } 611 612 613 /*********************************************************************************************** 614 * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * 615 * * 616 * Use this routine to determine the cost per game "tick" to produce the object. * 617 * * 618 * INPUT: none * 619 * * 620 * OUTPUT: Returns the number of credits necessary to advance production one game tick. * 621 * * 622 * WARNINGS: none * 623 * * 624 * HISTORY: * 625 * 12/26/1994 JLB : Created. * 626 *=============================================================================================*/ 627 int FactoryClass::Cost_Per_Tick(void) 628 { 629 assert(Factories.ID(this) == ID); 630 631 if (Object) { 632 int steps = STEP_COUNT - Fetch_Stage(); 633 if (steps) { 634 return(Balance / steps); 635 } 636 return(Balance); 637 } 638 return(0); 639 } 640 641 642 /*********************************************************************************************** 643 * FactoryClass::Completed -- Clears factory object after a completed production process. * 644 * * 645 * This routine is called after production completes, and the object produced has been * 646 * placed into the game. It resets the factory for deletion or starting of new production. * 647 * * 648 * INPUT: none * 649 * * 650 * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * 651 * any completed object. An immediate second call to this routine will also * 652 * yield false. * 653 * * 654 * WARNINGS: none * 655 * * 656 * HISTORY: * 657 * 12/26/1994 JLB : Created. * 658 *=============================================================================================*/ 659 bool FactoryClass::Completed(void) 660 { 661 assert(Factories.ID(this) == ID); 662 663 if (Object && Fetch_Stage() == STEP_COUNT) { 664 Object = NULL; 665 IsSuspended = true; 666 IsDifferent = true; 667 Set_Stage(0); 668 Set_Rate(0); 669 return(true); 670 } 671 672 if (SpecialItem && Fetch_Stage() == STEP_COUNT) { 673 SpecialItem = SPC_NONE; 674 IsSuspended = true; 675 IsDifferent = true; 676 Set_Stage(0); 677 Set_Rate(0); 678 return(true); 679 } 680 return(false); 681 }