CnC_Remastered_Collection/TIBERIANDAWN/MAP.CPP

1596 lines
75 KiB
C++
Raw Permalink Normal View History

//
// 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\map.cpv 2.17 16 Oct 1995 16:52:22 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 : MAP.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : September 10, 1993 *
* *
* Last Update : August 20, 1995 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* MapClass::Cell_Distance -- Determines the distance between two cells. *
* MapClass::Cell_Region -- Determines the region from a specified cell number. *
* MapClass::Cell_Threat -- Gets a houses threat value for a cell *
* MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. *
* MapClass::In_Radar -- Is specified cell in the radar map? *
* MapClass::Init -- clears all cells *
* MapClass::Logic -- Handles map related logic functions. *
* MapClass::One_Time -- Performs special one time initializations for the map. *
* MapClass::Overlap_Down -- computes & marks object's overlap cells *
* MapClass::Overlap_Up -- Computes & clears object's overlap cells *
* MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. *
* MapClass::Pick_Up -- Removes specified object from the map. *
* MapClass::Place_Down -- Places the specified object onto the map. *
* MapClass::Place_Random_Crate -- Places a crate at random location on map. *
* MapClass::Read_Binary -- reads the map's binary image file *
* MapClass::Set_Map_Dimensions -- Initialize the map. *
* MapClass::Sight_From -- Mark as visible the cells within a specified radius. *
* MapClass::Validate -- validates every cell on the map *
* MapClass::Write_Binary -- writes the map's binary image file *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#define MCW MAP_CELL_W
int const MapClass::RadiusOffset[] = {
/* 0 */ 0,
/* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1,
/* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1,
/* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1,
/* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1,
/* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1,
/* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1,
/* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1,
/* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1,
/* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1,
/* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4,
(MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1,
};
int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309};
CellClass *BlubCell;
/***********************************************************************************************
* MapClass::One_Time -- Performs special one time initializations for the map. *
* *
* This routine is used by the game initialization function in order to perform any one *
* time initializations required for the map. This includes allocation of the map and *
* setting up its default dimensions. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: This routine MUST be called once and only once. *
* *
* HISTORY: *
* 05/31/1994 JLB : Created. *
* 12/01/1994 BR : Added CellTriggers initialization *
*=============================================================================================*/
void MapClass::One_Time(void)
{
GScreenClass::One_Time();
XSize = MAP_CELL_W;
YSize = MAP_CELL_H;
Size = XSize * YSize;
/*
** Allocate the cell array.
*/
Alloc_Cells();
/*
** Init the CellTriggers array to the required size.
*/
CellTriggers.Resize(MAP_CELL_TOTAL);
}
////////////////////////////////////////////////////
// Added this function to allow the editor to setup the map without setting up the entire system. - 06/18/2019 JAS
void MapClass::One_Time_Editor(void)
{
XSize = MAP_CELL_W;
YSize = MAP_CELL_H;
Size = XSize * YSize;
/*
** Allocate the cell array.
*/
Alloc_Cells();
/*
** Init the CellTriggers array to the required size.
*/
CellTriggers.Resize(MAP_CELL_TOTAL);
}
// End of change. - 06/15/2019 JAS
////////////////////////////////////////////////////
/***********************************************************************************************
* MapClass::Init_Clear -- clears the map & buffers to a known state *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 03/17/1995 BRR : Created. *
*=============================================================================================*/
void MapClass::Init_Clear(void)
{
GScreenClass::Init_Clear();
Init_Cells();
TiberiumScan = 0;
IsForwardScan = true;
TiberiumGrowthCount = 0;
TiberiumSpreadCount = 0;
}
/***********************************************************************************************
* MapClass::Alloc_Cells -- allocates the cell array *
* *
* This routine should be called at One_Time, and after loading the Map object from a save *
* game, but prior to loading the cell objects. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 03/17/1995 BRR : Created. *
*=============================================================================================*/
void MapClass::Alloc_Cells(void)
{
/*
** Assume that whatever the contents of the VectorClass are is garbage
** (it may have been loaded from a save-game file), so zero it out first.
*/
Vector = 0;
VectorMax = 0;
IsAllocated = 0;
Resize(Size);
}
/***********************************************************************************************
* MapClass::Free_Cells -- frees the cell array *
* *
* This routine is used by the Load_Game routine to free the map's cell array before loading *
* the map object from disk; the array is then re-allocated & cleared before the cell objects *
* are loaded. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 03/17/1995 BRR : Created. *
*=============================================================================================*/
void MapClass::Free_Cells(void)
{
Clear();
}
/***********************************************************************************************
* MapClass::Init_Cells -- Initializes the cell array to a fresh state. *
* *
* This routine is used by Init_Clear to set the cells to a known state; it's also used by *
* the Load_Game routine to init all cells before loading a set of cells from disk, so it *
* needs to be called separately from the other Init_xxx() routines. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 03/17/1995 BRR : Created. *
*=============================================================================================*/
void MapClass::Init_Cells(void)
{
TotalValue = 0;
#ifdef NEVER
Free_Cells();
Alloc_Cells();
#else
for (int index = 0; index < MAP_CELL_TOTAL; index++) {
Map[index] = CellClass();
}
#endif
}
/***********************************************************************************************
* MapClass::Set_Map_Dimensions -- Set map dimensions. *
* *
* This routine is used to set the legal limits and position of the *
* map as it relates to the overall map array. Typically, this is *
* called by the scenario loading code. *
* *
* INPUT: x,y -- The X and Y coordinate of the "upper left" corner *
* of the map. *
* *
* w,h -- The width and height of the legal map. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/14/1994 JLB : Created. *
*=============================================================================================*/
void MapClass::Set_Map_Dimensions(int x, int y, int w, int h)
{
MapCellX = x;
MapCellY = y;
MapCellWidth = w;
MapCellHeight = h;
}
/***********************************************************************************************
* MapClass::Sight_From -- Mark as visible the cells within a specified radius. *
* *
* This routine is used to reveal the cells around a specific location. *
* Typically, as a unit moves or is deployed, this routine will be *
* called. Since it deals with MANY cells, it needs to be extremely *
* fast. *
* *
* INPUT: house -- Player to perform the visibility update for *
* *
* cell -- The coordinate that the sighting originates from. *
* *
* sightrange-- The distance in cells that sighting extends. *
* *
* incremental-- Is this an incremental sighting. In other *
* words, has this function been called before where *
* the center coordinate is no more than one cell *
* distant from the last time? *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/19/1992 JLB : Created. *
* 03/08/1994 JLB : Updated to use sight table and incremental flag. *
* 05/18/1994 JLB : Converted to member function. *
* 03/06/2019 ST : Added HouseClass pointer parameter for new multiplayer code *
*=============================================================================================*/
void MapClass::Sight_From(HouseClass *house, CELL cell, int sightrange, bool incremental)
{
int xx; // Center cell X coordinate (bounds checking).
int const *ptr; // Offset pointer.
int count; // Counter for number of offsets to process.
// Added. ST - 3/6/2019 1:50PM
if ((house == NULL || house->IsHuman == false) && !ShareAllyVisibility) {
return;
}
/*
** Units that are off-map cannot sight.
*/
if (!In_Radar(cell)) return;
if (!sightrange || sightrange > 10) return;
/*
** Determine logical cell coordinate for center scan point.
*/
xx = Cell_X(cell);
/*
** Incremental scans only scan the outer rings. Full scans
** scan all internal cells as well.
*/
count = RadiusCount[sightrange];
ptr = &RadiusOffset[0];
if (incremental) {
if (sightrange > 1) {
ptr += RadiusCount[sightrange-2];
count -= RadiusCount[sightrange-2];
}
}
/*
** Process all offsets required for the desired scan.
*/
while (count--) {
CELL newcell; // New cell with offset.
int xdiff; // New cell's X coordinate distance from center.
newcell = cell + *ptr++;
/*
** Determine if the map edge has been wrapped. If so,
** then don't process the cell.
*/
if ((unsigned)newcell >= MAP_CELL_TOTAL) continue;
xdiff = Cell_X(newcell) - xx;
xdiff = ABS(xdiff);
if (xdiff > sightrange) continue;
if (Distance(newcell, cell) > sightrange) continue;
/*
** Map the cell. For incremental scans, then update
** adjacent cells as well. For full scans, just update
** the cell itself.
*/
// Pass the house through, instead of assuming it's the local player. ST - 3/6/2019 10:26AM
//Map.Map_Cell(newcell, PlayerPtr);
Map.Map_Cell(newcell, house, true);
}
}
/***********************************************************************************************
* MapClass::Cell_Distance -- Determines the distance between two cells. *
* *
* This routine will return with the calculated "straight line" *
* distance between the two cells specified. It uses the dragon strike *
* method of distance calculation. *
* *
* INPUT: cell1 -- First cell. *
* *
* cell2 -- Second cell. *
* *
* OUTPUT: Returns with the "cell" distance between the two cells *
* specified. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 04/29/1994 JLB : Created. *
* 04/30/1994 JLB : Converted to member function. *
*=============================================================================================*/
int MapClass::Cell_Distance(CELL cell1, CELL cell2)
{
register int x,y; // Difference on X and Y axis.
x = Cell_X(cell1) - Cell_X(cell2);
y = Cell_Y(cell1) - Cell_Y(cell2);
if (x < 0) x = -x;
if (y < 0) y = -y;
if (x > y) {
return(x + (y>>1));
}
return(y + (x>>1));
}
/***********************************************************************************************
* MapClass::In_Radar -- Is specified cell in the radar map? *
* *
* This determines if the specified cell can be within the navigable *
* bounds of the map. Technically, this means, any cell that can be *
* scanned by radar. If a cell returns false from this function, then *
* the player could never move to or pass over this cell. *
* *
* INPUT: cell -- The cell to examine. *
* *
* OUTPUT: bool; Is this cell possible to be displayed on radar? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 10/07/1992 JLB : Created. *
* 04/30/1994 JLB : Converted to member function. *
* 05/01/1994 JLB : Speeded up. *
*=============================================================================================*/
bool MapClass::In_Radar(CELL cell) const
{
if (cell & 0xF000) return(false);
return((unsigned)(Cell_X(cell) - MapCellX) < (unsigned)MapCellWidth && (unsigned)(Cell_Y(cell) - MapCellY) < (unsigned)MapCellHeight);
}
/***********************************************************************************************
* MapClass::Place_Down -- Places the specified object onto the map. *
* *
* This routine is used to place an object onto the map. It updates the "occupier" of the *
* cells that this object covers. The cells are determined from the Occupy_List function *
* provided by the object. Only one cell can have an occupier and this routine is the only *
* place that sets this condition. *
* *
* INPUT: cell -- The cell to base object occupation around. *
* *
* object -- The object to place onto the map. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/31/1994 JLB : Created. *
*=============================================================================================*/
void MapClass::Place_Down(CELL cell, ObjectClass * object)
{
if (!object) return;
short const *list = object->Occupy_List();
while (*list != REFRESH_EOL) {
CELL newcell = cell + *list++;
if ((unsigned)newcell < MAP_CELL_TOTAL) {
(*this)[newcell].Occupy_Down(object);
(*this)[newcell].Recalc_Attributes();
(*this)[newcell].Redraw_Objects();
}
}
list = object->Overlap_List();
while (*list != REFRESH_EOL) {
CELL newcell = cell + *list++;
if ((unsigned)newcell < MAP_CELL_TOTAL) {
(*this)[newcell].Overlap_Down(object);
(*this)[newcell].Redraw_Objects();
}
}
}
/***********************************************************************************************
* MapClass::Pick_Up -- Removes specified object from the map. *
* *
* The object specified is removed from the map by this routine. This will remove the *
* occupation flag for all the cells that the object covers. The cells that are covered *
* are determined from the Occupy_List function. *
* *
* INPUT: cell -- The cell that the object is centered about. *
* *
* object -- Pointer to the object that will be removed. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/31/1994 JLB : Created. *
*=============================================================================================*/
void MapClass::Pick_Up(CELL cell, ObjectClass * object)
{
if (!object) return;
short const *list = object->Occupy_List();
while (*list != REFRESH_EOL) {
CELL newcell = cell + *list++;
if ((unsigned)newcell < MAP_CELL_TOTAL) {
(*this)[newcell].Occupy_Up(object);
(*this)[newcell].Recalc_Attributes();
(*this)[newcell].Redraw_Objects();
}
}
list = object->Overlap_List();
while (*list != REFRESH_EOL) {
CELL newcell = cell + *list++;
if ((unsigned)newcell < MAP_CELL_TOTAL) {
(*this)[newcell].Overlap_Up(object);
(*this)[newcell].Redraw_Objects();
}
}
}
/***********************************************************************************************
* MapClass::Overlap_Down -- computes & marks object's overlap cells *
* *
* This routine is just like Place_Down, but it doesn't mark the cell's Occupier. *
* This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing *
* an object's render size, but not its logical size (ie when it's selected or an *
* animation is attached to it). *
* *
* INPUT: *
* cell -- The cell to base object overlap around. *
* object -- The object to place onto the map. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 07/12/1995 BRR : Created. *
*=============================================================================================*/
void MapClass::Overlap_Down(CELL cell, ObjectClass * object)
{
if (!object) return;
short const *list = object->Overlap_List();
while (*list != REFRESH_EOL) {
CELL newcell = cell + *list++;
if ((unsigned)newcell < MAP_CELL_TOTAL) {
(*this)[newcell].Overlap_Down(object);
(*this)[newcell].Redraw_Objects();
}
}
}
/***********************************************************************************************
* MapClass::Overlap_Up -- Computes & clears object's overlap cells *
* *
* This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. *
* This routine is used to implement MARK_OVERLAP_UP, which is useful for changing *
* an object's render size, but not its logical size (ie when it's selected or an *
* animation is attached to it). *
* *
* INPUT: *
* cell -- The cell to base object overlap around. *
* object -- The object to place onto the map. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 07/12/1995 BRR : Created. *
*=============================================================================================*/
void MapClass::Overlap_Up(CELL cell, ObjectClass * object)
{
if (!object) return;
short const *list = object->Overlap_List();
while (*list != REFRESH_EOL) {
CELL newcell = cell + *list++;
if ((unsigned)newcell < MAP_CELL_TOTAL) {
(*this)[newcell].Overlap_Up(object);
(*this)[newcell].Redraw_Objects();
}
}
}
/***********************************************************************************************
* MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. *
* *
* This routine will clean up anything necessary with the presumption that the map has *
* been freshly created. Such things to clean up include various tiberium concentrations. *
* *
* INPUT: none *
* *
* OUTPUT: Returns the total credit value of the tiberium on the map. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
* 02/13/1995 JLB : Returns total tiberium worth. *
* 02/15/1995 JLB : Optimal scan. *
*=============================================================================================*/
long MapClass::Overpass(void)
{
long value = 0;
/*
** Smooth out Tiberium. Cells that are not surrounded by other tiberium
** will be reduced in density.
*/
for (int y = 0; y < MapCellHeight-1; y++) {
for (int x = 0; x < MapCellWidth; x++) {
value += (*this)[(MapCellY+y) * MAP_CELL_W + (MapCellX+x)].Tiberium_Adjust(true);
}
}
return(value);
}
/***********************************************************************************************
* MapClass::Read_Binary -- reads the map's binary image file *
* *
* INPUT: *
* root root filename for scenario *
* crc ptr to CRC value to update *
* *
* OUTPUT: *
* 1 = success, 0 = failure *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/14/1994 BR : Created. *
* 01/08/1995 JLB : Fixup any obsolete icons detected. *
*=============================================================================================*/
#ifdef DEMO
bool MapClass::Read_Binary(char const * root, unsigned long *)
#else
bool MapClass::Read_Binary(char const * root, unsigned long *crc)
#endif
{
CCFileClass file;
char fname[_MAX_FNAME+_MAX_EXT];
int i;
char *map;
void *rawmap;
void const * shape;
/*
** Filename = INI name with BIN extension.
*/
sprintf(fname,"%s.BIN",root);
/*
** Create object & open file.
*/
file.Set_Name(fname);
if (!file.Is_Available()) {
return(false);
}
file.Open(READ);
/*
** Loop through all cells.
*/
CellClass * cellptr = &Map[0];
for (i = 0; i < MAP_CELL_TOTAL; i++) {
struct {
TemplateType TType; // Template type.
unsigned char TIcon; // Template icon number.
} temp;
if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break;
if (temp.TType == (TemplateType)255) {
temp.TType = TEMPLATE_NONE;
}
/*
** Verify that the template type actually contains the template number specified. If
** an illegal icon was specified, then replace it with clear terrain.
*/
if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) {
TemplateTypeClass const &ttype = TemplateTypeClass::As_Reference(temp.TType);
shape = ttype.Get_Image_Data();
if (shape) {
rawmap = Get_Icon_Set_Map(shape);
if (rawmap) {
map = (char*)rawmap;
if ((temp.TIcon >= (ttype.Width * ttype.Height)) || (map[temp.TIcon] == -1)) {
temp.TIcon = 0;
temp.TType = TEMPLATE_NONE;
}
}
}
}
cellptr->TType = temp.TType;
cellptr->TIcon = temp.TIcon;
cellptr->Recalc_Attributes();
#ifndef DEMO
Add_CRC(crc, (unsigned long)cellptr->TType);
Add_CRC(crc, (unsigned long)cellptr->TIcon);
#endif
cellptr++;
}
/*
** Close the file.
*/
file.Close();
return(i == MAP_CELL_TOTAL);
}
/***********************************************************************************************
* MapClass::Read_BinaryRead_Binary_File -- reads the map's binary image file *
* *
* INPUT: *
* fname file path for scenario *
* crc ptr to CRC value to update *
* *
* OUTPUT: *
* 1 = success, 0 = failure *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 10/28/2019 JAS : Created. *
*=============================================================================================*/
bool MapClass::Read_Binary_File(char const * fname, unsigned long *crc)
{
CCFileClass file;
int i;
char *map;
void *rawmap;
void const * shape;
/*
** Create object & open file.
*/
file.Set_Name(fname);
if (!file.Is_Available()) {
return(false);
}
file.Open(READ);
/*
** Loop through all cells.
*/
CellClass * cellptr = &Map[0];
for (i = 0; i < MAP_CELL_TOTAL; i++) {
struct {
TemplateType TType; // Template type.
unsigned char TIcon; // Template icon number.
} temp;
if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break;
if (temp.TType == (TemplateType)255) {
temp.TType = TEMPLATE_NONE;
}
/*
** Verify that the template type actually contains the template number specified. If
** an illegal icon was specified, then replace it with clear terrain.
*/
if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) {
shape = TemplateTypeClass::As_Reference(temp.TType).Get_Image_Data();
if (shape) {
rawmap = Get_Icon_Set_Map(shape);
if (rawmap) {
map = (char*)rawmap;
if (map[temp.TIcon] == -1) {
temp.TIcon = 0;
temp.TType = TEMPLATE_NONE;
}
}
}
}
cellptr->TType = temp.TType;
cellptr->TIcon = temp.TIcon;
cellptr->Recalc_Attributes();
#ifndef DEMO
Add_CRC(crc, (unsigned long)cellptr->TType);
Add_CRC(crc, (unsigned long)cellptr->TIcon);
#endif
cellptr++;
}
/*
** Close the file.
*/
file.Close();
return(i == MAP_CELL_TOTAL);
}
/***********************************************************************************************
* MapClass::Write_Binary -- writes the map's binary image file *
* *
* INPUT: *
* root root filename for scenario *
* *
* OUTPUT: *
* 1 = success, 0 = failure *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/14/1994 BR : Created. *
*=============================================================================================*/
bool MapClass::Write_Binary(char const * root)
{
CCFileClass *file;
char fname[_MAX_FNAME+_MAX_EXT];
int i;
/*
** Filename = INI name with BIN extension.
*/
sprintf(fname,"%s.BIN",root);
/*
** Create object & open file.
*/
file = new CCFileClass(fname);
file->Open(WRITE);
/*
** Loop through all cells.
*/
for (i = 0; i < MAP_CELL_TOTAL; i++) {
/*
** Save TType.
*/
if (file->Write (&(Map[i].TType), sizeof(TemplateType)) != sizeof(TemplateType)) {
file->Close();
delete file;
return(false);
}
/*
** Save TIcon.
*/
if (file->Write (&(Map[i].TIcon), sizeof(unsigned char)) != sizeof(unsigned char)) {
file->Close();
delete file;
return(false);
}
}
/*
** Close the file.
*/
file->Close();
delete file;
return(true);
}
/***********************************************************************************************
* MapClass::Logic -- Handles map related logic functions. *
* *
* Manages tiberium growth and spread. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/11/1995 JLB : Created. *
* 07/09/1995 JLB : Handles two directional scan. *
* 08/01/1995 JLB : Gives stronger weight to blossom trees. *
*=============================================================================================*/
void MapClass::Logic(void)
{
if (Debug_Force_Crash) { *((int *)0) = 1; }
/*
** Bail early if there is no allowed growth or spread of Tiberium.
*/
if (!Special.IsTGrowth && !Special.IsTSpread) return;
/*
** Scan another block of the map in order to accumulate the potential
** Tiberium cells that can grow or spread.
*/
int subcount = 30;
int index;
for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) {
CELL cell = index;
if (!IsForwardScan) cell = (MAP_CELL_TOTAL-1) - index;
CellClass *ptr = &(*this)[cell];
if (Special.IsTGrowth && ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData < 11) {
if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) {
TiberiumGrowth[TiberiumGrowthCount++] = cell;
} else {
TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell;
}
}
/*
** Heavy Tiberium growth can spread.
*/
TerrainClass * terrain = ptr->Cell_Terrain();
if (Special.IsTSpread &&
(ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData > 6) ||
(terrain && terrain->Class->IsTiberiumSpawn)) {
int tries = 1;
if (terrain) tries = 3;
for (int i = 0; i < tries; i++) {
if (TiberiumSpreadCount < sizeof(TiberiumSpread)/sizeof(TiberiumSpread[0])) {
TiberiumSpread[TiberiumSpreadCount++] = cell;
} else {
TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell;
}
}
}
subcount--;
if (!subcount) break;
}
TiberiumScan = index;
if (TiberiumScan >= MAP_CELL_TOTAL) {
int tries = 1;
if (Special.IsTFast || GameToPlay != GAME_NORMAL) tries = 2;
/*
** Use the Tiberium setting as a multiplier on growth rate. ST - 7/1/2020 3:05PM
*/
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
if (MPlayerTiberium > 1) {
tries += (MPlayerTiberium - 1) << 1;
}
}
TiberiumScan = 0;
IsForwardScan = (IsForwardScan == false);
/*
** Growth logic.
*/
if (TiberiumGrowthCount) {
for (int i = 0; i < tries; i++) {
int pick = Random_Pick(0, TiberiumGrowthCount-1);
CELL cell = TiberiumGrowth[pick];
CellClass * newcell = &(*this)[cell];
if (newcell->Land_Type() == LAND_TIBERIUM && newcell->OverlayData < 12-1) {
newcell->OverlayData++;
}
TiberiumGrowth[pick] = TiberiumGrowth[TiberiumGrowthCount - 1];
TiberiumGrowthCount--;
if (TiberiumGrowthCount <= 0) {
break;
}
}
}
TiberiumGrowthCount = 0;
/*
** Spread logic.
*/
if (TiberiumSpreadCount) {
for (int i = 0; i < tries; i++) {
int pick = Random_Pick(0, TiberiumSpreadCount-1);
CELL cell = TiberiumSpread[pick];
/*
** Find a pseudo-random adjacent cell that doesn't contain any tiberium.
*/
if (Map.In_Radar(cell)) {
FacingType offset = Random_Pick(FACING_N, FACING_NW);
for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
Command & Conquer Remastered post-launch patch Improvements to harvester resource finding logic. Don't allow the Advanced Comm Center to be capturable in skirmish or multiplayer. Increased failed pathfinding fudge factor. Buildings accept the Guard command if they can attack. Don't allow force capturing of ally structures. Fixes for laser Orcas in S3cr3t M1ss10n. Properly restore them after save. Reset Orcas after loads. Fixed flag animation rendering in CTF. Potentially fix a crash if aircraft are destroyed outside the map bounds. Fixed legacy Obelisk line rendering. Fix out-of-bounds crash in TD; issue was already fixed in RA. Disable capture flag on Commandos. Drop the flag when entering the limbo state. Fixed end game race condition, winning team ID is always sent before individual player win/lose messages. Fixed Chan spawn on SCB10EA. Don't show enter cursor for enemy units on refineries and repair pads. Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold. Don't debug reveal legacy rendering when a player is defeated. Fixed crash when loading saves of custom campaign maps. Reset harvester archived target when given a direct harvest order. Prevent NOD cargo planes from being force attacked. Fixed unit selection on load. Migrated queued repair pad functionality from RA to TD. Randomly animate infantry in area guard mode. Fixed crash accessing inactive objects. Added some walls in SCG08EB to prevent civilians from killing themselves. TD + RA: Audio: Overiding "Our base is under attack" cooldown timing from legacy from 2 minutes to 30 seconds, so it will be heard 4x as often. Fixed adjacent cell out-of-bounds crash issues. Kill player on disconnect Fixed and improved build time calculations to be consistent between TD and RA. Don't show health bars for cloaked Stealth Tanks in the legacy renderer. Fix selection of individual control groups after mixed selection. More adjustments to SCG08EB; switch C7 to C5, and add civilian AI to avoid Tiberium. Extra safety checks for units that have no weapons and aircraft that can't hunt. Fix loading of multiple infantry onto an APC. Additional safety checks for invalid coordinates. Prevent units from being instantly repaired. Fix map passability. Fail Allied mission 5B if the spy re-boards the starting transport (matches 5A and 5C behavior). Fixed multiplayer formation move causing units to move at light speed. Ignore movement destination checks if a unit is part of a mission-driven team. Fix buffer overrun crash. Ignore mines when determining win conditions. Fixed river passability in Blue Lakes.
2020-06-23 04:43:21 +12:00
CellClass *newcell = (*this)[cell].Adjacent_Cell(index+offset);
if (newcell && newcell->Cell_Object() == NULL && newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) {
bool found = false;
switch (newcell->TType) {
case TEMPLATE_BRIDGE1:
case TEMPLATE_BRIDGE2:
case TEMPLATE_BRIDGE3:
case TEMPLATE_BRIDGE4:
break;
default:
found = true;
new OverlayClass(Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12), newcell->Cell_Number());
newcell->OverlayData = 1;
break;
}
if (found) break;
}
}
}
TiberiumSpread[pick] = TiberiumSpread[TiberiumSpreadCount - 1];
TiberiumSpreadCount--;
if (TiberiumSpreadCount <= 0) {
break;
}
}
}
TiberiumSpreadCount = 0;
}
}
/***********************************************************************************************
* MapClass::Cell_Region -- Determines the region from a specified cell number. *
* *
* Use this routine to determine what region a particular cell lies in. *
* *
* INPUT: cell -- The cell number to examine. *
* *
* OUTPUT: Returns with the region that the specified cell occupies. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/15/1995 JLB : Created. *
*=============================================================================================*/
int MapClass::Cell_Region(CELL cell)
{
return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH);
}
/***************************************************************************
* MapClass::Cell_Threat -- Gets a houses threat value for a cell *
* *
* INPUT: CELL cell - the cell number to check *
* HouseType house - the house to check *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 04/25/1995 PWG : Created. *
*=========================================================================*/
int MapClass::Cell_Threat(CELL cell, HousesType house)
{
int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value();
//using function for IsVisible so we have different results for different players - JAS 2019/09/30
if (!threat && Map[cell].Is_Visible(house)) {
threat = 1;
}
return(threat);
}
/***********************************************************************************************
* MapClass::Place_Random_Crate -- Places a crate at random location on map. *
* *
* This routine will place a crate at a random location on the map. This routine will only *
* make a limited number of attempts to place and if unsuccessful, it will not place any. *
* *
* INPUT: none *
* *
* OUTPUT: Was a crate successfully placed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/08/1995 JLB : Created. *
*=============================================================================================*/
bool MapClass::Place_Random_Crate(void)
{
int old = ScenarioInit;
ScenarioInit = 0;
for (int index = 0; index < 100; index++) {
int x = Random_Pick(0, MapCellWidth-1);
int y = Random_Pick(0, MapCellHeight-1);
CELL cell = XY_Cell(MapCellX+x, MapCellY+y);
CellClass * ptr = &(*this)[cell];
if (ptr->Is_Generally_Clear() && ptr->Overlay == OVERLAY_NONE) {
ptr->Overlay = OVERLAY_WOOD_CRATE;
ptr->OverlayData = 0;
ptr->Redraw_Objects();
ScenarioInit = old;
return(true);
}
}
ScenarioInit = old;
return(false);
}
/***************************************************************************
* MapClass::Validate -- validates every cell on the map *
* *
* This is a debugging routine, designed to detect memory trashers that *
* alter the map. This routine is slow, but thorough. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* true = map is OK, false = an error was found *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 07/08/1995 BRR : Created. *
*=========================================================================*/
int MapClass::Validate(void)
{
CELL cell;
TemplateType ttype;
unsigned char ticon;
//TemplateTypeClass const *tclass;
//unsigned char map[13*8];
OverlayType overlay;
SmudgeType smudge;
ObjectClass *obj;
LandType land;
int i;
BlubCell = &((*this)[797]);
if (BlubCell->Overlapper[1]) {
obj = BlubCell->Overlapper[1];
if (obj) {
if (obj->IsInLimbo)
obj = obj;
}
}
/*------------------------------------------------------------------------
Check every cell on the map, even those that aren't displayed,
in the hopes of detecting a memory trasher.
------------------------------------------------------------------------*/
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
/*.....................................................................
Validate Template & Icon data
.....................................................................*/
ttype = (*this)[cell].TType;
ticon = (*this)[cell].TIcon;
if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE)
return(false);
/*.....................................................................
To validate the icon value, we have to get a copy of the template's
"icon map"; this map will have 0xff's in spots where there is no
icon. If the icon value is out of range or points to an invalide spot,
return an error.
.....................................................................*/
#if (0)
if (ttype != TEMPLATE_NONE) {
tclass = &TemplateTypeClass::As_Reference(ttype);
ticon = (*this)[cell].TIcon;
Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map,
tclass->Width * tclass->Height);
if (ticon < 0 ||
ticon >= (tclass->Width * tclass->Height) ||
map[ticon]==0xff)
return (false);
}
#endif
/*.....................................................................
Validate Overlay
.....................................................................*/
overlay = (*this)[cell].Overlay;
if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT)
return(false);
/*.....................................................................
Validate Smudge
.....................................................................*/
smudge = (*this)[cell].Smudge;
if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT)
return(false);
/*.....................................................................
Validate LandType
.....................................................................*/
land = (*this)[cell].Land_Type();
if (land < LAND_CLEAR || land >= LAND_COUNT)
return(false);
/*.....................................................................
Validate Occupier
.....................................................................*/
obj = (*this)[cell].Cell_Occupier();
if (obj) {
volatile TARGET target = obj->As_Target(); // This will do some internal verification
if (!obj->IsActive) {
return false;
}
if (obj->IsInLimbo) {
return false;
}
if (((unsigned int)Coord_Cell(obj->Coord) > 4095)) {
return (false);
}
}
/*.....................................................................
Validate Overlappers
.....................................................................*/
for (i = 0; i < 3; i++) {
obj = (*this)[cell].Overlapper[i];
if (obj) {
volatile TARGET target = obj->As_Target(); // This will do some internal verification
if (!obj->IsActive) {
return false;
}
if (obj->IsInLimbo) {
return false;
}
if (((unsigned int)Coord_Cell(obj->Coord) > 4095)) {
return (false);
}
}
}
}
return (true);
}
/***********************************************************************************************
* MapClass::Clean -- Clean up dangling pointers caused by bugs in the originl code. *
* *
* Ideally, we'd fix the underlying cause of the overlappers not being cleared *
* but we can afford the CPU time now *
* *
* INPUT: none *
* *
* OUTPUT: *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 4/14/2020 11:48AM ST : Created. *
*=============================================================================================*/
void MapClass::Clean(void)
{
CELL cell;
ObjectClass *obj;
int i;
#ifndef NDEBUG
char debug_message[256];
#endif
bool active_fail = false;
bool limbo_fail = false;
const char *type_text = NULL;
const char *ini_name = NULL;
AbstractClass abstract_object;
unsigned long abstract_vtable = *(unsigned long*)&abstract_object;
/*------------------------------------------------------------------------
Check every cell on the map, even those that aren't displayed.
------------------------------------------------------------------------*/
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
/*.....................................................................
Validate Occupier
.....................................................................*/
(*this)[cell].Cell_Occupier();
/*.....................................................................
Validate Overlappers
.....................................................................*/
for (i = 0; i < 3; i++) {
obj = (*this)[cell].Overlapper[i];
if (obj) {
if (!obj->IsActive) {
(*this)[cell].Overlapper[i] = NULL;
active_fail = true;
}
if (!active_fail && obj->IsInLimbo) {
(*this)[cell].Overlapper[i] = NULL;
limbo_fail = true;
}
if (active_fail || limbo_fail) {
#ifndef NDEBUG
/*
** This object is likely deleted.
*/
if (abstract_vtable == *(unsigned long*)obj) {
type_text = "Abstract";
ini_name = "UNKNOWN";
} else {
RTTIType type = obj->What_Am_I();
switch (type) {
default:
type_text = "Unknown";
break;
case RTTI_INFANTRY:
type_text = "Infantry";
break;
case RTTI_UNIT:
type_text = "Unit";
break;
case RTTI_AIRCRAFT:
type_text = "Aircraft";
break;
case RTTI_BUILDING:
type_text = "Building";
break;
case RTTI_BULLET:
type_text = "Bullet";
break;
case RTTI_ANIM:
type_text = "Anim";
break;
case RTTI_SMUDGE:
type_text = "Smudge";
break;
case RTTI_TERRAIN:
type_text = "Terrain";
break;
}
ini_name = obj->Class_Of().IniName;
}
sprintf_s(debug_message, sizeof(debug_message) - 1, "Cleaned %s overlapper in cell %08X. Type=%s, IniName=%s", active_fail ? "inactive" : "limbo", cell, type_text, ini_name);
GlyphX_Debug_Print(debug_message);
#endif //NDEBUG
}
}
}
}
}
/***********************************************************************************************
* MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. *
* *
* This routine is used by the mouse input processing code to find a clickable object *
* close to coordinate specified. This is for targeting as well as selection determination. *
* *
* INPUT: coord -- The coordinate to scan for close object from. *
* *
* OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. *
* *
* WARNINGS: There could be a cloaked object at the location, but it won't be considered *
* if it is not owned by the player. *
* *
* HISTORY: *
* 08/20/1995 JLB : Created. *
*=============================================================================================*/
ObjectClass * MapClass::Close_Object(COORDINATE coord) const
{
ObjectClass * object = 0;
int distance = 0;
CELL cell = Coord_Cell(coord);
/*
** Scan through current and adjacent cells, looking for the
** closest object (within reason) to the specified coordinate.
*/
static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)};
for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) {
/*
** Examine the cell for close object. Make sure that the cell actually is a
** legal one.
*/
CELL newcell = cell + _offsets[index];
if (In_Radar(newcell)) {
/*
** Search through all objects that occupy this cell and then
** find the closest object. Check against any previously found object
** to ensure that it is actually closer.
*/
ObjectClass * o = (*this)[newcell].Cell_Occupier();
while (o) {
/*
** Special case check to ignore cloaked object if not allied with the player.
*/
// Changed for multiplayer. ST - 3/13/2019 5:38PM
if (!o->Is_Techno() || !((TechnoClass *)o)->Is_Cloaked(PlayerPtr)) {
//if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) {
int d=-1;
if (o->What_Am_I() == RTTI_BUILDING) {
d = Distance(coord, Cell_Coord(newcell));
if (d > 0x00C0) d = -1;
} else {
d = Distance(coord, o->Center_Coord());
}
if (d >= 0 && (!object || d < distance)) {
distance = d;
object = o;
}
}
o = o->Next;
}
}
}
/*
** Handle aircraft selection separately, since they aren't tracked in cells while flying
*/
for (int index = 0; index < Aircraft.Count(); index++) {
AircraftClass * aircraft = Aircraft.Ptr(index);
if (aircraft->In_Which_Layer() != LAYER_GROUND) {
if (!aircraft->Is_Cloaked(PlayerPtr)) {
int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -Pixel_To_Lepton(aircraft->Altitude))));
if (d >= 0 && (!object || d < distance)) {
distance = d;
object = aircraft;
}
}
}
}
/*
** Only return the object if it is within 1/4 cell distance from the specified
** coordinate.
*/
if (object && distance > 0xC0) {
object = 0;
}
return(object);
}
#ifdef USE_RA_AI
/*
** Nearby_Location pulled in from RA for AI. ST - 7/24/2019 5:54PM
*/
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
#endif
/***********************************************************************************************
* MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. *
* *
* This routine is used to find a location that probably will be ok to move to that is *
* located as close as possible to the specified cell. The computer uses this when it has *
* determined the ideal location for an object, but then needs to give a valid movement *
* destination to a unit. *
* *
* INPUT: cell -- The cell that scanning should radiate out from. *
* *
* zone -- The zone that must be matched to find a legal location (value of -1 means *
* any zone will do). *
* *
* *
* check -- The type of zone to check against. Only valid if a zone value is given. *
* *
* OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close *
* to the specified cell. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 10/05/1995 JLB : Created. *
*=============================================================================================*/
CELL MapClass::Nearby_Location(CELL cell) const //, SpeedType speed, int zone, MZoneType check) const
{
CELL topten[10];
int count = 0;
int xx = Cell_X(cell);
int yy = Cell_Y(cell);
/*
** Determine the limits of the scanning in the four directions so that
** it won't scan past the edge of the world.
*/
int left = MapCellX;
int right = MapCellX + MapCellWidth - 1;
int top = MapCellY;
int bottom = MapCellY + MapCellHeight - 1;
/*
** Radiate outward from the specified location, looking for the closest
** location that is generally clear.
*/
for (int radius = 0; radius < MAP_CELL_W; radius++) {
CELL newcell;
CellClass const * cellptr;
/*
** Scan the top and bottom rows of the "box".
*/
for (int x = xx-radius; x <= xx+radius; x++) {
if (x >= left && x <= right) {
int y = yy-radius;
if (y >= top) {
newcell = XY_Cell(x, y);
cellptr = &Map[newcell];
if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) {
topten[count++] = newcell;
}
}
if (count == ARRAY_SIZE(topten)) break;
y = yy+radius;
if (y <= bottom) {
newcell = XY_Cell(x, y);
cellptr = &Map[newcell];
if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) {
topten[count++] = newcell;
}
}
if (count == ARRAY_SIZE(topten)) break;
}
}
if (count == ARRAY_SIZE(topten)) break;
/*
** Scan the left and right columns of the "box".
*/
for (int y = yy-radius; y <= yy+radius; y++) {
if (y >= top && y <= bottom) {
int x = xx-radius;
if (x >= left) {
newcell = XY_Cell(x, y);
cellptr = &Map[newcell];
if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) {
topten[count++] = newcell;
}
}
if (count == ARRAY_SIZE(topten)) break;
x = xx+radius;
if (x <= right) {
newcell = XY_Cell(x, y);
cellptr = &Map[newcell];
if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) {
topten[count++] = newcell;
}
}
if (count == ARRAY_SIZE(topten)) break;
}
}
if (count > 0) break;
}
if (count > 0) {
return(topten[Frame % count]);
}
return(0);
}
#endif // USE_RA_AI