343 lines
No EOL
14 KiB
C++
343 lines
No EOL
14 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\logic.cpv 2.17 16 Oct 1995 16:50:52 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 : LOGIC.CPP *
|
||
* *
|
||
* Programmer : Joe L. Bostic *
|
||
* *
|
||
* Start Date : September 27, 1993 *
|
||
* *
|
||
* Last Update : December 23, 1994 [JLB] *
|
||
* *
|
||
*---------------------------------------------------------------------------------------------*
|
||
* Functions: *
|
||
* LogicClass::AI -- Handles AI logic processing for game objects. *
|
||
* LogicClass::Debug_Dump -- Displays logic class status to the mono screen. *
|
||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
||
#include "function.h"
|
||
#include "logic.h"
|
||
|
||
static unsigned FramesPerSecond=0;
|
||
|
||
#ifdef CHEAT_KEYS
|
||
|
||
static unsigned TotalFrames;
|
||
static unsigned FPSDivider = 1;
|
||
static unsigned AverageFramesPerSecond;
|
||
|
||
/***********************************************************************************************
|
||
* LogicClass::Debug_Dump -- Displays logic class status to the mono screen. *
|
||
* *
|
||
* This is a debugging support routine. It displays the current state of the logic class *
|
||
* to the monochrome monitor. It assumes that it is being called once per second. *
|
||
* *
|
||
* INPUT: none *
|
||
* *
|
||
* OUTPUT: none *
|
||
* *
|
||
* WARNINGS: Call this routine only once per second. *
|
||
* *
|
||
* HISTORY: *
|
||
* 05/31/1994 JLB : Created. *
|
||
*=============================================================================================*/
|
||
void LogicClass::Debug_Dump(MonoClass *mono) const
|
||
{
|
||
#define RECORDCOUNT 40
|
||
#define RECORDHEIGHT 21
|
||
static struct {
|
||
int Graphic;
|
||
} _record[RECORDCOUNT];
|
||
static int _framecounter = 0;
|
||
|
||
TotalFrames+= FramesPerSecond;
|
||
AverageFramesPerSecond = TotalFrames/FPSDivider++;
|
||
|
||
mono->Set_Cursor(21, 9);
|
||
mono->Print(
|
||
"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ\r"
|
||
"<EFBFBD>Units.....<2E> <20>Frame Rate: Avg: Frame: <20>\r"
|
||
"<EFBFBD>Infantry..<2E> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ\r"
|
||
"<EFBFBD>Aircraft..<2E> <20> <20>\r"
|
||
"<EFBFBD>Buildings.<2E> <20> <20>\r"
|
||
"<EFBFBD>Terrain...<2E> <20> <20>\r"
|
||
"<EFBFBD>Bullets...<2E> <20> <20>\r"
|
||
"<EFBFBD>Anims.....<2E> <20> <20>\r"
|
||
"<EFBFBD>Teams.....<2E> <20> Ĵ\r"
|
||
"<EFBFBD>Triggers..<2E> <20> <20>\r"
|
||
"<EFBFBD>Factories.<2E> <20> <20>\r"
|
||
"<EFBFBD> <20> <20> <20>\r"
|
||
"<EFBFBD> <20> <20> <20>\r"
|
||
"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĴSpare CPU Time<6D><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>\r");
|
||
|
||
_framecounter++;
|
||
mono->Set_Cursor(70, 10);mono->Printf("%ld", Frame);
|
||
if (ScenarioInit) {
|
||
mono->Set_Cursor(21, 9);mono->Printf("%d", ScenarioInit);
|
||
}
|
||
|
||
mono->Set_Cursor(33, 10);mono->Printf("%3d", Units.Count());
|
||
mono->Set_Cursor(33, 11);mono->Printf("%3d", Infantry.Count());
|
||
mono->Set_Cursor(33, 12);mono->Printf("%3d", Aircraft.Count());
|
||
mono->Set_Cursor(33, 13);mono->Printf("%3d", Buildings.Count());
|
||
mono->Set_Cursor(33, 14);mono->Printf("%3d", Terrains.Count());
|
||
mono->Set_Cursor(33, 15);mono->Printf("%3d", Bullets.Count());
|
||
mono->Set_Cursor(33, 16);mono->Printf("%3d", Anims.Count());
|
||
mono->Set_Cursor(33, 17);mono->Printf("%3d", Teams.Count());
|
||
mono->Set_Cursor(33, 18);mono->Printf("%3d", Triggers.Count());
|
||
mono->Set_Cursor(33, 19);mono->Printf("%3d", Factories.Count());
|
||
|
||
mono->Set_Cursor(48, 10);mono->Printf("%d", FramesPerSecond);
|
||
mono->Set_Cursor(58, 10);mono->Printf("%d", AverageFramesPerSecond);
|
||
|
||
/*
|
||
** Advance to the next recorded performance record. If the record buffer
|
||
** is full then throw out the oldest record.
|
||
*/
|
||
memcpy(&_record[0], &_record[1], sizeof(_record[0])*(RECORDCOUNT-1));
|
||
|
||
/*
|
||
** Fill in the data for the current frame's performance record.
|
||
*/
|
||
SpareTicks = MIN((long)SpareTicks, (long)TIMER_SECOND);
|
||
_record[RECORDCOUNT-1].Graphic = Fixed_To_Cardinal(RECORDHEIGHT, Cardinal_To_Fixed(TIMER_SECOND, SpareTicks));
|
||
|
||
/*
|
||
** Draw the bars across the performance record screen.
|
||
*/
|
||
for (int column = 0; column < RECORDCOUNT; column++) {
|
||
for (int row = 1; row < RECORDHEIGHT; row += 2) {
|
||
static unsigned char _barchar[4] = {' ', 220, 0, 219};
|
||
char str[2];
|
||
int index = 0;
|
||
|
||
index |= (_record[column].Graphic >= row) ? 0x01 : 0x00;
|
||
index |= (_record[column].Graphic >= row+1) ? 0x02: 0x00;
|
||
|
||
str[1] = '\0';
|
||
str[0] = _barchar[index];
|
||
mono->Text_Print(str, 37+column, 21-(row/2));
|
||
}
|
||
}
|
||
|
||
SpareTicks = 0;
|
||
FramesPerSecond = 0;
|
||
}
|
||
#endif
|
||
|
||
|
||
/***********************************************************************************************
|
||
* LogicClass::AI -- Handles AI logic processing for game objects. *
|
||
* *
|
||
* This routine is used to perform the AI processing for all game objects. This includes *
|
||
* all houses, factories, objects, and teams. *
|
||
* *
|
||
* INPUT: none *
|
||
* *
|
||
* OUTPUT: none *
|
||
* *
|
||
* WARNINGS: none *
|
||
* *
|
||
* HISTORY: *
|
||
* 05/29/1994 JLB : Created. *
|
||
* 12/17/1994 JLB : Must perform one complete pass rather than bailing early. *
|
||
* 12/23/1994 JLB : Esures that no object gets skipped if it was deleted. *
|
||
*=============================================================================================*/
|
||
void LogicClass::AI(void)
|
||
{
|
||
int index;
|
||
|
||
FramesPerSecond++;
|
||
|
||
/*
|
||
** Crate regeneration is handled here.
|
||
*/
|
||
if (GameToPlay != GAME_NORMAL && CrateMaker && CrateTimer.Expired()) {
|
||
Map.Place_Random_Crate();
|
||
CrateTimer = TICKS_PER_MINUTE * Random_Pick(7, 15);
|
||
}
|
||
|
||
/*
|
||
** Team AI is processed.
|
||
*/
|
||
for (index = 0; index < Teams.Count(); index++) {
|
||
Teams.Ptr(index)->AI();
|
||
}
|
||
|
||
// Heap_Dump_Check( "After Team AI" );
|
||
|
||
/*
|
||
** AI for all sentient objects is processed.
|
||
*/
|
||
for (index = 0; index < Count(); index++) {
|
||
ObjectClass * obj = (*this)[index];
|
||
int count = Count();
|
||
|
||
obj->AI();
|
||
|
||
/*
|
||
** If the object was destroyed in the process of performing its AI, then
|
||
** adjust the index so that no object gets skipped.
|
||
*/
|
||
int count_diff = Count() - count;
|
||
if (count_diff < 0) {
|
||
index += count_diff;
|
||
}
|
||
}
|
||
|
||
// Heap_Dump_Check( "After Object AI" );
|
||
|
||
/*
|
||
** A second pass through the sentient objects is required so that the appropriate scan
|
||
** bits will be set for the owner house.
|
||
*/
|
||
for (index = 0; index < Units.Count(); index++) {
|
||
UnitClass const * unit = Units.Ptr(index);
|
||
if (unit->IsLocked && (GameToPlay != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) {
|
||
unit->House->NewUScan |= (1L << unit->Class->Type);
|
||
if (!unit->IsInLimbo) unit->House->NewActiveUScan |= (1L << unit->Class->Type);
|
||
}
|
||
}
|
||
for (index = 0; index < Infantry.Count(); index++) {
|
||
InfantryClass const * infantry = Infantry.Ptr(index);
|
||
if (infantry->IsLocked && (GameToPlay != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) {
|
||
infantry->House->NewIScan |= (1L << infantry->Class->Type);
|
||
if (!infantry->IsInLimbo) infantry->House->NewActiveIScan |= (1L << infantry->Class->Type);
|
||
}
|
||
}
|
||
for (index = 0; index < Aircraft.Count(); index++) {
|
||
AircraftClass const * aircraft = Aircraft.Ptr(index);
|
||
if (aircraft->IsLocked && (GameToPlay != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) {
|
||
aircraft->House->NewAScan |= (1L << aircraft->Class->Type);
|
||
if (!aircraft->IsInLimbo) aircraft->House->NewActiveAScan |= (1L << aircraft->Class->Type);
|
||
}
|
||
}
|
||
for (index = 0; index < Buildings.Count(); index++) {
|
||
BuildingClass const * building = Buildings.Ptr(index);
|
||
if (building->IsLocked && (GameToPlay != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) {
|
||
building->House->NewBScan |= (1L << building->Class->Type);
|
||
if (!building->IsInLimbo) building->House->NewActiveBScan |= (1L << building->Class->Type);
|
||
}
|
||
}
|
||
|
||
// Heap_Dump_Check( "After Object AI 2" );
|
||
|
||
#ifdef USE_RA_AI
|
||
//
|
||
// Added for RA AI in TD. ST - 7/26/2019 10:56AM
|
||
//
|
||
HouseClass::Recalc_Attributes();
|
||
#endif // USE_RA_AI
|
||
|
||
/*
|
||
** Map related logic is performed.
|
||
*/
|
||
Map.Logic();
|
||
|
||
// Heap_Dump_Check( "After Map.Logic" );
|
||
|
||
/*
|
||
** Factory processing is performed.
|
||
*/
|
||
for (index = 0; index < Factories.Count(); index++) {
|
||
Factories.Ptr(index)->AI();
|
||
}
|
||
|
||
// Heap_Dump_Check( "After Factory AI" );
|
||
|
||
#if (1)
|
||
/*
|
||
** Changed integrated from RA to only call AI on the houses that need it. Without this change, AI houses immediately
|
||
** become paranoid at the start of a multiplayer match
|
||
** ST - 10/30/2019 11:15AM
|
||
*/
|
||
if (GameToPlay != GAME_NORMAL) {
|
||
for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) {
|
||
HouseClass * hptr = HouseClass::As_Pointer(house);
|
||
if (hptr && hptr->IsActive) {
|
||
hptr->AI();
|
||
}
|
||
}
|
||
|
||
HouseClass* neutral_house = HouseClass::As_Pointer(HOUSE_NEUTRAL);
|
||
if (neutral_house && neutral_house->IsActive) {
|
||
neutral_house->AI();
|
||
}
|
||
|
||
HouseClass* jp_house = HouseClass::As_Pointer(HOUSE_JP);
|
||
if (jp_house && jp_house->IsActive) {
|
||
jp_house->AI();
|
||
}
|
||
|
||
} else {
|
||
|
||
for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
|
||
HouseClass * hptr = HouseClass::As_Pointer(house);
|
||
if (hptr && hptr->IsActive) {
|
||
hptr->AI();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
#else
|
||
/*
|
||
** House processing is performed.
|
||
*/
|
||
for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
|
||
HouseClass * hptr = HouseClass::As_Pointer(house);
|
||
if (hptr && hptr->IsActive) {
|
||
hptr->AI();
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// Heap_Dump_Check( "After House AI" );
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/***********************************************************************************************
|
||
* LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were *
|
||
* recently created *
|
||
* *
|
||
* INPUT: none *
|
||
* *
|
||
* OUTPUT: none *
|
||
* *
|
||
* WARNINGS: none *
|
||
* *
|
||
* HISTORY: *
|
||
* 8/19/2019 5:47PM ST : Created. *
|
||
*=============================================================================================*/
|
||
void LogicClass::Clear_Recently_Created_Bits(void)
|
||
{
|
||
for (int index = 0; index < Count(); index++) {
|
||
ObjectClass * obj = (*this)[index];
|
||
obj->IsRecentlyCreated = false;
|
||
}
|
||
}
|
||
|
||
|