2020-05-27 20:16:20 +01:00
|
|
|
//
|
|
|
|
// Copyright 2020 Electronic Arts Inc.
|
|
|
|
//
|
|
|
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
|
|
|
|
// software: you can redistribute it and/or modify it under the terms of
|
|
|
|
// the GNU General Public License as published by the Free Software Foundation,
|
|
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
|
|
|
|
// in the hope that it will be useful, but with permitted additional restrictions
|
|
|
|
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
|
|
|
|
// distributed with this program. You should have received a copy of the
|
|
|
|
// GNU General Public License along with permitted additional restrictions
|
|
|
|
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
|
|
|
|
|
|
|
/* $Header: /CounterStrike/MAP.CPP 3 3/14/97 5:15p Joe_b $ */
|
|
|
|
/***********************************************************************************************
|
|
|
|
*** 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 : October 5, 1996 [JLB] *
|
|
|
|
* *
|
|
|
|
*---------------------------------------------------------------------------------------------*
|
|
|
|
* Functions: *
|
|
|
|
* MapClass::Base_Region -- Finds the owner and base zone for specified cell. *
|
|
|
|
* 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::Destroy_Bridge_At -- Destroyes the bridge at location specified. *
|
|
|
|
* MapClass::Detach -- Remove specified object from map references. *
|
|
|
|
* MapClass::In_Radar -- Is specified cell in the radar map? *
|
|
|
|
* MapClass::Init -- clears all cells *
|
|
|
|
* MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. *
|
|
|
|
* MapClass::Logic -- Handles map related logic functions. *
|
|
|
|
* MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. *
|
|
|
|
* 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 binary data from the straw specified. *
|
|
|
|
* MapClass::Remove_Crate -- Remove a crate from the specified cell. *
|
|
|
|
* 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 -- Pipes the map template data to the destination specified. *
|
|
|
|
* MapClass::Zone_Reset -- Resets all zone numbers to match the map. *
|
|
|
|
* MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. *
|
|
|
|
* MapClass::Pick_Random_Location -- Picks a random location on the map. *
|
|
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
|
|
#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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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;
|
|
|
|
TiberiumGrowthCount = 0;
|
|
|
|
TiberiumGrowthExcess = 0;
|
|
|
|
TiberiumSpreadCount = 0;
|
|
|
|
TiberiumSpreadExcess = 0;
|
|
|
|
for (int index = 0; index < ARRAY_SIZE(Crates); index++) {
|
|
|
|
Crates[index].Init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
new (&Array) VectorClass<CellClass>;
|
|
|
|
Array.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)
|
|
|
|
{
|
|
|
|
Array.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;
|
|
|
|
for (int index = 0; index < MAP_CELL_TOTAL; index++) {
|
|
|
|
new (&Array[index]) CellClass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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: 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. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::Sight_From(CELL cell, int sightrange, HouseClass * house, bool incremental)
|
|
|
|
{
|
|
|
|
int xx; // Center cell X coordinate (bounds checking).
|
|
|
|
int const * ptr; // Offset pointer.
|
|
|
|
int count; // Counter for number of offsets to process.
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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 > 2) {
|
|
|
|
ptr += RadiusCount[sightrange-3];
|
|
|
|
count -= RadiusCount[sightrange-3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Map the cell. For incremental scans, then update
|
|
|
|
** adjacent cells as well. For full scans, just update
|
|
|
|
** the cell itself.
|
|
|
|
*/
|
|
|
|
//if (!(*this)[newcell].IsMapped) { // ST - 8/7/2019 10:31AM
|
|
|
|
Map.Map_Cell(newcell, house, true, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Shroud_From -- cloak a radius of cells *
|
|
|
|
* *
|
|
|
|
* This routine is used to shroud the cells around a specific location. *
|
|
|
|
* Typically, as a gap generator refreshes (when Encroach_Shadow() is called) this routine's*
|
|
|
|
* called. Since it deals with MANY cells, it needs to be extremely *
|
|
|
|
* fast. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The coordinate that the shrouding originates from. *
|
|
|
|
* *
|
|
|
|
* sightrange-- The distance in cells that sighting extends. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 11/10/1995 BWG : Created. *
|
|
|
|
* 08/09/2019 ST : Added house parameter *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::Shroud_From(CELL cell, int sightrange, HouseClass *house)
|
|
|
|
{
|
|
|
|
int xx; // Center cell X coordinate (bounds checking).
|
|
|
|
int const * ptr; // Offset pointer.
|
|
|
|
int count; // Counter for number of offsets to process.
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Units that are off-map cannot sight.
|
|
|
|
*/
|
|
|
|
if (!In_Radar(cell)) return;
|
|
|
|
if (!sightrange || sightrange > Rule.GapShroudRadius) 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];
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Shroud the cell.
|
|
|
|
*/
|
|
|
|
Map.Shroud_Cell(newcell, house);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Jam_From -- Mark as jammed the cells within a specified radius. *
|
|
|
|
* *
|
|
|
|
* This routine is used to jam the cells around a specific location. *
|
|
|
|
* Typically, as a gap generator structure is created, this routine will be *
|
|
|
|
* called. Since it deals with MANY cells, it needs to be extremely *
|
|
|
|
* fast. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The coordinate that the jamming originates from. *
|
|
|
|
* *
|
|
|
|
* jamrange -- The distance in cells that jamming extends. *
|
|
|
|
* *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 11/09/1995 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::Jam_From(CELL cell, int jamrange, HouseClass * house)
|
|
|
|
{
|
|
|
|
int xx; // Center cell X coordinate (bounds checking).
|
|
|
|
int const * ptr; // Offset pointer.
|
|
|
|
int count; // Counter for number of offsets to process.
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Units that are off-map cannot jam.
|
|
|
|
*/
|
|
|
|
if (!jamrange || jamrange > Rule.GapShroudRadius) 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[jamrange];
|
|
|
|
ptr = &RadiusOffset[0];
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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 > jamrange) continue;
|
|
|
|
if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Jam the cell. For incremental scans, then update
|
|
|
|
** adjacent cells as well. For full scans, just update
|
|
|
|
** the cell itself.
|
|
|
|
*/
|
|
|
|
Map.Jam_Cell(newcell, house/*KO, false*/);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM
|
|
|
|
*/
|
|
|
|
if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
|
|
|
|
if (!house->IsPlayerControl) {
|
|
|
|
Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, PlayerPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
for (int i = 0; i < Session.Players.Count(); i++) {
|
|
|
|
HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
|
|
|
|
if (player_house->IsHuman && player_house != house) {
|
|
|
|
Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, player_house);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef OBSOLETE
|
|
|
|
/*
|
|
|
|
** The objects on the map need to perform a manual look operation if they happen
|
|
|
|
** to have their sight range overlap the gap radius.
|
|
|
|
*/
|
|
|
|
if (!house->IsPlayerControl) {
|
|
|
|
// if (house != PlayerPtr) {
|
|
|
|
|
|
|
|
for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) {
|
|
|
|
ObjectClass * object = Map.Layer[LAYER_GROUND][index];
|
|
|
|
if (object && object->Is_Techno()) {
|
|
|
|
TechnoClass * tech = ((TechnoClass *)object);
|
|
|
|
|
|
|
|
if (tech->IsDiscoveredByPlayer &&
|
|
|
|
(tech->Distance(As_Target(cell)) / CELL_LEPTON_W) <= tech->Techno_Type_Class()->SightRange + Rule.GapShroudRadius &&
|
|
|
|
(tech->House->IsPlayerControl ||
|
|
|
|
(tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)))) {
|
|
|
|
|
|
|
|
object->Look();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef OBSOLETE
|
|
|
|
/*
|
|
|
|
** Here all the player's vehicles will perform a look if they're within
|
|
|
|
** the shadow.
|
|
|
|
*/
|
|
|
|
for (int index = 0; index < Units.Count(); index++) {
|
|
|
|
UnitClass * unit = Units.Ptr(index);
|
|
|
|
if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) {
|
|
|
|
int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W;
|
|
|
|
if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) {
|
|
|
|
unit->Look();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (index = 0; index < Infantry.Count(); index++) {
|
|
|
|
InfantryClass * unit = Infantry.Ptr(index);
|
|
|
|
if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) {
|
|
|
|
int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W;
|
|
|
|
if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) {
|
|
|
|
unit->Look();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (index = 0; index < Vessels.Count(); index++) {
|
|
|
|
VesselClass * unit = Vessels.Ptr(index);
|
|
|
|
if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) {
|
|
|
|
int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W;
|
|
|
|
if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) {
|
|
|
|
unit->Look();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::UnJam_From -- Remove jamming on the cells within a specified radius. *
|
|
|
|
* *
|
|
|
|
* This routine is used to jam the cells around a specific location. *
|
|
|
|
* Typically, as a gap generator structure is created, this routine will be *
|
|
|
|
* called. Since it deals with MANY cells, it needs to be extremely *
|
|
|
|
* fast. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The coordinate that the jamming originates from. *
|
|
|
|
* *
|
|
|
|
* jamrange -- The distance in cells that jamming extends. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 11/09/1995 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::UnJam_From(CELL cell, int jamrange, HouseClass * house)
|
|
|
|
{
|
|
|
|
int xx; // Center cell X coordinate (bounds checking).
|
|
|
|
int const * ptr; // Offset pointer.
|
|
|
|
int count; // Counter for number of offsets to process.
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Units that are off-map cannot jam.
|
|
|
|
*/
|
|
|
|
if (!jamrange || jamrange > Rule.GapShroudRadius) 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[jamrange];
|
|
|
|
ptr = &RadiusOffset[0];
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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 > jamrange) continue;
|
|
|
|
if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Jam the cell. For incremental scans, then update
|
|
|
|
** adjacent cells as well. For full scans, just update
|
|
|
|
** the cell itself.
|
|
|
|
*/
|
|
|
|
Map.UnJam_Cell(newcell, house);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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 the cell value is WAY out of range, then it obviously can't be part of the game
|
|
|
|
** playfield.
|
|
|
|
*/
|
|
|
|
if ((unsigned)cell > MAP_CELL_TOTAL) return(false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the cell is off the left or right edge of the playfield, then return the "not in
|
|
|
|
** radar" flag.
|
|
|
|
*/
|
|
|
|
if ((unsigned)(Cell_X(cell) - MapCellX) >= (unsigned)MapCellWidth) return(false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the cell is off the top or bottom edge of the playfield, then return the "not in
|
|
|
|
** radar" flag.
|
|
|
|
*/
|
|
|
|
if ((unsigned)(Cell_Y(cell) - MapCellY) >= (unsigned)MapCellHeight) return(false);
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) {
|
|
|
|
short xlist[32];
|
|
|
|
List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist);
|
|
|
|
short const * list = xlist;
|
|
|
|
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_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist);
|
|
|
|
list = xlist;
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) {
|
|
|
|
short xlist[32];
|
|
|
|
List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist);
|
|
|
|
short const * list = xlist;
|
|
|
|
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_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist);
|
|
|
|
list = xlist;
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) {
|
|
|
|
short xlist[32];
|
|
|
|
List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist);
|
|
|
|
short const * list = xlist;
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) {
|
|
|
|
short xlist[32];
|
|
|
|
List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist);
|
|
|
|
short const * list = xlist;
|
|
|
|
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; y++) {
|
|
|
|
for (int x = 0; x < MapCellWidth; x++) {
|
|
|
|
CELL cell = (MapCellY+y) * MAP_CELL_W + (MapCellX+x);
|
|
|
|
value += (*this)[cell].Tiberium_Adjust(true);
|
|
|
|
(*this)[cell].Recalc_Attributes();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Write_Binary -- Pipes the map template data to the destination specified. *
|
|
|
|
* *
|
|
|
|
* This stores the template data from the map to the output pipe specified. The template *
|
|
|
|
* data consists of the template type number and template icon number for every cell on *
|
|
|
|
* the map. The output is organized in such a way so as to get maximum compression. *
|
|
|
|
* *
|
|
|
|
* INPUT: pipe -- Reference to the output pipe that will receive the map template data. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the total number of bytes output to the pipe. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/03/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int MapClass::Write_Binary(Pipe & pipe)
|
|
|
|
{
|
|
|
|
int total = 0;
|
|
|
|
|
|
|
|
LCWPipe comp(LCWPipe::COMPRESS);
|
|
|
|
comp.Put_To(&pipe);
|
|
|
|
|
|
|
|
CellClass * cellptr = &Array[0];
|
|
|
|
for (int i = 0; i < MAP_CELL_TOTAL; i++) {
|
|
|
|
total += comp.Put(&cellptr->TType, sizeof(cellptr->TType));
|
|
|
|
cellptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cellptr = &Array[0];
|
|
|
|
for (int i = 0; i < MAP_CELL_TOTAL; i++) {
|
|
|
|
total += comp.Put(&cellptr->TIcon, sizeof(cellptr->TIcon));
|
|
|
|
cellptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(total);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Read_Binary -- Reads the binary data from the straw specified. *
|
|
|
|
* *
|
|
|
|
* This routine will retrieve the map template data from the straw specified. *
|
|
|
|
* *
|
|
|
|
* INPUT: straw -- Reference to the straw that will supply the map template data. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Was the template data retrieved? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/03/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool MapClass::Read_Binary(Straw & straw)
|
|
|
|
{
|
|
|
|
LCWStraw decomp(LCWStraw::DECOMPRESS);
|
|
|
|
decomp.Get_From(&straw);
|
|
|
|
|
|
|
|
CELL cell;
|
|
|
|
CellClass * cellptr;
|
|
|
|
switch (NewINIFormat) {
|
|
|
|
default:
|
|
|
|
cellptr = &Array[0];
|
|
|
|
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
decomp.Get(&cellptr->TType, sizeof(cellptr->TType));
|
|
|
|
cellptr++;
|
|
|
|
}
|
|
|
|
cellptr = &Array[0];
|
|
|
|
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon));
|
|
|
|
cellptr->Recalc_Attributes();
|
|
|
|
cellptr++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
cellptr = &Array[0];
|
|
|
|
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
decomp.Get(&cellptr->TType, sizeof(cellptr->TType));
|
|
|
|
decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon));
|
|
|
|
cellptr->Recalc_Attributes();
|
|
|
|
cellptr++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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; }
|
|
|
|
/*
|
|
|
|
** Crate regeneration is handled here.
|
|
|
|
*/
|
|
|
|
if (Session.Type != GAME_NORMAL && Session.Options.Goodies) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Find any crate that has expired and then regenerate it at a new
|
|
|
|
** spot.
|
|
|
|
*/
|
|
|
|
for (int index = 0; index < ARRAY_SIZE(Crates); index++) {
|
|
|
|
if (Crates[index].Is_Expired()) {
|
|
|
|
Crates[index].Remove_It();
|
|
|
|
Place_Random_Crate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Bail early if there is no allowed growth or spread of Tiberium.
|
|
|
|
*/
|
|
|
|
if (!Rule.IsTGrowth && !Rule.IsTSpread) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Scan another block of the map in order to accumulate the potential
|
|
|
|
** Tiberium cells that can grow or spread.
|
|
|
|
*/
|
|
|
|
int subcount = MAP_CELL_TOTAL / (Rule.GrowthRate * TICKS_PER_MINUTE);
|
2020-08-06 17:44:54 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
** Use the Tiberium setting as a multiplier on growth rate. ST - 7/1/2020 3:05PM
|
|
|
|
*/
|
|
|
|
if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
|
|
|
|
if (Session.Options.Tiberium > 1) {
|
|
|
|
subcount *= Session.Options.Tiberium;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 20:16:20 +01:00
|
|
|
subcount = max(subcount, 1);
|
|
|
|
int index;
|
|
|
|
for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) {
|
|
|
|
CELL cell = index;
|
|
|
|
if (In_Radar(cell)) {
|
|
|
|
CellClass * ptr = &(*this)[cell];
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Tiberium cells can grow.
|
|
|
|
*/
|
|
|
|
if (ptr->Can_Tiberium_Grow()) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Either replace an existing recorded cell value or add the new cell value to
|
|
|
|
** the list.
|
|
|
|
*/
|
|
|
|
if (Random_Pick(0, TiberiumGrowthExcess) <= TiberiumGrowthCount) {
|
|
|
|
if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) {
|
|
|
|
TiberiumGrowth[TiberiumGrowthCount++] = cell;
|
|
|
|
} else {
|
|
|
|
TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TiberiumGrowthExcess++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Heavy Tiberium growth can spread.
|
|
|
|
*/
|
|
|
|
if (ptr->Can_Tiberium_Spread()) {
|
|
|
|
/*
|
|
|
|
** Either replace an existing recorded cell value or add the new cell value to
|
|
|
|
** the list.
|
|
|
|
*/
|
|
|
|
if (Random_Pick(0, TiberiumSpreadExcess) <= TiberiumSpreadCount) {
|
|
|
|
if (TiberiumSpreadCount < ARRAY_SIZE(TiberiumSpread)) {
|
|
|
|
TiberiumSpread[TiberiumSpreadCount++] = cell;
|
|
|
|
} else {
|
|
|
|
TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TiberiumSpreadExcess++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
subcount--;
|
|
|
|
if (subcount == 0) break;
|
|
|
|
}
|
|
|
|
TiberiumScan = index;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** When the entire map has been processed, proceed with tiberium (ore) growth
|
|
|
|
** and spread action.
|
|
|
|
*/
|
|
|
|
if (TiberiumScan >= MAP_CELL_TOTAL) {
|
|
|
|
TiberiumScan = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Growth logic.
|
|
|
|
*/
|
|
|
|
if (TiberiumGrowthCount) {
|
|
|
|
for (int i = 0; i < TiberiumGrowthCount; i++) {
|
|
|
|
CELL cell = TiberiumGrowth[i];
|
|
|
|
CellClass * newcell = &(*this)[cell];
|
|
|
|
newcell->Grow_Tiberium();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TiberiumGrowthCount = 0;
|
|
|
|
TiberiumGrowthExcess = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Spread logic.
|
|
|
|
*/
|
|
|
|
if (TiberiumSpreadCount) {
|
|
|
|
for (int i = 0; i < TiberiumSpreadCount; i++) {
|
|
|
|
Map[TiberiumSpread[i]].Spread_Tiberium();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TiberiumSpreadCount = 0;
|
|
|
|
TiberiumSpreadExcess = 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)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** Find a crate index that is free for assignment. If there are
|
|
|
|
** no free slots, then return with failure to place crate.
|
|
|
|
*/
|
|
|
|
int crateindex = 0;
|
|
|
|
for (crateindex = 0; crateindex < ARRAY_SIZE(Crates); crateindex++) {
|
|
|
|
if (!Crates[crateindex].Is_Valid()) break;
|
|
|
|
}
|
|
|
|
if (crateindex == ARRAY_SIZE(Crates)) {
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Give a good effort to scan for and place a crate down on the map.
|
|
|
|
*/
|
|
|
|
for (int index = 0; index < 1000; index++) {
|
|
|
|
CELL cell = Map.Pick_Random_Location();
|
|
|
|
|
|
|
|
if (Crates[crateindex].Create_Crate(cell)) {
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Remove_Crate -- Remove a crate from the specified cell. *
|
|
|
|
* *
|
|
|
|
* This will examine the cell and remove any crates there. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The cell to examine for crates and remove from. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Was a crate found at the location specified and was it removed? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 08/26/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool MapClass::Remove_Crate(CELL cell)
|
|
|
|
{
|
|
|
|
if (Session.Type != GAME_NORMAL) {
|
|
|
|
for (int index = 0; index < ARRAY_SIZE(Crates); index++) {
|
|
|
|
if (Crates[index].Is_Here(cell)) {
|
|
|
|
return(Crates[index].Remove_It());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (Session.Type == GAME_NORMAL) {
|
|
|
|
CellClass * cellptr = &(*this)[cell];
|
|
|
|
if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsCrate) {
|
|
|
|
cellptr->Overlay = OVERLAY_NONE;
|
|
|
|
cellptr->OverlayData = 0;
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
// } else {
|
|
|
|
// for (int index = 0; index < ARRAY_SIZE(Crates); index++) {
|
|
|
|
// if (Crates[index].Is_Here(cell)) {
|
|
|
|
// return(Crates[index].Remove_It());
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
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 = &Array[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 invalid spot,
|
|
|
|
** return an error.
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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) {
|
|
|
|
if (
|
|
|
|
((unsigned int)obj & 0xff000000) ||
|
|
|
|
((unsigned int)obj->Next & 0xff000000) ||
|
|
|
|
// ((unsigned int)obj->Trigger & 0xff000000) ||
|
|
|
|
obj->IsInLimbo ||
|
|
|
|
((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) {
|
|
|
|
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Validate Overlappers
|
|
|
|
*/
|
|
|
|
for (i = 0; i < ARRAY_SIZE((*this)[cell].CellClass::Overlapper); i++) {
|
|
|
|
obj = (*this)[cell].Overlapper[i];
|
|
|
|
if (obj) {
|
|
|
|
if (
|
|
|
|
((unsigned int)obj & 0xff000000) ||
|
|
|
|
((unsigned int)obj->Next & 0xff000000) ||
|
|
|
|
// ((unsigned int)obj->Trigger & 0xff000000) ||
|
|
|
|
obj->IsInLimbo ||
|
|
|
|
((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) {
|
|
|
|
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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 = Array[newcell].Cell_Occupier();
|
|
|
|
while (o != NULL) {
|
|
|
|
|
|
|
|
/*
|
2020-08-06 17:44:54 +01:00
|
|
|
** Special case check to ignore cloaked object if not allied with the player.
|
2020-05-27 20:16:20 +01:00
|
|
|
*/
|
|
|
|
// Change for client/server multiplayer. ST - 8/7/2019 10:35AM
|
|
|
|
//if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) {
|
2020-08-06 17:44:54 +01:00
|
|
|
if (!o->Is_Techno() || !((TechnoClass *)o)->Is_Cloaked(PlayerPtr)) {
|
2020-05-27 20:16:20 +01:00
|
|
|
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) {
|
2020-08-06 17:44:54 +01:00
|
|
|
if (!aircraft->Is_Cloaked(PlayerPtr)) {
|
2020-05-27 20:16:20 +01:00
|
|
|
int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -aircraft->Height)));
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Zone_Reset -- Resets all zone numbers to match the map. *
|
|
|
|
* *
|
|
|
|
* This routine will rescan the map and fill in the zone values for each of the cells. *
|
|
|
|
* All cells that are contiguous are given the same zone number. *
|
|
|
|
* *
|
|
|
|
* INPUT: method -- The method to recalculate the zones upon. If 1 then recalc non *
|
|
|
|
* crushable zone. If 2 then recalc crushable zone. If 3, then *
|
|
|
|
* recalc both zones. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: This is a time consuming routine. Call it as infrequently as possible. It must *
|
|
|
|
* be called whenever something that would affect contiguousness occurs. Example: *
|
|
|
|
* when a bridge is built or destroyed. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 09/22/1995 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool MapClass::Zone_Reset(int method)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** Zero out all zones to a null state.
|
|
|
|
*/
|
|
|
|
for (int index = 0; index < MAP_CELL_TOTAL; index++) {
|
|
|
|
if (method & MZONEF_NORMAL) {
|
|
|
|
Array[index].Zones[MZONE_NORMAL] = 0;
|
|
|
|
}
|
|
|
|
if (method & MZONEF_CRUSHER) {
|
|
|
|
Array[index].Zones[MZONE_CRUSHER] = 0;
|
|
|
|
}
|
|
|
|
if (method & MZONEF_DESTROYER) {
|
|
|
|
Array[index].Zones[MZONE_DESTROYER] = 0;
|
|
|
|
}
|
|
|
|
if (method & MZONEF_WATER) {
|
|
|
|
Array[index].Zones[MZONE_WATER] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Normal zone recalculation.
|
|
|
|
*/
|
|
|
|
if (method & MZONEF_NORMAL) {
|
|
|
|
int zone = 1; // Starting zone number.
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
if (Zone_Span(cell, zone, MZONE_NORMAL)) {
|
|
|
|
zone++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Crushable wall recalculation.
|
|
|
|
*/
|
|
|
|
if (method & MZONEF_CRUSHER) {
|
|
|
|
int zone = 1; // Starting zone number.
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
if (Zone_Span(cell, zone, MZONE_CRUSHER)) {
|
|
|
|
zone++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Wall destroyer zone recalculation.
|
|
|
|
*/
|
|
|
|
if (method & MZONEF_DESTROYER) {
|
|
|
|
int zone = 1; // Starting zone number.
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
if (Zone_Span(cell, zone, MZONE_DESTROYER)) {
|
|
|
|
zone++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Water based zone recalcuation.
|
|
|
|
*/
|
|
|
|
if (method & MZONEF_WATER) {
|
|
|
|
int zone = 1; // Starting zone number.
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
if (Zone_Span(cell, zone, MZONE_WATER)) {
|
|
|
|
zone++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. *
|
|
|
|
* *
|
|
|
|
* This routine is used to fill a zone value into the map. The map is "flood filled" from *
|
|
|
|
* the cell specified. All adjacent (8 connected) and generally passable terrain cells are *
|
|
|
|
* given the zone number specified. This routine checks for legality before filling *
|
|
|
|
* occurs. The routine is safe to call even if the legality of the cell is unknown at the *
|
|
|
|
* time of the call. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The cell to begin filling from. *
|
|
|
|
* *
|
|
|
|
* zone -- The zone number to assign to all adjacent cells. *
|
|
|
|
* *
|
|
|
|
* check -- The zone type to check against. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the number of cells marked by this routine. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: This routine is slow and recursive. Only use when necessary. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 09/25/1995 JLB : Created. *
|
|
|
|
* 10/05/1996 JLB : Examines crushable walls. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int MapClass::Zone_Span(CELL cell, int zone, MZoneType check)
|
|
|
|
{
|
|
|
|
int filled = 0;
|
|
|
|
int xbegin = Cell_X(cell);
|
|
|
|
int xend = xbegin;
|
|
|
|
int y = Cell_Y(cell);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Perform some preliminary legality checks. If the cell specified
|
|
|
|
** is illegal, then no further processing is necessary.
|
|
|
|
*/
|
|
|
|
if (y < MapCellY || y >= MapCellY+MapCellHeight || xbegin < MapCellX || xbegin >= MapCellX+MapCellWidth) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Find the full extent of the current span by first scanning leftward
|
|
|
|
** until a boundary is reached.
|
|
|
|
*/
|
|
|
|
for (; xbegin >= MapCellX; xbegin--) {
|
|
|
|
CellClass * cellptr = &(*this)[XY_Cell(xbegin, y)];
|
|
|
|
if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Special short circuit code to bail from this entire routine if
|
|
|
|
** it was called for a cell that is not a legal candidate for
|
|
|
|
** zone marking. This eliminates redundant processing and allows this
|
|
|
|
** routine to be called for illegal cells without causing an error.
|
|
|
|
*/
|
|
|
|
if (xbegin == Cell_X(cell)) return(0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Otherwise break out of the left scan since a boundary was discovered.
|
|
|
|
*/
|
|
|
|
xbegin++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xbegin = max(xbegin, MapCellX);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Scan rightward until a boundary is reached. This will then define the
|
|
|
|
** extent of the current span.
|
|
|
|
*/
|
|
|
|
for (; xend < MapCellX+MapCellWidth; xend++) {
|
|
|
|
CellClass * cellptr = &(*this)[XY_Cell(xend, y)];
|
|
|
|
if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) {
|
|
|
|
xend--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xend = min(xend, MapCellX+MapCellWidth-1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** At this point we know the bounds of the current span. Fill in the zone values
|
|
|
|
** for the entire span.
|
|
|
|
*/
|
|
|
|
for (int x = xbegin; x <= xend; x++) {
|
|
|
|
(*this)[XY_Cell(x, y)].Zones[check] = zone;
|
|
|
|
filled++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Now scan the upper and lower shadow rows. If any of these rows contain
|
|
|
|
** candidate cells, then recursively call the span process for them. Take
|
|
|
|
** note that the adjacent span scanning starts one cell wider on each
|
|
|
|
** end of the scan. This is necessary because diagonals are considered
|
|
|
|
** adjacent.
|
|
|
|
*/
|
|
|
|
for (int x = xbegin-1; x <= xend; x++) {
|
|
|
|
filled += Zone_Span(XY_Cell(x, y-1), zone, check);
|
|
|
|
filled += Zone_Span(XY_Cell(x, y+1), zone, check);
|
|
|
|
}
|
|
|
|
return(filled);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
* checkflagged -- Whether the cell's flagged status is checked (used when dropping). *
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
*=============================================================================================*/
|
2020-08-06 17:44:54 +01:00
|
|
|
CELL MapClass::Nearby_Location(CELL cell, SpeedType speed, int zone, MZoneType check, bool checkflagged, int locationmod) const
|
2020-05-27 20:16:20 +01:00
|
|
|
{
|
|
|
|
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(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) {
|
|
|
|
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(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) {
|
|
|
|
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(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) {
|
|
|
|
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(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) {
|
|
|
|
topten[count++] = newcell;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (count == ARRAY_SIZE(topten)) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 0) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 0) {
|
2020-08-06 17:44:54 +01:00
|
|
|
return(topten[(Frame+locationmod) % count]);
|
2020-05-27 20:16:20 +01:00
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Base_Region -- Finds the owner and base zone for specified cell. *
|
|
|
|
* *
|
|
|
|
* This routine is used to determine what base the specified cell is close to and what *
|
|
|
|
* zone of that base the cell lies in. This routine is particularly useful in letting the *
|
|
|
|
* computer know when the player targets a destination near a computer's base. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The cell that is to be checked. *
|
|
|
|
* *
|
|
|
|
* house -- Reference to the house type number. This value will be set if a base *
|
|
|
|
* was found nearby the specified cell. *
|
|
|
|
* *
|
|
|
|
* zone -- The zone that the cell is located in IF the cell is near a base. *
|
|
|
|
* *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Was a base near the specified cell found? If not, then the 'house' and 'zone' *
|
|
|
|
* reference values are left in an undefined state and the return value will be *
|
|
|
|
* false. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 10/05/1995 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool MapClass::Base_Region(CELL cell, HousesType & house, ZoneType & zone) const
|
|
|
|
{
|
|
|
|
if ((unsigned)cell < MAP_CELL_TOTAL && In_Radar(cell)) {
|
|
|
|
for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
|
|
|
|
HouseClass * h = HouseClass::As_Pointer(house);
|
|
|
|
|
|
|
|
if (h && h->IsActive && !h->IsDefeated && h->Center) {
|
|
|
|
zone = h->Which_Zone(cell);
|
|
|
|
if (zone != ZONE_NONE) {
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. *
|
|
|
|
* *
|
|
|
|
* This routine will destroy the bridge at the location specified. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- A cell that can uniquely identify the bridge. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Was the bridge destroyed? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/29/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
|
|
|
|
// Need to inform the server of the cell change so it can communicate with the clients - SKY
|
|
|
|
extern void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name);
|
|
|
|
|
|
|
|
struct CellUpdateStruct
|
|
|
|
{
|
|
|
|
const TemplateTypeClass* Type;
|
|
|
|
CELL Cell;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int MAX_UPDATES = 8;
|
|
|
|
|
|
|
|
static void Add_Cell_Update(CellUpdateStruct* updates, int& count, TemplateType type, CELL cell)
|
|
|
|
{
|
|
|
|
new TemplateClass(type, cell);
|
|
|
|
|
|
|
|
assert(count < MAX_UPDATES);
|
|
|
|
if (count < MAX_UPDATES)
|
|
|
|
{
|
|
|
|
updates[count].Type = TemplateTypes.Ptr((int)type);
|
|
|
|
updates[count].Cell = cell;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MapClass::Destroy_Bridge_At(CELL cell)
|
|
|
|
{
|
|
|
|
bool destroyed = false;
|
|
|
|
if (In_Radar(cell) && !Special.IsCaptureTheFlag) {
|
|
|
|
CellClass * cellptr = &(*this)[cell];
|
|
|
|
TemplateType ttype = cellptr->TType;
|
|
|
|
|
|
|
|
CellUpdateStruct cell_updates[MAX_UPDATES];
|
|
|
|
int update_count = 0;
|
|
|
|
|
|
|
|
if (ttype == TEMPLATE_BRIDGE1 || ttype == TEMPLATE_BRIDGE2) {
|
|
|
|
int icon = cellptr->TIcon;
|
|
|
|
int w = TemplateTypeClass::As_Reference(ttype).Width;
|
|
|
|
int h = TemplateTypeClass::As_Reference(ttype).Height;
|
|
|
|
|
|
|
|
cell -= icon % w;
|
|
|
|
cell -= MAP_CELL_W * (icon / w);
|
|
|
|
|
|
|
|
if (ttype == TEMPLATE_BRIDGE1) {
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1H, cell);
|
|
|
|
} else {
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2H, cell);
|
|
|
|
}
|
|
|
|
|
|
|
|
new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + w/2 + (h/2)*MAP_CELL_W));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ttype == TEMPLATE_BRIDGE1H || ttype == TEMPLATE_BRIDGE2H) {
|
|
|
|
int icon = cellptr->TIcon;
|
|
|
|
int bridge_w = TemplateTypeClass::As_Reference(ttype).Width;
|
|
|
|
int bridge_h = TemplateTypeClass::As_Reference(ttype).Height;
|
|
|
|
|
|
|
|
cell -= icon % bridge_w;
|
|
|
|
cell -= MAP_CELL_W * (icon / bridge_w);
|
|
|
|
|
|
|
|
if (ttype == TEMPLATE_BRIDGE1H) {
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1D, cell);
|
|
|
|
} else {
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2D, cell);
|
|
|
|
}
|
|
|
|
|
|
|
|
Scen.BridgeCount--;
|
|
|
|
Scen.IsBridgeChanged = true;
|
|
|
|
new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + bridge_w/2 + (bridge_h/2)*MAP_CELL_W));
|
|
|
|
Map.Zone_Reset(MZONEF_ALL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Now, loop through all the bridge cells and find anyone standing
|
|
|
|
** on a destroyed part (which is now river), and nuke them.
|
|
|
|
*/
|
|
|
|
CELL cell2 = cell;
|
|
|
|
for (int y = 0; y < bridge_h; y++) {
|
|
|
|
for (int x = 0; x < bridge_w; x++) {
|
|
|
|
CellClass * bridge_cell = &(*this)[cell2];
|
|
|
|
if (bridge_cell->TType == ttype) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Any unit that is firing on the bridge at this location, will stop
|
|
|
|
** firing because the bridge has been destroyed.
|
|
|
|
*/
|
|
|
|
Detach_This_From_All(As_Target(cell2), true);
|
|
|
|
|
|
|
|
ObjectClass * obj = bridge_cell->Cell_Occupier();
|
|
|
|
while (obj != NULL) {
|
|
|
|
ObjectClass * next = obj->Next;
|
|
|
|
if (obj->Is_Techno()) {
|
|
|
|
int damage = obj->Strength;
|
|
|
|
obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
|
|
|
|
}
|
|
|
|
obj = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cell2++;
|
|
|
|
}
|
|
|
|
cell2 += MAP_CELL_W - bridge_w;
|
|
|
|
}
|
|
|
|
Shake_The_Screen(3);
|
|
|
|
|
|
|
|
destroyed = true;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
** All this code is for the multi-part bridges.
|
|
|
|
*/
|
|
|
|
if (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3E) {
|
|
|
|
int icon = cellptr->TIcon;
|
|
|
|
int w = TemplateTypeClass::As_Reference(ttype).Width;
|
|
|
|
int h = TemplateTypeClass::As_Reference(ttype).Height;
|
|
|
|
|
|
|
|
cell -= icon % w;
|
|
|
|
cell -= MAP_CELL_W * (icon / w);
|
|
|
|
switch (ttype) {
|
|
|
|
case TEMPLATE_BRIDGE_1A:
|
|
|
|
case TEMPLATE_BRIDGE_1B:
|
|
|
|
case TEMPLATE_BRIDGE_2A:
|
|
|
|
case TEMPLATE_BRIDGE_2B:
|
|
|
|
case TEMPLATE_BRIDGE_3A:
|
|
|
|
case TEMPLATE_BRIDGE_3B:
|
|
|
|
ttype++;
|
|
|
|
Add_Cell_Update(cell_updates, update_count, ttype, cell);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If we were a middle piece that just got blown up, update the
|
|
|
|
** adjoining pieces to make sure they're shaped properly.
|
|
|
|
*/
|
|
|
|
if (ttype == TEMPLATE_BRIDGE_3C) {
|
|
|
|
// check the template below us, at x-1, y+1
|
|
|
|
CELL cell2 = cell + (MAP_CELL_W - 1);
|
|
|
|
CellClass * celptr = &(*this)[cell2];
|
|
|
|
if (celptr->TType == TEMPLATE_BRIDGE_3C) {
|
|
|
|
// It was also destroyed. Update us and it.
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell);
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now check the template above us, at x+1, y-1.
|
|
|
|
cell2 = cell - (MAP_CELL_W - 1);
|
|
|
|
celptr = &(*this)[cell2];
|
|
|
|
if (celptr->TType == TEMPLATE_BRIDGE_3C) {
|
|
|
|
if (cellptr->TType == TEMPLATE_BRIDGE_3D) {
|
|
|
|
// if we're already one-sided, turn us to all water
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell);
|
|
|
|
} else {
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell);
|
|
|
|
}
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2);
|
|
|
|
}
|
|
|
|
Map.Zone_Reset(MZONEF_ALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If we're an end bridge piece, update the adjoining piece to
|
|
|
|
** be the proper shape.
|
|
|
|
*/
|
|
|
|
if (cellptr->TType == TEMPLATE_BRIDGE_1C) {
|
|
|
|
Scen.BridgeCount--;
|
|
|
|
Scen.IsBridgeChanged = true;
|
|
|
|
|
|
|
|
// Point to the template below us, x-1, y+2
|
|
|
|
CELL cell2 = cell + (MAP_CELL_W * 2) - 1;
|
|
|
|
switch ((*this)[cell2].TType) {
|
|
|
|
case TEMPLATE_BRIDGE_3A:
|
|
|
|
case TEMPLATE_BRIDGE_3B:
|
|
|
|
case TEMPLATE_BRIDGE_3C:
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2);
|
|
|
|
break;
|
|
|
|
case TEMPLATE_BRIDGE_3D:
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (cellptr->TType == TEMPLATE_BRIDGE_2C) {
|
|
|
|
// Point to the template above us, x+2, y-1
|
|
|
|
CELL cell2 = cell - (MAP_CELL_W - 2);
|
|
|
|
switch ((*this)[cell2].TType) {
|
|
|
|
case TEMPLATE_BRIDGE_3A:
|
|
|
|
case TEMPLATE_BRIDGE_3B:
|
|
|
|
case TEMPLATE_BRIDGE_3C:
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2);
|
|
|
|
break;
|
|
|
|
case TEMPLATE_BRIDGE_3E:
|
|
|
|
Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cellptr->TType == TEMPLATE_BRIDGE_1C ||
|
|
|
|
cellptr->TType == TEMPLATE_BRIDGE_2C ||
|
|
|
|
(cellptr->TType >= TEMPLATE_BRIDGE_3C && cellptr->TType <= TEMPLATE_BRIDGE_3E)) {
|
|
|
|
int x, y, tdata = 0;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
|
|
for (x = 0; x < w; x++) {
|
|
|
|
CellClass * ptr = &(*this)[(CELL)(cell + x)];
|
|
|
|
if (ptr->TType == cellptr->TType || ptr->Land_Type() == LAND_RIVER || ptr->Land_Type() == LAND_WATER) {
|
|
|
|
Detach_This_From_All(As_Target((CELL)(cell+tdata)), true);
|
|
|
|
|
|
|
|
ObjectClass * obj = ptr->Cell_Occupier();
|
|
|
|
while (obj != NULL) {
|
|
|
|
ObjectClass * next = obj->Next;
|
|
|
|
if (obj->Is_Techno()) {
|
|
|
|
int damage = obj->Strength;
|
|
|
|
obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true);
|
|
|
|
}
|
|
|
|
obj = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
tdata++;
|
|
|
|
}
|
|
|
|
cell += MAP_CELL_W;
|
|
|
|
}
|
|
|
|
Map.Zone_Reset(MZONEF_ALL);
|
|
|
|
destroyed = true;
|
|
|
|
}
|
|
|
|
Shake_The_Screen(3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cell_index = 0;
|
|
|
|
char cell_name[_MAX_PATH] = { 0 };
|
|
|
|
char icon_number[32] = { 0 };
|
|
|
|
int icon = 0;
|
|
|
|
void *image_data = 0;
|
|
|
|
for (int i = 0; i < update_count; i++) {
|
|
|
|
const TemplateTypeClass* type = cell_updates[i].Type;
|
|
|
|
CELL cell = cell_updates[i].Cell;
|
|
|
|
for (int y = 0; y < type->Height; y++) {
|
|
|
|
for (int x = 0; x < type->Width; x++) {
|
|
|
|
CELL newcell = cell + y * MAP_CELL_W + x;
|
|
|
|
if (Map.In_Radar(newcell)) {
|
|
|
|
CellClass * newcellptr = &Map[newcell];
|
|
|
|
if (newcellptr->Get_Template_Info(cell_name, icon, image_data)) {
|
|
|
|
itoa(icon, icon_number, 10);
|
|
|
|
strncat(cell_name, "_i", 32);
|
|
|
|
strncat(cell_name, icon_number, 32);
|
|
|
|
strncat(cell_name, ".tga", 32);
|
|
|
|
On_Update_Map_Cell(Cell_X(newcell), Cell_Y(newcell), cell_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(destroyed);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Detach -- Remove specified object from map references. *
|
|
|
|
* *
|
|
|
|
* This routine will take the object (represented by a target value) and remove all *
|
|
|
|
* references to it from the map. Typically, this is used to remove trigger reference. *
|
|
|
|
* *
|
|
|
|
* INPUT: target -- The target object to remove from the map. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/28/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::Detach(TARGET target, bool)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** Remove this trigger from the map zone/line tracking list.
|
|
|
|
*/
|
|
|
|
if (Is_Target_Trigger(target)) {
|
|
|
|
for (int index = 0; index < MapTriggers.Count(); index++) {
|
|
|
|
if (MapTriggers[index] == As_Trigger(target)) {
|
|
|
|
MapTriggers.Delete(index);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Loop through all cells; remove any reference to this trigger
|
|
|
|
*/
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
if ((*this)[cell].Trigger == As_Trigger(target)) {
|
|
|
|
(*this)[cell].Trigger = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. *
|
|
|
|
* *
|
|
|
|
* This will examine the entire map and return the number of bridges that are intact. An *
|
|
|
|
* intact bridge is one that units can travel over. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the number of intact bridges on the map. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/28/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int MapClass::Intact_Bridge_Count(void) const
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** Count all non-destroyed bridges on the map.
|
|
|
|
*/
|
|
|
|
int count = 0;
|
|
|
|
CellClass const * cellptr = &(*this)[(CELL)0];
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
switch (cellptr->TType) {
|
|
|
|
case TEMPLATE_BRIDGE1:
|
|
|
|
case TEMPLATE_BRIDGE1H:
|
|
|
|
case TEMPLATE_BRIDGE2:
|
|
|
|
case TEMPLATE_BRIDGE2H:
|
|
|
|
case TEMPLATE_BRIDGE_1A:
|
|
|
|
case TEMPLATE_BRIDGE_1B:
|
|
|
|
if (cellptr->TIcon == 6) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cellptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(count);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Pick_Random_Location -- Picks a random location on the map. *
|
|
|
|
* *
|
|
|
|
* This routine will pick a random location on the map. It performs no legality checking *
|
|
|
|
* other than forcing the cell to be on the map proper. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with a cell that is within the map. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 09/25/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
CELL MapClass::Pick_Random_Location(void) const
|
|
|
|
{
|
|
|
|
int x = Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1);
|
|
|
|
int y = Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1);
|
|
|
|
|
|
|
|
return(XY_Cell(x, y));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if (1)
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) *
|
|
|
|
* *
|
|
|
|
* INPUT: House to shroud *
|
|
|
|
* *
|
|
|
|
* OUTPUT: None
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 10/19/1996 BWG : Created. *
|
|
|
|
* 08/12/2019 ST : Updated for client/server multiplayer *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::Shroud_The_Map(HouseClass *house)
|
|
|
|
{
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
CellClass * cellptr = &Map[cell];
|
|
|
|
if (cellptr->Is_Mapped(house) || cellptr->Is_Visible(house)) {
|
|
|
|
|
|
|
|
cellptr->Redraw_Objects();
|
|
|
|
|
|
|
|
/*
|
|
|
|
** BG: remove "ring of darkness" around edge of map.
|
|
|
|
*/
|
|
|
|
int x = Cell_X(cell);
|
|
|
|
int y = Cell_Y(cell);
|
|
|
|
if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) &&
|
|
|
|
y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) {
|
|
|
|
cellptr->Set_Mapped(house, false);
|
|
|
|
cellptr->Set_Visible(house, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) {
|
|
|
|
ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index];
|
|
|
|
if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == house) {
|
|
|
|
layer_object->Look();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Flag_To_Redraw(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
//
|
|
|
|
// Old code for posterity. ST - 8/12/2019 11:34AM
|
|
|
|
//
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with a cell that is within the map. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 10/19/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void MapClass::Shroud_The_Map(void)
|
|
|
|
{
|
|
|
|
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
|
|
|
|
CellClass * cellptr = &Map[cell];
|
|
|
|
if (cellptr->IsMapped || cellptr->IsVisible) {
|
|
|
|
cellptr->Redraw_Objects();
|
|
|
|
/*
|
|
|
|
** BG: remove "ring of darkness" around edge of map.
|
|
|
|
*/
|
|
|
|
int x = Cell_X(cell);
|
|
|
|
int y = Cell_Y(cell);
|
|
|
|
if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) &&
|
|
|
|
y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) {
|
|
|
|
cellptr->IsMapped = false;
|
|
|
|
cellptr->IsVisible = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) {
|
|
|
|
ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index];
|
|
|
|
if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == PlayerPtr) {
|
|
|
|
layer_object->Look();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Flag_To_Redraw(true);
|
|
|
|
}
|
|
|
|
#endif
|