// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: /CounterStrike/TRIGGER.CPP 1 3/03/97 10:26a Joe_bostic $ */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * File Name : TRIGGER.CPP * * * * Programmer : Joe L. Bostic * * * * Start Date : 11/12/94 * * * * Last Update : August 13, 1996 [JLB] * * * *---------------------------------------------------------------------------------------------* * Functions: * * Find_Or_Make -- Find or create a trigger of the type specified. * * TriggerClass::As_Target -- Converts trigger to a target value * * TriggerClass::Attaches_To -- Determines what trigger can attach to. * * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * * TriggerClass::Detach -- Detach specified target from this trigger. * * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * * TriggerClass::Init -- clears triggers for new scenario * * TriggerClass::Spring -- Spring the trigger (possibly). * * TriggerClass::TriggerClass -- constructor * * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * * TriggerClass::operator new -- 'new' operator * * TriggerClass::~TriggerClass -- Destructor for trigger objects. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" #if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) /*********************************************************************************************** * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * * * * In the rare (possibly never?) case of requiring a description for this trigger, then * * just have the model class generate a description and return that. * * * * INPUT: none * * * * OUTPUT: Returns with an ASCII description of this trigger. * * * * WARNINGS: see TriggerTypeClass::Description() * * * * HISTORY: * * 07/09/1996 JLB : Created. * *=============================================================================================*/ char const * TriggerClass::Description(void) const { return(Class->Description()); } /*********************************************************************************************** * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * * * * This routine is called when the trigger has been assigned to a list box. It will * * display a description of the trigger. * * * * INPUT: see below... * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/09/1996 JLB : Created. * *=============================================================================================*/ void TriggerClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const { RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); static int _tabs[] = {13,40}; if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { if (selected) { flags = flags | TPF_BRIGHT_COLOR; LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); } else { if (!(flags & TPF_USE_GRAD_PAL)) { flags = flags | TPF_MEDIUM_COLOR; } } Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); } else { Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); } } #endif /*********************************************************************************************** * TriggerClass::TriggerClass -- constructor * * * * INPUT: * * none. * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 11/28/1994 BR : Created. * *=============================================================================================*/ TriggerClass::TriggerClass(TriggerTypeClass * trigtype) : RTTI(RTTI_TRIGGER), ID(Triggers.ID(this)), Class(trigtype), AttachCount(0), Cell(0) { Class->Event1.Reset(Event1); Class->Event2.Reset(Event2); } /*********************************************************************************************** * TriggerClass::~TriggerClass -- Destructor for trigger objects. * * * * This destructor will update the house blockage value if necessary. No other action need * * be performed on trigger destruction. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/29/1995 JLB : Created. * *=============================================================================================*/ TriggerClass::~TriggerClass(void) { if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_GENERAL) != 0) { if (LogicTriggerID >= LogicTriggers.ID(this)) { LogicTriggerID--; if (LogicTriggerID < 0 && LogicTriggers.Count() == 0) { LogicTriggerID = 0; } } } if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_MAP) != 0) { if (MapTriggerID >= MapTriggers.ID(this)) { MapTriggerID--; if (MapTriggerID < 0 && MapTriggers.Count() == 0) { MapTriggerID = 0; } } } if (GameActive && Class->House != HOUSE_NONE && Class->Action1.Action == TACTION_ALLOWWIN) { if (Houses.Ptr(Class->House)->Blockage) Houses.Ptr(Class->House)->Blockage--; Houses.Ptr(Class->House)->BorrowedTime = TICKS_PER_SECOND*4; } ID = -1; } /*********************************************************************************************** * TriggerClass::Init -- clears triggers for new scenario * * * * INPUT: * * none. * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 11/29/1994 BR : Created. * *=============================================================================================*/ void TriggerClass::Init(void) { Triggers.Free_All(); } /*********************************************************************************************** * TriggerClass::Spring -- Spring the trigger (possibly). * * * * This routine is called when a potential trigger even has occurred. The event is matched * * with the trigger event needed by this trigger. If the condition warrants, the trigger * * action is performed. * * * * INPUT: event -- The event that is occurring. * * * * obj -- If the trigger is attached to an object, this points to the object. * * * * cell -- If the trigger is attached to a cell, this is the cell number. * * * * forced -- Should the trigger be forced to execute regardless of the event? * * * * OUTPUT: bool; Was the trigger sprung? * * * * WARNINGS: none * * * * HISTORY: * * 05/31/1996 JLB : Created. * * 08/13/1996 JLB : Linked triggers supported. * *=============================================================================================*/ bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool forced) { assert(Triggers.ID(this) == ID); bool e1 = Class->Event1(Event1, event, Class->House, obj, forced); bool e2 = false; bool execute = false; /* ** Forced triggers must presume that the cell parameter is invalid. It then ** uses the embedded cell value in the trigger as the official location where ** the trigger will detonate at. */ if (forced) { cell = Cell; } else { /* ** Determine if the trigger event is considered to have been sprung according to the ** event control value. This might require that both events be triggered, one event ** triggered, or either event triggered to activate the trigger action. */ switch (Class->EventControl) { case MULTI_ONLY: execute = e1; break; case MULTI_AND: e2 = Class->Event2(Event2, event, Class->House, obj, forced); execute = (e1 && e2); break; case MULTI_LINKED: case MULTI_OR: e2 = Class->Event2(Event2, event, Class->House, obj, forced); execute = (e1 || e2); break; } } /* ** See if the trigger is sprung with a qualifying event. */ if (execute || forced) { /* ** Verify that the trigger event should really be sprung. Exceptions ** would include semi-persistent triggers that don't actually ** spring until all triggers have sprung. */ if (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT) { /* ** Detach ourselves from the object and record that there ** is one less attachment to keep track of. */ if (obj) { obj->Trigger = NULL; } if (cell) { Map[cell].Trigger = NULL; } /* ** If we're attached to more objects, don't spring; otherwise, spring. ** And, mark ourselves as volatile so we'll completely remove ourselves ** from the game after we go off. */ AttachCount--; if (AttachCount > 0) { return(false); } } /* ** For linked trigger events, perform the action associated with the matching ** trigger event. Otherwise perform the action for both events. */ bool ok = false; HousesType hh = Class->House; if (Class->EventControl == MULTI_LINKED) { if (e1 || forced) ok |= Class->Action1(hh, obj, ID, cell); if (e2 && !forced) ok |= Class->Action2(hh, obj, ID, cell); } else { switch (Class->ActionControl) { case MULTI_ONLY: ok |= Class->Action1(hh, obj, ID, cell); break; default: case MULTI_AND: ok |= Class->Action1(hh, obj, ID, cell); ok |= Class->Action2(hh, obj, ID, cell); break; } } if (!IsActive) return(true); /* ** If at least one action was performed, then consider this ** trigger to have completed and thus will be deleted if ** necessary. */ if (ok) { #ifdef CHEAT_KEYS MonoArray[DMONO_STRESS].Sub_Window(61, 1, 17, 11); MonoArray[DMONO_STRESS].Scroll(); MonoArray[DMONO_STRESS].Sub_Window(61, 1, 18, 11); MonoArray[DMONO_STRESS].Set_Cursor(0, 10); 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); MonoArray[DMONO_STRESS].Sub_Window(); #endif if (Class->IsPersistant == TriggerTypeClass::VOLATILE || (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT && AttachCount <= 1)) { Detach_This_From_All(As_Target(), true); delete this; return(true); } else { /* ** Reset event data so that the event will ** repeat as necessary. */ Class->Event1.Reset(Event1); Class->Event2.Reset(Event2); } } } return(false); } /*********************************************************************************************** * TriggerClass::operator new -- 'new' operator * * * * INPUT: * * none. * * * * OUTPUT: * * pointer to new trigger * * * * WARNINGS: * * none. * * * * HISTORY: * * 11/28/1994 BR : Created. * *=============================================================================================*/ void * TriggerClass::operator new(size_t ) { void * ptr = Triggers.Allocate(); if (ptr) { ((TriggerClass *)ptr)->IsActive = true; } return(ptr); } /*********************************************************************************************** * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * * * * This routine will return a previously allocated trigger object back to the memory * * pool from which it came. * * * * INPUT: pointer -- Pointer to the trigger to return to the memory pool. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/09/1996 JLB : Created. * *=============================================================================================*/ void TriggerClass::operator delete(void * pointer) { if (pointer) { ((TriggerClass *)pointer)->IsActive = false; } Triggers.Free((TriggerClass *)pointer); } /*********************************************************************************************** * TriggerClass::As_Target -- Converts trigger to a target value * * * * Converts the trigger object into a target identifier. * * * * INPUT: none * * * * OUTPUT: TARGET value * * * * WARNINGS: none * * * * HISTORY: * * 09/19/1994 JLB : Created. * *=============================================================================================*/ TARGET TriggerClass::As_Target(void) const { assert(Triggers.ID(this) == ID); return(Build_Target(RTTI_TRIGGER, ID)); } /*********************************************************************************************** * Find_Or_Make -- Find or create a trigger of the type specified. * * * * This routine is used when, given a trigger type, an actual trigger object is needed. * * In this case, an existing trigger of the correct type must be located, or a trigger * * object must be created. In either case, this routine will return a trigger object that * * corresponds to the trigger type class specified. * * * * INPUT: trigtype -- Pointer to the trigger type to find (or create) a matching trigger * * object. * * * * OUTPUT: Returns a pointer to a matching trigger object. If no more triggers could be * * allocated and no matching trigger could be found, then this routine will return * * NULL (a very rare case). * * * * WARNINGS: This routine could return NULL. * * * * HISTORY: * * 07/09/1996 JLB : Created. * *=============================================================================================*/ TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype) { if (!trigtype) return(NULL); for (int index = 0; index < Triggers.Count(); index++) { if (trigtype == Triggers.Ptr(index)->Class) { return(Triggers.Ptr(index)); } } /* ** No trigger was found, so make one. */ TriggerClass * trig = new TriggerClass(trigtype); return(trig); } /*********************************************************************************************** * TriggerClass::Detach -- Detach specified target from this trigger. * * * * This routine is called when the specified trigger MUST be detached from all references * * to it. The only reference maintained by a trigger is the reference to the trigger * * type class it is modeled after. * * * * INPUT: target -- The target identifier to remove all attachments to. * * * * OUTPUT: none * * * * WARNINGS: You must never detach the trigger type class from a trigger. Such a process * * will leave the trigger orphan and in a 'crash the game immediately if used' * * state. As such, this routine will throw an assertion if this is tried. * * * * HISTORY: * * 07/09/1996 JLB : Created. * *=============================================================================================*/ void TriggerClass::Detach(TARGET target, bool ) { if (Is_Target_TriggerType(target)) { assert((TriggerTypeClass*)Class != As_TriggerType(target)); // if (Class == As_TriggerType(target)) { // Class = NULL; // } } }