CnC_Remastered_Collection/TIBERIANDAWN/MAP.CPP
PG-SteveT ae72fce5dd August 6th Patch Update
Accumulated DLL source code changes since June 22nd patch
2020-08-06 09:44:54 -07:00

1596 lines
75 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\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++) {
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