TRIGGER.CPP 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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/TRIGGER.CPP 1 3/03/97 10:26a 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 : TRIGGER.CPP *
  26. * *
  27. * Programmer : Joe L. Bostic *
  28. * *
  29. * Start Date : 11/12/94 *
  30. * *
  31. * Last Update : August 13, 1996 [JLB] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * Find_Or_Make -- Find or create a trigger of the type specified. *
  36. * TriggerClass::As_Target -- Converts trigger to a target value *
  37. * TriggerClass::Attaches_To -- Determines what trigger can attach to. *
  38. * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. *
  39. * TriggerClass::Detach -- Detach specified target from this trigger. *
  40. * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. *
  41. * TriggerClass::Init -- clears triggers for new scenario *
  42. * TriggerClass::Spring -- Spring the trigger (possibly). *
  43. * TriggerClass::TriggerClass -- constructor *
  44. * TriggerClass::operator delete -- Returns a trigger to the special memory pool. *
  45. * TriggerClass::operator new -- 'new' operator *
  46. * TriggerClass::~TriggerClass -- Destructor for trigger objects. *
  47. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  48. #include "function.h"
  49. #if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR)
  50. /***********************************************************************************************
  51. * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. *
  52. * *
  53. * In the rare (possibly never?) case of requiring a description for this trigger, then *
  54. * just have the model class generate a description and return that. *
  55. * *
  56. * INPUT: none *
  57. * *
  58. * OUTPUT: Returns with an ASCII description of this trigger. *
  59. * *
  60. * WARNINGS: see TriggerTypeClass::Description() *
  61. * *
  62. * HISTORY: *
  63. * 07/09/1996 JLB : Created. *
  64. *=============================================================================================*/
  65. char const * TriggerClass::Description(void) const
  66. {
  67. return(Class->Description());
  68. }
  69. /***********************************************************************************************
  70. * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. *
  71. * *
  72. * This routine is called when the trigger has been assigned to a list box. It will *
  73. * display a description of the trigger. *
  74. * *
  75. * INPUT: see below... *
  76. * *
  77. * OUTPUT: none *
  78. * *
  79. * WARNINGS: none *
  80. * *
  81. * HISTORY: *
  82. * 07/09/1996 JLB : Created. *
  83. *=============================================================================================*/
  84. void TriggerClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const
  85. {
  86. RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
  87. static int _tabs[] = {13,40};
  88. if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) {
  89. if (selected) {
  90. flags = flags | TPF_BRIGHT_COLOR;
  91. LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow);
  92. } else {
  93. if (!(flags & TPF_USE_GRAD_PAL)) {
  94. flags = flags | TPF_MEDIUM_COLOR;
  95. }
  96. }
  97. Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs);
  98. } else {
  99. Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs);
  100. }
  101. }
  102. #endif
  103. /***********************************************************************************************
  104. * TriggerClass::TriggerClass -- constructor *
  105. * *
  106. * INPUT: *
  107. * none. *
  108. * *
  109. * OUTPUT: *
  110. * none. *
  111. * *
  112. * WARNINGS: *
  113. * none. *
  114. * *
  115. * HISTORY: *
  116. * 11/28/1994 BR : Created. *
  117. *=============================================================================================*/
  118. TriggerClass::TriggerClass(TriggerTypeClass * trigtype) :
  119. RTTI(RTTI_TRIGGER),
  120. ID(Triggers.ID(this)),
  121. Class(trigtype),
  122. AttachCount(0),
  123. Cell(0)
  124. {
  125. Class->Event1.Reset(Event1);
  126. Class->Event2.Reset(Event2);
  127. }
  128. /***********************************************************************************************
  129. * TriggerClass::~TriggerClass -- Destructor for trigger objects. *
  130. * *
  131. * This destructor will update the house blockage value if necessary. No other action need *
  132. * be performed on trigger destruction. *
  133. * *
  134. * INPUT: none *
  135. * *
  136. * OUTPUT: none *
  137. * *
  138. * WARNINGS: none *
  139. * *
  140. * HISTORY: *
  141. * 07/29/1995 JLB : Created. *
  142. *=============================================================================================*/
  143. TriggerClass::~TriggerClass(void)
  144. {
  145. if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_GENERAL) != 0) {
  146. if (LogicTriggerID >= LogicTriggers.ID(this)) {
  147. LogicTriggerID--;
  148. if (LogicTriggerID < 0 && LogicTriggers.Count() == 0) {
  149. LogicTriggerID = 0;
  150. }
  151. }
  152. }
  153. if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_MAP) != 0) {
  154. if (MapTriggerID >= MapTriggers.ID(this)) {
  155. MapTriggerID--;
  156. if (MapTriggerID < 0 && MapTriggers.Count() == 0) {
  157. MapTriggerID = 0;
  158. }
  159. }
  160. }
  161. if (GameActive && Class->House != HOUSE_NONE && Class->Action1.Action == TACTION_ALLOWWIN) {
  162. if (Houses.Ptr(Class->House)->Blockage) Houses.Ptr(Class->House)->Blockage--;
  163. Houses.Ptr(Class->House)->BorrowedTime = TICKS_PER_SECOND*4;
  164. }
  165. ID = -1;
  166. }
  167. /***********************************************************************************************
  168. * TriggerClass::Init -- clears triggers for new scenario *
  169. * *
  170. * INPUT: *
  171. * none. *
  172. * *
  173. * OUTPUT: *
  174. * none. *
  175. * *
  176. * WARNINGS: *
  177. * none. *
  178. * *
  179. * HISTORY: *
  180. * 11/29/1994 BR : Created. *
  181. *=============================================================================================*/
  182. void TriggerClass::Init(void)
  183. {
  184. Triggers.Free_All();
  185. }
  186. /***********************************************************************************************
  187. * TriggerClass::Spring -- Spring the trigger (possibly). *
  188. * *
  189. * This routine is called when a potential trigger even has occurred. The event is matched *
  190. * with the trigger event needed by this trigger. If the condition warrants, the trigger *
  191. * action is performed. *
  192. * *
  193. * INPUT: event -- The event that is occurring. *
  194. * *
  195. * obj -- If the trigger is attached to an object, this points to the object. *
  196. * *
  197. * cell -- If the trigger is attached to a cell, this is the cell number. *
  198. * *
  199. * forced -- Should the trigger be forced to execute regardless of the event? *
  200. * *
  201. * OUTPUT: bool; Was the trigger sprung? *
  202. * *
  203. * WARNINGS: none *
  204. * *
  205. * HISTORY: *
  206. * 05/31/1996 JLB : Created. *
  207. * 08/13/1996 JLB : Linked triggers supported. *
  208. *=============================================================================================*/
  209. bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool forced)
  210. {
  211. assert(Triggers.ID(this) == ID);
  212. bool e1 = Class->Event1(Event1, event, Class->House, obj, forced);
  213. bool e2 = false;
  214. bool execute = false;
  215. /*
  216. ** Forced triggers must presume that the cell parameter is invalid. It then
  217. ** uses the embedded cell value in the trigger as the official location where
  218. ** the trigger will detonate at.
  219. */
  220. if (forced) {
  221. cell = Cell;
  222. } else {
  223. /*
  224. ** Determine if the trigger event is considered to have been sprung according to the
  225. ** event control value. This might require that both events be triggered, one event
  226. ** triggered, or either event triggered to activate the trigger action.
  227. */
  228. switch (Class->EventControl) {
  229. case MULTI_ONLY:
  230. execute = e1;
  231. break;
  232. case MULTI_AND:
  233. e2 = Class->Event2(Event2, event, Class->House, obj, forced);
  234. execute = (e1 && e2);
  235. break;
  236. case MULTI_LINKED:
  237. case MULTI_OR:
  238. e2 = Class->Event2(Event2, event, Class->House, obj, forced);
  239. execute = (e1 || e2);
  240. break;
  241. }
  242. }
  243. /*
  244. ** See if the trigger is sprung with a qualifying event.
  245. */
  246. if (execute || forced) {
  247. /*
  248. ** Verify that the trigger event should really be sprung. Exceptions
  249. ** would include semi-persistent triggers that don't actually
  250. ** spring until all triggers have sprung.
  251. */
  252. if (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT) {
  253. /*
  254. ** Detach ourselves from the object and record that there
  255. ** is one less attachment to keep track of.
  256. */
  257. if (obj) {
  258. obj->Trigger = NULL;
  259. }
  260. if (cell) {
  261. Map[cell].Trigger = NULL;
  262. }
  263. /*
  264. ** If we're attached to more objects, don't spring; otherwise, spring.
  265. ** And, mark ourselves as volatile so we'll completely remove ourselves
  266. ** from the game after we go off.
  267. */
  268. AttachCount--;
  269. if (AttachCount > 0) {
  270. return(false);
  271. }
  272. }
  273. /*
  274. ** For linked trigger events, perform the action associated with the matching
  275. ** trigger event. Otherwise perform the action for both events.
  276. */
  277. bool ok = false;
  278. HousesType hh = Class->House;
  279. if (Class->EventControl == MULTI_LINKED) {
  280. if (e1 || forced) ok |= Class->Action1(hh, obj, ID, cell);
  281. if (e2 && !forced) ok |= Class->Action2(hh, obj, ID, cell);
  282. } else {
  283. switch (Class->ActionControl) {
  284. case MULTI_ONLY:
  285. ok |= Class->Action1(hh, obj, ID, cell);
  286. break;
  287. default:
  288. case MULTI_AND:
  289. ok |= Class->Action1(hh, obj, ID, cell);
  290. ok |= Class->Action2(hh, obj, ID, cell);
  291. break;
  292. }
  293. }
  294. if (!IsActive) return(true);
  295. /*
  296. ** If at least one action was performed, then consider this
  297. ** trigger to have completed and thus will be deleted if
  298. ** necessary.
  299. */
  300. if (ok) {
  301. #ifdef CHEAT_KEYS
  302. MonoArray[DMONO_STRESS].Sub_Window(61, 1, 17, 11);
  303. MonoArray[DMONO_STRESS].Scroll();
  304. MonoArray[DMONO_STRESS].Sub_Window(61, 1, 18, 11);
  305. MonoArray[DMONO_STRESS].Set_Cursor(0, 10);
  306. MonoArray[DMONO_STRESS].Printf("%02d:%02d:%02d-%s", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND, Class->IniName);
  307. MonoArray[DMONO_STRESS].Sub_Window();
  308. #endif
  309. if (Class->IsPersistant == TriggerTypeClass::VOLATILE || (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT && AttachCount <= 1)) {
  310. Detach_This_From_All(As_Target(), true);
  311. delete this;
  312. return(true);
  313. } else {
  314. /*
  315. ** Reset event data so that the event will
  316. ** repeat as necessary.
  317. */
  318. Class->Event1.Reset(Event1);
  319. Class->Event2.Reset(Event2);
  320. }
  321. }
  322. }
  323. return(false);
  324. }
  325. /***********************************************************************************************
  326. * TriggerClass::operator new -- 'new' operator *
  327. * *
  328. * INPUT: *
  329. * none. *
  330. * *
  331. * OUTPUT: *
  332. * pointer to new trigger *
  333. * *
  334. * WARNINGS: *
  335. * none. *
  336. * *
  337. * HISTORY: *
  338. * 11/28/1994 BR : Created. *
  339. *=============================================================================================*/
  340. void * TriggerClass::operator new(size_t )
  341. {
  342. void * ptr = Triggers.Allocate();
  343. if (ptr) {
  344. ((TriggerClass *)ptr)->IsActive = true;
  345. }
  346. return(ptr);
  347. }
  348. /***********************************************************************************************
  349. * TriggerClass::operator delete -- Returns a trigger to the special memory pool. *
  350. * *
  351. * This routine will return a previously allocated trigger object back to the memory *
  352. * pool from which it came. *
  353. * *
  354. * INPUT: pointer -- Pointer to the trigger to return to the memory pool. *
  355. * *
  356. * OUTPUT: none *
  357. * *
  358. * WARNINGS: none *
  359. * *
  360. * HISTORY: *
  361. * 07/09/1996 JLB : Created. *
  362. *=============================================================================================*/
  363. void TriggerClass::operator delete(void * pointer)
  364. {
  365. if (pointer) {
  366. ((TriggerClass *)pointer)->IsActive = false;
  367. }
  368. Triggers.Free((TriggerClass *)pointer);
  369. }
  370. /***********************************************************************************************
  371. * TriggerClass::As_Target -- Converts trigger to a target value *
  372. * *
  373. * Converts the trigger object into a target identifier. *
  374. * *
  375. * INPUT: none *
  376. * *
  377. * OUTPUT: TARGET value *
  378. * *
  379. * WARNINGS: none *
  380. * *
  381. * HISTORY: *
  382. * 09/19/1994 JLB : Created. *
  383. *=============================================================================================*/
  384. TARGET TriggerClass::As_Target(void) const
  385. {
  386. assert(Triggers.ID(this) == ID);
  387. return(Build_Target(RTTI_TRIGGER, ID));
  388. }
  389. /***********************************************************************************************
  390. * Find_Or_Make -- Find or create a trigger of the type specified. *
  391. * *
  392. * This routine is used when, given a trigger type, an actual trigger object is needed. *
  393. * In this case, an existing trigger of the correct type must be located, or a trigger *
  394. * object must be created. In either case, this routine will return a trigger object that *
  395. * corresponds to the trigger type class specified. *
  396. * *
  397. * INPUT: trigtype -- Pointer to the trigger type to find (or create) a matching trigger *
  398. * object. *
  399. * *
  400. * OUTPUT: Returns a pointer to a matching trigger object. If no more triggers could be *
  401. * allocated and no matching trigger could be found, then this routine will return *
  402. * NULL (a very rare case). *
  403. * *
  404. * WARNINGS: This routine could return NULL. *
  405. * *
  406. * HISTORY: *
  407. * 07/09/1996 JLB : Created. *
  408. *=============================================================================================*/
  409. TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype)
  410. {
  411. if (!trigtype) return(NULL);
  412. for (int index = 0; index < Triggers.Count(); index++) {
  413. if (trigtype == Triggers.Ptr(index)->Class) {
  414. return(Triggers.Ptr(index));
  415. }
  416. }
  417. /*
  418. ** No trigger was found, so make one.
  419. */
  420. TriggerClass * trig = new TriggerClass(trigtype);
  421. return(trig);
  422. }
  423. /***********************************************************************************************
  424. * TriggerClass::Detach -- Detach specified target from this trigger. *
  425. * *
  426. * This routine is called when the specified trigger MUST be detached from all references *
  427. * to it. The only reference maintained by a trigger is the reference to the trigger *
  428. * type class it is modeled after. *
  429. * *
  430. * INPUT: target -- The target identifier to remove all attachments to. *
  431. * *
  432. * OUTPUT: none *
  433. * *
  434. * WARNINGS: You must never detach the trigger type class from a trigger. Such a process *
  435. * will leave the trigger orphan and in a 'crash the game immediately if used' *
  436. * state. As such, this routine will throw an assertion if this is tried. *
  437. * *
  438. * HISTORY: *
  439. * 07/09/1996 JLB : Created. *
  440. *=============================================================================================*/
  441. void TriggerClass::Detach(TARGET target, bool )
  442. {
  443. if (Is_Target_TriggerType(target)) {
  444. assert((TriggerTypeClass*)Class != As_TriggerType(target));
  445. // if (Class == As_TriggerType(target)) {
  446. // Class = NULL;
  447. // }
  448. }
  449. }