FACTORY.CPP 37 KB

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