TRIGGER.CPP 27 KB

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