CnC_Remastered_Collection/REDALERT/MAP.CPP

2294 lines
107 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: /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);
/*
** 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;
}
}
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) {
/*
** Special case check to ignore cloaked object if not allied with the player.
*/
// Change for client/server multiplayer. ST - 8/7/2019 10:35AM
//if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) {
if (!o->Is_Techno() || !((TechnoClass *)o)->Is_Cloaked(PlayerPtr)) {
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, -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. *
*=============================================================================================*/
CELL MapClass::Nearby_Location(CELL cell, SpeedType speed, int zone, MZoneType check, bool checkflagged, int locationmod) 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(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) {
return(topten[(Frame+locationmod) % count]);
}
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