CnC_Remastered_Collection/REDALERT/TRIGGER.CPP
PG-SteveT 03416d24e1 Initial Source Code commit
Initial commit of original Tiberian Dawn and Red Alert source code converted to build as DLLs, and compatible with the release version of Command & Conquer Remastered.
2020-05-27 12:16:20 -07:00

495 lines
27 KiB
C++

//
// 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;
// }
}
}