CnC_Remastered_Collection/TIBERIANDAWN/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

1484 lines
63 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: F:\projects\c&c\vcs\code\trigger.cpv 2.17 16 Oct 1995 16:51:20 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 27, 1995 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. *
* TriggerClass::Action_From_Name -- retrieves ActionType for given name *
* TriggerClass::Action_Need_Team -- Determines if this action event requires a team. *
* TriggerClass::As_Pointer -- returns pointer for the given trigger name *
* TriggerClass::As_Target -- Converts trigger to a target value *
* TriggerClass::Event_From_Name -- retrieves EventType for given name *
* TriggerClass::Event_Need_Data -- Determines if this event requires a data value. *
* TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. *
* TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. *
* TriggerClass::Init -- clears triggers for new scenario *
* TriggerClass::Name_From_Action -- retrieves name for ActionType *
* TriggerClass::Name_From_Event -- retrieves name for EventType *
* TriggerClass::Read_INI -- reads triggers from the INI file *
* TriggerClass::Remove -- removes this trigger from the game *
* TriggerClass::Spring -- Trigger processing routine for cell-based triggers *
* TriggerClass::Spring -- Trigger processing routine for house-based triggers *
* TriggerClass::Spring -- Trigger processing routine for object-based triggers *
* TriggerClass::TriggerClass -- constructor *
* TriggerClass::Validate -- validates trigger pointer *
* TriggerClass::Write_INI -- writes triggers to the INI file *
* TriggerClass::operator delete -- 'delete' operator *
* TriggerClass::operator new -- 'new' operator *
* TriggerClass::~TriggerClass -- Destructor for trigger objects. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
static void Do_All_To_Hunt(void);
#define FIXUP 0
/*
********************************** Globals **********************************
*/
static const char * EventText[EVENT_COUNT + 1] = {
"None",
"Player Enters",
"Discovered",
"Attacked",
"Destroyed",
"Any",
"House Discov.",
"Units Destr.",
"Bldgs Destr.",
"All Destr.",
"Credits",
"Time",
"# Bldgs Dstr.",
"# Units Dstr.",
"No Factories",
"Civ. Evac.",
"Built It"
};
static const char * ActionText[TriggerClass::ACTION_COUNT + 1] = {
"None",
"Win",
"Lose",
"Production",
"Create Team",
"Dstry Teams",
"All to Hunt",
"Reinforce.",
"DZ at 'Z'",
"Airstrike",
"Nuclear Missile",
"Ion Cannon",
"Dstry Trig 'XXXX'",
"Dstry Trig 'YYYY'",
"Dstry Trig 'ZZZZ'",
"Autocreate",
"Cap=Win/Des=Lose",
"Allow Win"
};
/***********************************************************************************************
* TriggerClass::Validate -- validates trigger pointer *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* 1 = ok, 0 = error *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 08/09/1995 BRR : Created. *
*=============================================================================================*/
#ifdef CHEAT_KEYS
int TriggerClass::Validate(void) const
{
int num;
num = Triggers.ID(this);
if (num < 0 || num >= TRIGGER_MAX) {
Validate_Error("TRIGGER");
return (0);
}
else
return (1);
}
#else
#define Validate()
#endif
/***********************************************************************************************
* TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. *
* *
* This routine determines if the specified event must be attached to an object. Such *
* events can only exist in a parasitic fashion attached to object(s) in the game. *
* *
* INPUT: event -- The event type to examine. *
* *
* OUTPUT: Does the specified event require attachement to an object? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/27/1995 JLB : Created. *
*=============================================================================================*/
bool TriggerClass::Event_Need_Object(EventType event)
{
switch (event) {
case EVENT_PLAYER_ENTERED:
case EVENT_DISCOVERED:
case EVENT_ATTACKED:
case EVENT_DESTROYED:
case EVENT_ANY:
return(true);
}
return(false);
}
/***********************************************************************************************
* TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. *
* *
* This routine is used to determine if the specified event requires a house identifier. *
* All trigger events that affect a house will require a house identifier. *
* *
* INPUT: event -- The event type to examine. *
* *
* OUTPUT: Does the specified event type require a house identifier? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/27/1995 JLB : Created. *
*=============================================================================================*/
bool TriggerClass::Event_Need_House(EventType event)
{
switch (event) {
case EVENT_PLAYER_ENTERED:
case EVENT_HOUSE_DISCOVERED:
case EVENT_UNITS_DESTROYED:
case EVENT_BUILDINGS_DESTROYED:
case EVENT_ALL_DESTROYED:
case EVENT_CREDITS:
case EVENT_TIME:
case EVENT_NBUILDINGS_DESTROYED:
case EVENT_NUNITS_DESTROYED:
case EVENT_NOFACTORIES:
case EVENT_EVAC_CIVILIAN:
case EVENT_BUILD:
return(true);
}
return(false);
}
/***********************************************************************************************
* TriggerClass::Event_Need_Data -- Determines if this event requires a data value. *
* *
* This routine will determine if the specified event requires a data number parameter. *
* This is commonly needed for trigger events. *
* *
* INPUT: event -- The event to examine. *
* *
* OUTPUT: Does the specified event require a data number parameter? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/27/1995 JLB : Created. *
*=============================================================================================*/
bool TriggerClass::Event_Need_Data(EventType event)
{
switch (event) {
case EVENT_CREDITS:
case EVENT_TIME:
case EVENT_NBUILDINGS_DESTROYED:
case EVENT_NUNITS_DESTROYED:
case EVENT_BUILD:
return(true);
}
return(false);
}
/***********************************************************************************************
* TriggerClass::Action_Need_Team -- Determines if this action event requires a team. *
* *
* This routine will determine if the specified action requires a team name parameter. *
* Typically, this is needed for reinforcements or other trigger events that affect *
* a particular team type. *
* *
* INPUT: action -- The action that is to be examined. *
* *
* OUTPUT: Does the specified action require a team type name? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/27/1995 JLB : Created. *
*=============================================================================================*/
bool TriggerClass::Action_Need_Team(TriggerClass::ActionType action)
{
switch (action) {
case ACTION_CREATE_TEAM:
case ACTION_DESTROY_TEAM:
case ACTION_REINFORCEMENTS:
return(true);
}
return(false);
}
/***********************************************************************************************
* TriggerClass::TriggerClass -- constructor *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/28/1994 BR : Created. *
*=============================================================================================*/
TriggerClass::TriggerClass(void)
{
IsPersistant = VOLATILE;
AttachCount = 0;
Event = EVENT_NONE;
Action = ACTION_NONE;
House = HOUSE_NONE;
DataCopy = Data = 0L;
Name[0] = '\0';
Team = NULL;
}
/***********************************************************************************************
* 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 && House != HOUSE_NONE && Action == ACTION_ALLOWWIN) {
if (Houses.Ptr(House)->Blockage) Houses.Ptr(House)->Blockage--;
Houses.Ptr(House)->BorrowedTime = TICKS_PER_SECOND*4;
}
}
/***********************************************************************************************
* 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 -- Trigger processing routine *
* *
* Checks whether this trigger should "spring" for the given event & object; *
* If it should, then some really cool undocumented stuff magically happens. *
* *
* INPUT: *
* event EventType: What happened? *
* object Ptr to object containing this trigger: What did it happen to? *
* *
* OUTPUT: *
* 0 = nothing happened; 1 = the trigger was sprung *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 12/06/1994 BR : Created. *
* 06/25/1995 JLB : Added more trigger events. *
*=============================================================================================*/
bool TriggerClass::Spring(EventType event, ObjectClass *obj)
{
Validate();
/*
** If this is not the event for this trigger, just return.
*/
if (event != Event && Event != EVENT_ANY) {
return(false);
}
/*
** If time-based, decrement the minute counter; return if it's not time yet
*/
if (Event == EVENT_TIME) {
Data--;
if (Data > 0) {
return(false);
}
Data = DataCopy;
}
/*
** Semi-persistant trigger: first detach it from the calling object, then
** see if this is the last object we're attached to; if so, the trigger
** will spring.
*/
if (IsPersistant == SEMIPERSISTANT) {
/*
** Detach ourselves from the object
*/
obj->Trigger = NULL;
/*
** Decrement our attachment counter
*/
AttachCount--;
/*
** 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.
*/
if (AttachCount > 0) {
return(false);
} else {
IsPersistant = VOLATILE;
}
}
/*
** Otherwise, take an appropriate action.
*/
bool success = true;
TriggerClass * trig = NULL;
switch (Action) {
case ACTION_NUKE:
HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false);
HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD);
break;
case ACTION_ION:
HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false);
HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD);
break;
case ACTION_WINLOSE:
switch (event) {
case EVENT_DESTROYED:
if (!PlayerPtr->IsToWin || PlayerPtr->Blockage > 0) PlayerPtr->Flag_To_Lose();
success = true;
break;
case EVENT_PLAYER_ENTERED:
if (!PlayerPtr->IsToLose) PlayerPtr->Flag_To_Win();
success = true;
break;
default:
success = false;
break;
}
break;
case ACTION_DESTROY_XXXX:
trig = As_Pointer("XXXX");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_DESTROY_YYYY:
trig = As_Pointer("YYYY");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_DESTROY_ZZZZ:
trig = As_Pointer("ZZZZ");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_AIRSTRIKE:
PlayerPtr->IsAirstrikePending = true;
// PlayerPtr->Make_Air_Strike_Available(true);
break;
case ACTION_DZ:
new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25]));
break;
case ACTION_NONE:
break;
case ACTION_WIN:
PlayerPtr->Flag_To_Win();
break;
case ACTION_LOSE:
PlayerPtr->Flag_To_Lose();
break;
case ACTION_BEGIN_PRODUCTION:
HouseClass::As_Pointer(House)->Begin_Production();
break;
case ACTION_AUTOCREATE:
if (obj && obj->Is_Techno()) {
((TechnoClass *)obj)->House->IsAlerted = true;
}
break;
case ACTION_CREATE_TEAM:
if (Team) {
ScenarioInit++;
Team->Create_One_Of();
ScenarioInit--;
}
break;
case ACTION_DESTROY_TEAM:
if (Team) {
Team->Destroy_All_Of();
}
break;
case ACTION_REINFORCEMENTS:
if (Team) {
success = Do_Reinforcements(Team);
}
break;
case ACTION_ALL_HUNT:
Do_All_To_Hunt();
break;
default:
break;
}
if (!success && Event == EVENT_TIME) Data = 1;
/*
** Remove trigger from the game.
*/
if (success && IsPersistant == VOLATILE) {
Remove();
}
return(true);
}
/***********************************************************************************************
* TriggerClass::Spring -- Trigger processing routine *
* *
* This version of Spring is for cell-based triggers. *
* *
* INPUT: *
* data elapsed time, or credits, depending on what 'Event' is. *
* *
* OUTPUT: *
* 0 = nothing happened; 1 = the trigger was sprung *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 12/06/1994 BR : Created. *
*=============================================================================================*/
bool TriggerClass::Spring(EventType event, CELL cell)
{
Validate();
/*
** If this is not the event for this trigger, just return.
*/
if (event != Event) {
return(false);
}
/*
** If time-based, decrement the minute counter; return if it's not time yet
*/
if (Event == EVENT_TIME) {
Data--;
if (Data > 0) {
return(false);
}
Data = DataCopy;
}
/*
** Semi-persistant trigger: first detach it from the calling cell, then
** see if this is the last cell we're attached to; if so, the trigger
** will spring.
*/
if (IsPersistant == SEMIPERSISTANT) {
/*
** Detach ourselves from the cell
*/
Map[cell].IsTrigger = 0;
/*
** Decrement our attachment counter
*/
AttachCount--;
/*
** If we're attached to more cells, don't spring; otherwise, spring.
** And, mark ourselves as volatile so we'll completely remove ourselves
** from the game after we go off.
*/
if (AttachCount > 0) {
return(false);
} else {
IsPersistant = VOLATILE;
}
}
/*
** Otherwise, take an appropriate action.
*/
bool success = true;
TriggerClass * trig = NULL;
int index;
switch (Action) {
case ACTION_NUKE:
HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false);
HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD);
break;
case ACTION_ION:
HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false);
HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD);
break;
case ACTION_AUTOCREATE:
for (index = 0; index < Houses.Count(); index++) {
Houses.Ptr(index)->IsAlerted = true;
}
break;
case ACTION_DESTROY_XXXX:
trig = As_Pointer("XXXX");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_DESTROY_YYYY:
trig = As_Pointer("YYYY");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_DESTROY_ZZZZ:
trig = As_Pointer("ZZZZ");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_AIRSTRIKE:
HouseClass::As_Pointer(House)->AirStrike.Enable(false, true);
if (House == PlayerPtr->Class->House) {
PlayerPtr->AirStrike.Forced_Charge(true);
Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE);
Map.Column[1].Flag_To_Redraw();
}
// PlayerPtr->Make_Air_Strike_Available(true);
break;
case ACTION_DZ:
new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25]));
break;
case ACTION_NONE:
break;
case ACTION_WIN:
PlayerPtr->Flag_To_Win();
break;
case ACTION_LOSE:
PlayerPtr->Flag_To_Lose();
break;
case ACTION_BEGIN_PRODUCTION:
if (PlayerPtr->Class->House == HOUSE_GOOD) {
HouseClass::As_Pointer(HOUSE_BAD)->Begin_Production();
} else {
HouseClass::As_Pointer(HOUSE_GOOD)->Begin_Production();
}
break;
case ACTION_CREATE_TEAM:
if (Team) {
ScenarioInit++;
Team->Create_One_Of();
ScenarioInit--;
}
break;
case ACTION_DESTROY_TEAM:
if (Team) {
Team->Destroy_All_Of();
}
break;
case ACTION_REINFORCEMENTS:
if (Team) {
success = Do_Reinforcements(Team);
}
break;
case ACTION_ALL_HUNT:
Do_All_To_Hunt();
break;
default:
break;
}
if (!success && Event == EVENT_TIME) Data = 1;
/*
** Remove trigger from the game.
*/
if (success && IsPersistant == VOLATILE) {
Remove();
}
return(true);
}
/***********************************************************************************************
* TriggerClass::Spring -- Trigger processing routine *
* *
* This version of Spring is for house-specific triggers. *
* For a time-based trigger, 'data' will the the current TickCount. *
* For a credit-based trigger, 'data' will be the credits for the HouseClass *
* containing this trigger. *
* *
* INPUT: *
* event the event that happened *
* house house that this event relates to *
* data elapsed time, or credits, depending on what 'Event' is. *
* *
* OUTPUT: *
* 0 = nothing happened; 1 = the trigger was sprung *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 12/06/1994 BR : Created. *
* 06/25/1995 JLB : Added more trigger events. *
*=============================================================================================*/
bool TriggerClass::Spring(EventType event, HousesType house, long data)
{
Validate();
/*
** If this is not the event for this trigger, just return.
*/
if (event != Event || house != House) {
return(false);
}
/*
** If credits-based, check 'data'
*/
if (Event == EVENT_CREDITS && data < Data) {
return(false);
}
/*
** Building event check to ensure that the building number matches.
*/
if (Event == EVENT_BUILD && data != Data) {
return(false);
}
/*
** Number of objects destroyed checker. If the data supplied indicates that
** the correct number of objects have been destroyed, then this trigger
** will succeed.
*/
if (Event == EVENT_NBUILDINGS_DESTROYED || Event == EVENT_NUNITS_DESTROYED) {
if (data < Data) {
return(false);
}
}
/*
** If time-based, decrement the minute counter; return if it's not time yet
*/
if (Event == EVENT_TIME) {
Data--;
if (Data > 0) {
return(false);
}
Data = DataCopy;
}
/*
** The trigger has gone off; take appropriate action
*/
bool success = true;
TriggerClass * trig = NULL;
switch (Action) {
case ACTION_NUKE:
HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false);
HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD);
break;
case ACTION_ION:
HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false);
HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD);
break;
/*
** This will remove a blockage to the win condition. No action need
** be performed here since the act of deleting the trigger will
** remove the blockage.
*/
case ACTION_ALLOWWIN:
break;
case ACTION_AUTOCREATE:
HouseClass::As_Pointer(House)->IsAlerted = true;
break;
case ACTION_DESTROY_XXXX:
trig = As_Pointer("XXXX");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_DESTROY_YYYY:
trig = As_Pointer("YYYY");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_DESTROY_ZZZZ:
trig = As_Pointer("ZZZZ");
if (trig) {
trig->Remove();
}
delete trig;
break;
case ACTION_AIRSTRIKE:
PlayerPtr->AirStrike.Enable(false, true);
if (House == PlayerPtr->Class->House) {
PlayerPtr->AirStrike.Forced_Charge(true);
Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE);
Map.Column[1].Flag_To_Redraw();
}
break;
case ACTION_NONE:
break;
case ACTION_DZ:
new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25]));
break;
case ACTION_WIN:
PlayerPtr->Flag_To_Win();
break;
case ACTION_LOSE:
PlayerPtr->Flag_To_Lose();
break;
case ACTION_BEGIN_PRODUCTION:
HouseClass::As_Pointer(House)->Begin_Production();
break;
case ACTION_CREATE_TEAM:
if (Team) {
ScenarioInit++;
Team->Create_One_Of();
ScenarioInit--;
}
break;
case ACTION_DESTROY_TEAM:
if (Team) {
Team->Destroy_All_Of();
}
break;
case ACTION_REINFORCEMENTS:
if (Team) {
success = Do_Reinforcements(Team);
}
break;
case ACTION_ALL_HUNT:
Do_All_To_Hunt();
break;
default:
break;
}
if (!success && Event == EVENT_TIME) Data = 1;
/*
** Remove trigger from the game.
*/
if (success && IsPersistant == VOLATILE) {
Remove();
}
return(true);
}
/***********************************************************************************************
* TriggerClass::Remove -- removes this trigger from the game *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* 1 = trigger was removed, 0 = it wasn't *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 12/06/1994 BR : Created. *
*=============================================================================================*/
bool TriggerClass::Remove(void)
{
Validate();
CELL cell;
HousesType h;
int index;
/*
** Loop through all cells; remove any reference to this trigger
*/
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
if (Map[cell].IsTrigger) {
if (CellTriggers[cell] == this) {
Map[cell].IsTrigger = 0;
CellTriggers[cell] = NULL;
}
}
}
/*
** Loop through all objects, removing any reference to this trigger
*/
for (index = 0; index < Infantry.Count(); index++) {
if (Infantry.Ptr(index)->Trigger == this) {
Infantry.Ptr(index)->Trigger = NULL;
}
}
for (index = 0; index < Buildings.Count(); index++) {
if (Buildings.Ptr(index)->Trigger == this) {
Buildings.Ptr(index)->Trigger = NULL;
}
}
for (index = 0; index < Units.Count(); index++) {
if (Units.Ptr(index)->Trigger == this) {
Units.Ptr(index)->Trigger = NULL;
}
}
for (index = 0; index < Terrains.Count(); index++) {
if (Terrains.Ptr(index)->Trigger == this) {
Terrains.Ptr(index)->Trigger = NULL;
}
}
/*
** Remove this trigger from any house list it's in. Invoking '-=' with a
** pointer not in the list has no effect; loop through all houses just to
** be on the safe side.
*/
for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) {
HouseTriggers[h].Delete(this);
}
delete this;
return(true);
}
/***********************************************************************************************
* TriggerClass::Read_INI -- reads triggers from the INI file *
* *
* INI entry format: *
* Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant *
* *
* This routine reads in the triggers & creates them. Then, other classes can *
* get pointers to the triggers they're linked to. *
* *
* The routine relies on the TeamTypeClasses already being loaded so it can resolve *
* references to teams in this function. *
* *
* Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), *
* and cleared in the Map::Init() routine (which clears all cell objects to 0's). *
* *
* Object's pointers are set in: *
* InfantryClass::Read_INI() *
* BuildingClass::Read_INI() *
* UnitClass::Read_INI() *
* TerrainClass::Read_INI() *
* The object trigger pointers are cleared in the ObjectClass constructor. *
* *
* The House's EMSListOf triggers is set in this routine, and cleared in the *
* HouseClass::Init() routine. *
* *
* INPUT: *
* buffer buffer to hold the INI data *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* This function must be called before any other class's Read_INI. *
* *
* HISTORY: *
* 11/28/1994 BR : Created. *
*=============================================================================================*/
void TriggerClass::Read_INI(char *buffer)
{
TriggerClass *trigger; // Working trigger pointer.
char *tbuffer; // Accumulation buffer of trigger IDs.
int len; // Length of data in buffer.
char buf[128];
/*
** Set 'tbuffer' to point just past the INI buffer
*/
len = strlen(buffer) + 2;
tbuffer = buffer + len;
/*
** Read all TRIGGER entry names into 'tbuffer'
*/
WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer);
/*
** Loop for all trigger entries.
*/
while (*tbuffer != '\0') {
/*
** Create a new trigger.
*/
trigger = new TriggerClass();
/*
** Set its name.
*/
trigger->Set_Name (tbuffer);
/*
** Get the trigger entry.
*/
WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer);
/*
** Fill in the trigger.
*/
trigger->Fill_In(tbuffer,buf);
/*
** Add 'trigger' to the House's list.
*/
// if (trigger->House != HOUSE_NONE && trigger->Event != EVENT_PLAYER_ENTERED) {
// if (Event_Need_House(trigger->Event) && !Event_Need_Object(trigger->Event)) {
if (trigger->House != HOUSE_NONE) {
if (trigger->Action == ACTION_ALLOWWIN) HouseClass::As_Pointer(trigger->House)->Blockage++;
HouseTriggers[trigger->House].Add(trigger);
trigger->AttachCount++;
}
/*
** Go to next entry.
*/
tbuffer += strlen(tbuffer)+1;
}
}
/***********************************************************************************************
* TriggerClass::Fill_In -- fills in trigger from the given INI entry *
* *
* This routine fills in the given trigger with the given name, and values from *
* the given INI entry. *
* *
* (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) *
* *
* INI entry format: *
* Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant *
* *
* INPUT: *
* name mnemonic for the desired trigger *
* entry INI entry to parse *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/28/1994 BR : Created. *
*=============================================================================================*/
void TriggerClass::Fill_In(char * name, char *entry)
{
Validate();
char *p;
/*
** Set its name.
*/
Set_Name(name);
/*
** 1st token: Event.
*/
Event = Event_From_Name(strtok(entry, ","));
/*
** 2nd token: Action.
*/
Action = Action_From_Name(strtok(NULL, ","));
/*
** 3rd token: Data.
*/
DataCopy = Data = atol(strtok(NULL, ","));
/*
** 4th token: House.
*/
House = HouseTypeClass::From_Name(strtok(NULL, ","));
if (House == HOUSE_NONE && Event == EVENT_PLAYER_ENTERED) {
House = PlayerPtr->Class->House;
}
/*
** 5th token: Team.
*/
Team = TeamTypeClass::As_Pointer(strtok(NULL, ","));
/*
** 6th token: IsPersistant. This token was added later, so we must check
** for its existence.
*/
p = strtok(NULL, ",");
if (p) {
IsPersistant = (PersistantType)atoi(p);
} else {
IsPersistant = VOLATILE;
}
}
/***********************************************************************************************
* TriggerClass::Write_INI -- writes triggers to the INI file *
* *
* INI entry format: *
* Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant *
* *
* INPUT: *
* buffer buffer to hold the INI data *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/28/1994 BR : Created. *
*=============================================================================================*/
void TriggerClass::Write_INI(char *buffer, bool refresh)
{
int index;
char buf[128];
TriggerClass *trigger;
char const *hname;
char const *tname;
/*
** First, clear out all existing trigger data from the INI file.
*/
if (refresh) {
WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer);
}
/*
** Now write all the trigger data out
*/
for (index = 0; index < Triggers.Count(); index++) {
/*
** Get ptr to next active trigger.
*/
trigger = Triggers.Ptr(index);
/*
** Generate INI entry.
*/
if (trigger->House==HOUSE_NONE) {
hname = "None";
} else {
hname = HouseClass::As_Pointer(trigger->House)->Class->IniName;
}
if (trigger->Team==NULL) {
tname = "None";
} else {
tname = trigger->Team->IniName;
}
sprintf(buf,"%s,%s,%ld,%s,%s,%d",
TriggerClass::Name_From_Event(trigger->Event),
TriggerClass::Name_From_Action(trigger->Action),
trigger->Data,
hname,
tname,
trigger->IsPersistant);
WWWritePrivateProfileString(INI_Name(), trigger->Get_Name(), buf, buffer);
}
}
/***********************************************************************************************
* TriggerClass::As_Pointer -- returns pointer for the given trigger name *
* *
* This function is designed for use at initialization time, ie when the *
* trigger mnemonics are read from the INI and the Read_INI functions need *
* to get a pointer to that trigger. *
* *
* INPUT: *
* name mnemonic for the desired trigger *
* *
* OUTPUT: *
* near pointer to that trigger, NULL if not found *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/28/1994 BR : Created. *
*=============================================================================================*/
TriggerClass * TriggerClass::As_Pointer(char const * name)
{
if (name == NULL) {
return(NULL);
}
for (int i = 0; i < Triggers.Count(); i++) {
TriggerClass * trigger = Triggers.Ptr(i);
if (!stricmp(name, trigger->Name)) {
return(trigger);
}
}
return(NULL);
}
/***********************************************************************************************
* 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 -- 'delete' operator *
* *
* INPUT: *
* ptr pointer to delete *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/28/1994 BR : Created. *
*=============================================================================================*/
void TriggerClass::operator delete(void *ptr)
{
if (ptr) {
((TriggerClass *)ptr)->IsActive = false;
}
Triggers.Free((TriggerClass *)ptr);
}
/***********************************************************************************************
* TriggerClass::Event_From_Name -- retrieves EventType for given name *
* *
* INPUT: *
* name name to get event for *
* *
* OUTPUT: *
* EventType for given name *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/29/1994 BR : Created. *
*=============================================================================================*/
EventType TriggerClass::Event_From_Name (char const *name)
{
int i;
if (name == NULL) {
return(EVENT_NONE);
}
for (i = EVENT_NONE; i < EVENT_COUNT; i++) {
if (!stricmp(name,EventText[i + 1])) {
return((EventType)i);
}
}
return(EVENT_NONE);
}
/***********************************************************************************************
* TriggerClass::Name_From_Event -- retrieves name for EventType *
* *
* INPUT: *
* event EventType to get name for *
* *
* OUTPUT: *
* name for EventType *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/29/1994 BR : Created. *
*=============================================================================================*/
char const *TriggerClass::Name_From_Event(EventType event)
{
return(EventText[event + 1]);
}
/***********************************************************************************************
* TriggerClass::Action_From_Name -- retrieves ActionType for given name *
* *
* INPUT: *
* name name to get ActionType for *
* *
* OUTPUT: *
* ActionType for given name *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/29/1994 BR : Created. *
*=============================================================================================*/
TriggerClass::ActionType TriggerClass::Action_From_Name (char const *name)
{
int i;
if (name == NULL) {
return(ACTION_NONE);
}
for (i = ACTION_NONE; i < ACTION_COUNT; i++) {
if (!stricmp(name,ActionText[i + 1])) {
return((ActionType)i);
}
}
return(ACTION_NONE);
}
/***********************************************************************************************
* TriggerClass::Name_From_Action -- retrieves name for ActionType *
* *
* INPUT: *
* action ActionType to get name for *
* *
* OUTPUT: *
* name of ActionType *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/29/1994 BR : Created. *
*=============================================================================================*/
char const *TriggerClass::Name_From_Action(ActionType action)
{
return(ActionText[action + 1]);
}
/***********************************************************************************************
* TriggerClass::As_Target -- Converts trigger to a target value *
* *
* INPUT: none *
* *
* OUTPUT: TARGET value *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
TARGET TriggerClass::As_Target(void) const
{
Validate();
return(Build_Target(KIND_TRIGGER, Triggers.ID(this)));
}
/***********************************************************************************************
* Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. *
* *
* This trigger action will cause the computer units and infantry to go into hunt mode. *
* Use it to bring a scenario to a sudden conclusion. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 04/20/1995 JLB : Created. *
* 08/14/1995 JLB : Removes the member from a team if necessary. *
*=============================================================================================*/
static void Do_All_To_Hunt(void)
{
int index;
for (index = 0; index < Units.Count(); index++) {
UnitClass * unit = Units.Ptr(index);
if (!unit->House->IsHuman && unit->IsDown && !unit->IsInLimbo) {
if (unit->Team) unit->Team->Remove(unit);
unit->Assign_Mission(MISSION_HUNT);
}
}
for (index = 0; index < Infantry.Count(); index++) {
InfantryClass * infantry = Infantry.Ptr(index);
if (!infantry->House->IsHuman && infantry->IsDown && !infantry->IsInLimbo) {
if (infantry->Team) infantry->Team->Remove(infantry);
infantry->Assign_Mission(MISSION_HUNT);
}
}
}