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