FACTORY.CPP 38 KB

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