CnC_Remastered_Collection/REDALERT/CELL.CPP

3331 lines
140 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/CELL.CPP 4 3/14/97 1: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 : CELL.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : April 29, 1994 *
* *
* Last Update : October 6, 1996 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. *
* CellClass::Adjust_Threat -- Allows adjustment of threat at cell level *
* CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. *
* CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. *
* CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. *
* CellClass::CellClass -- Constructor for cell objects. *
* CellClass::Cell_Building -- Return with building at specified cell. *
* CellClass::Cell_Color -- Determine what radar color to use for this cell. *
* CellClass::Cell_Coord -- Returns the coordinate of this cell. *
* CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell *
* CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. *
* CellClass::Cell_Object -- Returns with clickable object in cell. *
* CellClass::Cell_Techno -- Return with the unit/building at specified cell. *
* CellClass::Cell_Terrain -- Determines terrain object in cell. *
* CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. *
* CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. *
* CellClass::Clear_Icon -- Calculates what the clear icon number should be. *
* CellClass::Closest_Free_Spot -- returns free spot closest to given coord *
* CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. *
* CellClass::Draw_It -- Draws the cell imagery at the location specified. *
* CellClass::Flag_Place -- Places a house flag down on the cell. *
* CellClass::Flag_Remove -- Removes the house flag from the cell. *
* CellClass::Goodie_Check -- Performs crate discovery logic. *
* CellClass::Grow_Tiberium -- Grows the tiberium in the cell. *
* CellClass::Incoming -- Causes objects in cell to "run for cover". *
* CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. *
* CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. *
* CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel *
* CellClass::Occupy_Down -- Flag occupation of specified cell. *
* CellClass::Occupy_Up -- Removes occupation flag from the specified cell. *
* CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over*
* CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. *
* CellClass::Overlap_Up -- Removes overlap flag for the cell. *
* CellClass::Read -- Reads a particular cell value from a save game file. *
* CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. *
* CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. *
* CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. *
* CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. *
* CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. *
* CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
* CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE *
* CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. *
* CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. *
* CellClass::Wall_Update -- Updates the imagery for wall objects in cell. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "vortex.h"
/*
** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:50PM
*/
#include "SidebarGlyphx.h"
/***********************************************************************************************
* CellClass::CellClass -- Constructor for cell objects. *
* *
* A cell object is constructed into an empty state. It contains no specific objects, *
* templates, or overlays. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/09/1994 JLB : Created. *
* 02/20/1996 JLB : Uses initializer list. *
*=============================================================================================*/
CellClass::CellClass(void) :
ID(Map.ID(this)),
IsPlot(false),
IsCursorHere(false),
IsMapped(false),
IsVisible(false),
IsWaypoint(false),
IsRadarCursor(false),
IsFlagged(false),
IsToShroud(false),
Jammed(0),
Trigger(NULL),
TType(TEMPLATE_NONE),
TIcon(0),
Overlay(OVERLAY_NONE),
OverlayData(0),
Smudge(SMUDGE_NONE),
SmudgeData(0),
Owner(HOUSE_NONE),
InfType(HOUSE_NONE),
OccupierPtr(0),
Land(LAND_CLEAR),
OverrideLand(LAND_NONE),
IsMappedByPlayerMask(0),
IsVisibleByPlayerMask(0),
CTFFlag(NULL)
{
for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) {
Zones[zone] = 0;
}
Flag.Composite = 0;
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
Overlapper[index] = 0;
}
}
/***********************************************************************************************
* CellClass::Cell_Color -- Determine what radar color to use for this cell. *
* *
* Use this routine to determine what radar color to render a radar *
* pixel with. This routine is called many many times to render the *
* radar map, so it must be fast. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with the color to display the radar pixel with. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/01/1994 JLB : Created. *
* 04/30/1994 JLB : Converted to member function. *
* 05/31/1994 JLB : Takes into account any stealth characteristics of object. *
*=============================================================================================*/
int CellClass::Cell_Color(bool override) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
BuildingClass * object = Cell_Building();
if (object && !object->Class->IsInvisible) {
return(ColorRemaps[object->House->RemapColor].Bar);
}
if (override) {
return(TBLACK);
}
if (LastTheater == THEATER_SNOW) {
return(::SnowColor[Land_Type()]);
} else {
return(::GroundColor[Land_Type()]);
}
}
/***********************************************************************************************
* CellClass::Cell_Techno -- Return with the unit/building at specified cell. *
* *
* Returns an object located in the cell. If there is a *
* building present, it returns a pointer to that, otherwise it returns *
* a pointer to one of the units there. If nothing is present in the *
* specified cell, then it returns NULL. *
* *
* INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting *
* the desired object within the cell. *
* *
* OUTPUT: Returns a pointer to a building or unit located in cell. If *
* nothing present, just returns NULL. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/05/1992 JLB : Created. *
* 04/30/1994 JLB : Converted to member function. *
*=============================================================================================*/
TechnoClass * CellClass::Cell_Techno(int x, int y) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
ObjectClass * object;
COORDINATE click; // Coordinate of click relative to cell corner.
TechnoClass * close = NULL;
long distance = 0; // Recorded closest distance.
/*
** Create a coordinate value that represent the pixel location within the cell. This is
** actually the lower significant bits (leptons) of a regular coordinate value.
*/
click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));
if (Cell_Occupier()) {
object = Cell_Occupier();
while (object && object->IsActive) {
if (object->Is_Techno()) {
COORDINATE coord = Coord_Fraction(object->Center_Coord());
long dist = Distance(coord, click);
if (!close || dist < distance) {
close = (TechnoClass *)object;
distance = dist;
}
}
object = object->Next;
}
}
return(close);
}
/***************************************************************************
* CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell *
* *
* INPUT: RTTIType the RTTI type we are searching for *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/17/1995 PWG : Created. *
* 06/12/1995 JLB : Returns object class pointer. *
*=========================================================================*/
ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
assert(rtti != RTTI_NONE);
ObjectClass * object = Cell_Occupier();
while (object != NULL && object->IsActive) {
if (object->What_Am_I() == rtti) {
return(object);
}
object = object->Next;
}
return(NULL);
}
/***********************************************************************************************
* CellClass::Cell_Building -- Return with building at specified cell. *
* *
* Given a cell, determine if there is a building associated *
* and return with a pointer to this building. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with a pointer to the building associated with the *
* cell. If there is no building associated, then NULL is *
* returned. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/05/1992 JLB : Created. *
* 04/30/1994 JLB : Converted to member function. *
*=============================================================================================*/
BuildingClass * CellClass::Cell_Building(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING));
}
/***********************************************************************************************
* CellClass::Cell_Terrain -- Determines terrain object in cell. *
* *
* This routine is used to determine the terrain object (if any) that *
* overlaps this cell. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with a pointer to the terrain object that overlaps *
* this cell. If there is no terrain object present, then NULL *
* is returned. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/18/1994 JLB : Created. *
*=============================================================================================*/
TerrainClass * CellClass::Cell_Terrain(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN));
}
/***********************************************************************************************
* CellClass::Cell_Object -- Returns with clickable object in cell. *
* *
* This routine is used to determine which object is to be selected *
* by a player click upon the cell. Not all objects that overlap the *
* cell are selectable by the player. This routine sorts out which *
* is which and returns with the appropriate object pointer. *
* *
* INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when *
* selecting the object within the cell. This plays a role in those cases *
* where several objects (such as infantry) exist within the same cell. *
* *
* OUTPUT: Returns with pointer to the object clickable within the *
* cell. NULL is returned if there is no clickable object *
* present. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/13/1994 JLB : Created. *
*=============================================================================================*/
ObjectClass * CellClass::Cell_Object(int x, int y) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
ObjectClass * ptr;
/*
** Hack so that aircraft landed on helipads can still be selected if directly
** clicked on.
*/
ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT);
if (ptr) {
return(ptr);
}
ptr = Cell_Techno(x, y);
if (ptr) {
return(ptr);
}
ptr = Cell_Terrain();
if (ptr) return(ptr);
return(ptr);
}
/***********************************************************************************************
* CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. *
* *
* This is a low level routine that marks all objects that overlap this *
* cell to be redrawn. It is necessary to call this routine whenever *
* the underlying icon has to be redrawn. *
* *
* INPUT: forced -- Should this redraw be forced even if flags *
* indicate that it would be redundant? *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/18/1994 JLB : Created. *
* 06/20/1994 JLB : Simplified to use object pointers. *
* 12/24/1994 JLB : Only checks if cell is in view and not flagged already. *
*=============================================================================================*/
void CellClass::Redraw_Objects(bool forced)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
CELL cell = Cell_Number();
if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) {
/*
** Flag the icon to be redrawn.
*/
Map.Flag_Cell(cell);
/*
** Flag the main object in the cell to be redrawn.
*/
if (Cell_Occupier() != NULL) {
ObjectClass * optr = Cell_Occupier();
while (optr != NULL && optr->IsActive) {
#ifdef SORTDRAW
if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) {
optr->Mark(MARK_CHANGE);
}
#else
optr->Mark(MARK_CHANGE);
#endif
if (optr->Next != NULL && !optr->Next->IsActive) {
optr->Next = NULL;
}
optr = optr->Next;
}
}
#ifdef SORTDRAW
/*
** Flag any overlapping object in this cell to be redrawn.
*/
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
if (Overlapper[index]) {
assert(Overlapper[index]->IsActive);
if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) {
Overlapper[index]->Mark(MARK_CHANGE);
}
}
}
#else
/*
** Flag any overlapping object in this cell to be redrawn.
*/
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
if (Overlapper[index] != NULL) {
if (!Overlapper[index]->IsActive) {
Overlapper[index] = NULL;
} else {
Overlapper[index]->Mark(MARK_CHANGE);
}
}
}
#endif
}
}
/***********************************************************************************************
* CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. *
* *
* This determines if the cell can become a proper foundation for *
* building placement. *
* *
* INPUT: loco -- The locomotion of the object trying to consider if this cell is *
* generally clear. Buildings use the value of SPEED_NONE. *
* *
* OUTPUT: bool; Is this cell generally clear (usually for building purposes)? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/18/1994 JLB : Created. *
* 06/25/1996 JLB : Handles different locomotion types. *
* 10/05/1996 JLB : Checks for crushable walls and crushable object. *
*=============================================================================================*/
bool CellClass::Is_Clear_To_Build(SpeedType loco) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
/*
** During scenario initialization, passability is always guaranteed.
*/
if (ScenarioInit) return(true);
/*
** If there is an object there, then don't allow building.
*/
if (Cell_Object() != NULL) {
return(false);
}
/*
** Prevents a building from being placed over a flag object.
*/
#ifdef FIXIT_FLAG_CHECK
if (IsFlagged) {
return(false);
}
#endif
/*
** Walls are always considered to block the terrain for general passability
** purposes. In normal game mode, all overlays are not buildable.
*/
if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) {
return(false);
}
/*
** Building over a bib is not allowed.
*/
if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) {
return(false);
}
/*
** Building on certain kinds of terrain is prohibited -- bridges in particular.
** If the locomotion type is SPEED_NONE, then this check is presumed to be
** for the purposes of building.
*/
if (loco == SPEED_NONE) {
if (Is_Bridge_Here()) {
return(false);
}
return(::Ground[Land_Type()].Build);
} else {
if (::Ground[Land_Type()].Cost[loco] == fixed(0)) {
// if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) {
return(false);
}
return(true);
}
}
/***********************************************************************************************
* CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. *
* *
* This routine recalculates the ground type in the cell. The speeds the find path *
* algorithm and other determinations of the cell type. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/29/1994 JLB : Created. *
* 06/20/1994 JLB : Knows about template pointer in cell object. *
*=============================================================================================*/
void CellClass::Recalc_Attributes(void)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
/*
** Special override for interior terrain set so that a non-template or a clear template
** is equivalent to impassable rock.
*/
if (LastTheater == THEATER_INTERIOR) {
if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) {
Land = LAND_ROCK;
return;
}
}
/*
** Check for wall effects.
*/
if (Overlay != OVERLAY_NONE) {
Land = OverlayTypeClass::As_Reference(Overlay).Land;
if (Land != LAND_CLEAR) return;
}
/*
** If there is a template associated with this cell, then fetch the
** land type given the template type and icon number.
*/
if (TType != TEMPLATE_NONE && TType != 255) {
TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType);
Land = ttype->Land_Type(TIcon);
return;
}
/*
** No template is the same as clear terrain.
*/
Land = LAND_CLEAR;
}
/***********************************************************************************************
* CellClass::Occupy_Down -- Flag occupation of specified cell. *
* *
* This routine is used to mark the cell as being occupied by the specified object. *
* *
* INPUT: object -- The object that is to occupy the cell *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
* 11/29/1994 JLB : Simplified. *
*=============================================================================================*/
void CellClass::Occupy_Down(ObjectClass * object)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
assert(object != NULL && object->IsActive);
ObjectClass * optr;
if (object == NULL) return;
/*
** Always add buildings to the end of the occupation chain. This is necessary because
** the occupation chain is a single list even though buildings occupy more than one
** cell. If more than one building is allowed to occupy the same cell, then this chain
** logic will fail.
*/
if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) {
optr = Cell_Occupier();
while (optr->Next != NULL) {
assert(optr != object);
assert(optr->What_Am_I() != RTTI_BUILDING);
optr = optr->Next;
}
optr->Next = object;
object->Next = 0;
} else {
object->Next = Cell_Occupier();
OccupierPtr = object;
}
Map.Radar_Pixel(Cell_Number());
/*
** If being placed down on a visible square, then flag this
** techno object as being revealed to the player.
*/
// Changes for client/server multiplayer. ST - 8/2/2019 2:51PM
//if (IsMapped || Session.Type != GAME_NORMAL) {
// object->Revealed(PlayerPtr);
//}
if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
if (IsMapped || Session.Type != GAME_NORMAL) {
object->Revealed(PlayerPtr);
}
} else {
for (int i = 0; i < Session.Players.Count(); i++) {
HousesType house_type = Session.Players[i]->Player.ID;
if (Is_Visible(house_type)) {
HouseClass *house = HouseClass::As_Pointer(house_type);
object->Revealed(house);
}
}
}
/*
** Special occupy bit set.
*/
switch (object->What_Am_I()) {
case RTTI_BUILDING:
Flag.Occupy.Building = true;
break;
case RTTI_VESSEL:
case RTTI_AIRCRAFT:
case RTTI_UNIT:
Flag.Occupy.Vehicle = true;
break;
case RTTI_TERRAIN:
Flag.Occupy.Monolith = true;
break;
default:
break;
}
}
/***********************************************************************************************
* CellClass::Occupy_Up -- Removes occupation flag from the specified cell. *
* *
* This routine will lift the object from the cell and free the cell to be occupied by *
* another object. Only if the cell was previously marked with the object specified, will *
* the object be lifted off. This routine is the counterpart to Occupy_Down(). *
* *
* INPUT: object -- The object that is being lifted off. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
* 11/29/1994 JLB : Fixed to handle next pointer in previous object. *
*=============================================================================================*/
void CellClass::Occupy_Up(ObjectClass * object)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
assert(object != NULL && object->IsActive);
if (object == NULL) return;
ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain.
if (optr == object) {
OccupierPtr = object->Next;
object->Next = 0;
} else {
bool found = false;
while (optr != NULL) {
if (optr->Next == object) {
optr->Next = object->Next;
object->Next = 0;
found = true;
break;
}
optr = optr->Next;
}
// assert(found);
}
Map.Radar_Pixel(Cell_Number());
/*
** Special occupy bit clear.
*/
switch (object->What_Am_I()) {
case RTTI_BUILDING:
Flag.Occupy.Building = false;
break;
case RTTI_VESSEL:
case RTTI_AIRCRAFT:
case RTTI_UNIT:
Flag.Occupy.Vehicle = false;
break;
case RTTI_TERRAIN:
Flag.Occupy.Monolith = false;
break;
default:
break;
}
}
/***********************************************************************************************
* CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla*
* *
* Most game objects can often have their graphic imagery spill into more than one cell *
* even though they are considered to "occupy" only one cell. All cells overlapped are *
* flagged by this routine. Using this information it is possible to keep the tactical map *
* display correct. *
* *
* INPUT: object -- The object to mark as overlapping this cell. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
* 07/04/1995 JLB : Ensures that buildings are always marked down. *
*=============================================================================================*/
void CellClass::Overlap_Down(ObjectClass * object)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
assert(object != NULL && object->IsActive);
ObjectClass ** ptr = 0;
if (!object) return;
int index;
for (index = 0; index < ARRAY_SIZE(Overlapper); index++) {
if (Overlapper[index] == object) return;
if (!Overlapper[index]) ptr = &Overlapper[index];
}
/*
** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody
** else out in this case.
*/
if (!ptr && object->What_Am_I() == RTTI_BUILDING) {
for (index = 0; index < ARRAY_SIZE(Overlapper); index++) {
switch (Overlapper[index]->What_Am_I()) {
case RTTI_BUILDING:
case RTTI_TERRAIN:
break;
default:
Overlapper[index] = object;
index = ARRAY_SIZE(Overlapper);
break;
}
}
}
if (ptr) *ptr = object;
/*
** If being placed down on a visible square, then flag this
** techno object as being revealed to the player.
*/
if (IsMapped) {
object->Revealed(PlayerPtr);
}
}
/***********************************************************************************************
* CellClass::Overlap_Up -- Removes overlap flag for the cell. *
* *
* This is the counterpart to Overlap_Down and is used to remove the overlap flag for the *
* specified unit on the cell. *
* *
* INPUT: object -- The object to remove the overlap flag for. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
*=============================================================================================*/
void CellClass::Overlap_Up(ObjectClass * object)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
assert(object != NULL && object->IsActive);
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
if (Overlapper[index] == object) {
Overlapper[index] = 0;
break;
}
}
}
/***********************************************************************************************
* CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. *
* *
* This routine will determine if a unit is occupying the cell and if so, return a pointer *
* to it. If there is no unit occupying the cell, then NULL is returned. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with pointer to unit occupying cell, else NULL. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
*=============================================================================================*/
UnitClass * CellClass::Cell_Unit(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
return((UnitClass*)Cell_Find_Object(RTTI_UNIT));
}
/***********************************************************************************************
* CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. *
* *
* Call this routine to query and return a pointer to a vessel located in the cell. If *
* there is no vessel present, then this routine will return NULL. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with a pointer to the vessel class object if one is present. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/20/1996 JLB : Created. *
*=============================================================================================*/
VesselClass * CellClass::Cell_Vessel(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
return((VesselClass*)Cell_Find_Object(RTTI_VESSEL));
}
/***********************************************************************************************
* CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. *
* *
* This routine examines the cell and returns a pointer to the first infantry unit *
* that occupies it. If there is no infantry unit in the cell, then NULL is returned. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are *
* present. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/21/1994 JLB : Created. *
*=============================================================================================*/
InfantryClass * CellClass::Cell_Infantry(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY));
}
#ifdef SORTDRAW
static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy)
{
int & px = WindowList[WINDOW_PARTIAL][WINDOWX];
int & py = WindowList[WINDOW_PARTIAL][WINDOWY];
int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH];
int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT];
int & tx = WindowList[WINDOW_TACTICAL][WINDOWX];
int & ty = WindowList[WINDOW_TACTICAL][WINDOWY];
int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH];
int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT];
px = cellx + tx;
py = celly + ty;
pw = CELL_PIXEL_W;
ph = CELL_PIXEL_H;
if (px < tx) {
pw -= tx - px;
px = tx;
}
if (pw < 1) return(false);
if (py < ty) {
ph -= ty - py;
py = ty;
}
if (ph < 1) return(false);
if (px + pw > tx + tw) {
pw -= (px + pw) - (tx + tw);
}
if (pw < 1) return(false);
if (py + ph > ty + th) {
ph -= (py + ph) - (ty + th);
}
if (ph < 1) return(false);
drawx = drawx - (px-tx);
drawy = drawy - (py-ty);
return(true);
}
static int _ocompare(const void * left, const void * right)
{
COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y();
COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y();
if (lcoord < rcoord) return(-1);
if (lcoord > rcoord) return(1);
return(0);
}
#endif
/***********************************************************************************************
* CellClass::Get_Template_Info -- Get some info about a template for external use *
* *
* *
* *
* *
* INPUT: Ref to info required *
* *
* OUTPUT: True if image info available *
* *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 1/10/2019 5:57PM ST : Created. *
*=============================================================================================*/
bool CellClass::Get_Template_Info(char *template_name, int &icon, void *&image_data)
{
TemplateTypeClass const *ttype = NULL;
if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { // Not sure why it's checking for 255 here since that's a valid tile type. ST - 6/4/2019
ttype = &TemplateTypeClass::As_Reference(TType);
icon = TIcon;
}
else {
ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1);
icon = Clear_Icon();
}
if (ttype) {
strcpy(template_name, ttype->IniName);
image_data = (void*)ttype->ImageData;
return true;
}
return false;
}
/***********************************************************************************************
* CellClass::Draw_It -- Draws the cell imagery at the location specified. *
* *
* This is the gruntwork cell rendering code. It draws the cell at the screen location *
* specified. This routine doesn't draw any overlapping or occupying units. It only *
* deals with the ground (cell) layer -- icon level. *
* *
* INPUT: x,y -- The screen coordinates to render the cell imagery at. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
* 08/21/1994 JLB : Revised for simple template objects. *
* 11/01/1994 BRR : Updated placement cursor; draws actual object *
* 11/14/1994 BRR : Added remapping code to show passable areas *
* 12/02/1994 BRR : Added trigger display *
* 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. *
* 04/25/1995 JLB : Smudges drawn BELOW overlays. *
* 07/22/1996 JLB : Objects added to draw process. *
*=============================================================================================*/
void CellClass::Draw_It(int x, int y, bool objects) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (!objects) {
BStart(BENCH_CELL);
TemplateTypeClass const * ttype = 0;
int icon; // The icon number to use from the template set.
CELL cell = Cell_Number();
void * remap = NULL;
#ifdef SCENARIO_EDITOR
TemplateTypeClass * tptr;
// TriggerClass * trig;
int i;
char waypt[3];
#endif
CellCount++;
/*
** Fetch a pointer to the template type associated with this cell.
*/
if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) {
ttype = &TemplateTypeClass::As_Reference(TType);
icon = TIcon;
} else {
ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1);
icon = Clear_Icon();
}
#ifdef CHEAT_KEYS
/*
** Draw the stamp of the template.
*/
if (Debug_Icon) {
LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254));
FontXSpacing -= 2;
Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW,
Cell_Y(cell), Cell_X(cell),
//(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1,
Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER],
Overlay, OverlayData
);
FontXSpacing += 2;
} else {
#endif
#ifdef SCENARIO_EDITOR
/*
** Set up the remap table for this icon.
*/
if (Debug_Map && Debug_Passable) {
if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL &&
Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable
remap = DisplayClass::FadingRed;
} else {
if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable
remap = DisplayClass::FadingGreen;
} else {
remap = DisplayClass::FadingYellow; // moderately passable
}
}
}
#endif
/*
** This is the underlying terrain icon.
*/
if (ttype->Get_Image_Data()) {
LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL);
if (remap) {
LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap);
}
}
#ifdef SCENARIO_EDITOR
/*
** Draw the map editor's "current" cell. This is the cell that can be
** assigned attributes such as tag labels.
** This must be draw before the placement cursor, but after drawing the
** objects in the cell.
*/
if (Debug_Map && CurrentCell == Cell_Number()) {
LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW);
}
#endif
/*
** Redraw any smudge.
*/
if (Smudge != SMUDGE_NONE) {
SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData);
}
/*
** Draw the overlay object.
*/
if (Overlay != OVERLAY_NONE) {
OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay);
IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific
CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow);
IsTheaterShape = false;
}
#ifdef SCENARIO_EDITOR
if (Debug_Map) {
/*
** Draw the cell's Trigger mnemonic, if it has a trigger
*/
if (Trigger.Is_Valid()) {
Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
}
/*
** Draw the cell's Waypoint designation if there is one.
*/
if (IsWaypoint) {
for (i = 0; i < WAYPT_HOME; i++) {
if (Scen.Waypoint[i] == Cell_Number()) {
if (i < 26) {
waypt[0] = 'A' + i;
waypt[1] = 0;
} else {
waypt[0] = 'A' + (i/26)-1;
waypt[1] = 'A' + (i % 26);
waypt[2] = 0;
}
Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2,
Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3,
&ColorRemaps[PCOLOR_RED], TBLACK,
TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW);
break;
}
}
if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) {
Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7,
&ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
}
if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) {
Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7,
&ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW);
}
}
}
#endif
/*
** Draw the placement cursor:
** - First, draw the hash-mark cursor, so it will appear underneath
** any cursor being drawn
** - If the PendingObject is a template, overlay, or smudge, draw it
** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it
*/
if (IsCursorHere) {
SpeedType loco = SPEED_NONE;
if (Map.PendingObjectPtr) {
if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) {
BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr);
loco = obj->Class->Speed;
// if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD ||
// *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT;
}
}
/*
** Draw the hash-mark cursor:
*/
if (Map.ProximityCheck && Is_Clear_To_Build(loco)) {
LogicPage->Draw_Stamp(DisplayClass::TransIconset, 0, x, y, NULL, WINDOW_TACTICAL);
} else {
LogicPage->Draw_Stamp(DisplayClass::TransIconset, 2, x, y, NULL, WINDOW_TACTICAL);
}
#ifdef SCENARIO_EDITOR
if (Debug_Map && Map.PendingObject) {
switch (Map.PendingObject->What_Am_I()) {
/*
** Draw a template:
** - Compute the icon offset of this cell for this template, using
** ZoneCell+ZoneOffset to get the upper-left corner of the placement
** cursor
** - Draw the icon
*/
case RTTI_TEMPLATETYPE:
tptr = (TemplateTypeClass *)Map.PendingObject;
if (tptr->Get_Image_Data()) {
icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) +
(Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) *
tptr->Width;
LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL);
}
break;
/*
** Draw an overlay; just use the existing 'OverlayData' even though
** it means nothing.
*/
case RTTI_OVERLAYTYPE:
OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData);
break;
/*
** Draw a smudge
*/
case RTTI_SMUDGETYPE:
SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0);
break;
default:
break;
}
}
#endif
}
#ifdef CHEAT_KEYS
}
#endif
BEnd(BENCH_CELL);
}
#ifdef SORTDRAW
if (objects) {
BStart(BENCH_OBJECTS);
/*
** Build a list of objects to draw into a working buffer. There is a
** big presumption here -- it is presumed that if the cell is to be
** redrawn, then all objects in the cell should properly be flagged to
** be redrawn as well. Normally, this isn't a problem, but for subs
** the IsToDisplay flag MUST REMAIN SET. This is because there is a
** hack overpass after the cells are redrawn so that subs can be
** redrawn separately.
*/
static DynamicVectorClass<ObjectClass*> optr(20 + ARRAY_SIZE(Overlapper));
optr.Delete_All();
ObjectClass * object = Cell_Occupier();
while (object != NULL) {
if (!object->IsActive) break;
optr.Add(object);
object->IsToDisplay = true;
object = object->Next;
}
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
object = Overlapper[index];
if (object != NULL && object->IsActive) {
object->IsToDisplay = true;
optr.Add(object);
}
}
/*
** Sort the object list so that objects will be drawn from
** back to front.
*/
switch (optr.Count()) {
/*
** If there are zero or one object, then sorting is
** unnecessary.
*/
case 0:
case 1:
break;
/*
** Two objects can be sorted with a single compare and swap.
*/
case 2:
if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) {
swap(optr[0], optr[1]);
}
break;
/*
** Three objects can be sorted with three compares and swaps.
*/
case 3:
if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) {
swap(optr[0], optr[2]);
}
if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) {
swap(optr[0], optr[1]);
}
if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) {
swap(optr[1], optr[2]);
}
break;
/*
** Large number of objects can be effeciently sorted by using
** a quicksort.
*/
default:
qsort(&optr[0], optr.Count(), sizeof(ObjectClass*), _ocompare);
break;
}
/*
** Draw any objects that happen to be in or overlapping this cell.
*/
for (int index = 0; index < optr.Count(); index++) {
object = optr[index];
int xx,yy;
if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) {
if (_Calc_Partial_Window(x, y, xx, yy)) {
object->Draw_It(xx, yy, WINDOW_PARTIAL);
if (Debug_Map) {
object->IsToDisplay = true;
} else {
object->IsToDisplay = false;
}
}
object->IsToDisplay = false;
}
}
BEnd(BENCH_OBJECTS);
}
#endif
}
/***********************************************************************************************
* CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. *
* *
* This routine examines the cells around the current one and from this, determines what *
* concrete icon shape to use (if any). The cell data is adjusted and the cell is marked *
* for redraw if the icon changed. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/01/1994 JLB : Created. *
*=============================================================================================*/
void CellClass::Concrete_Calc(void)
{
#ifdef OBSOLETE
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW};
static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S};
FacingType * ptr; // Working pointer into adjacent cell list.
int index; // Constructed bit index.
int icon; // Icon number.
bool isodd; // Is this for the odd column?
#define OF_N 0x01
#define OF_NE 0x02
#define OF_E 0x04
#define OF_SE 0x08
#define OF_S 0x10
#define EF_N 0x01
#define EF_NW 0x10
#define EF_W 0x08
#define EF_SW 0x04
#define EF_S 0x02
/*
** Determine if the even or odd row logic is necessary.
*/
isodd = ((Cell_Number() & 0x01) != 0);
/*
** Fetch correct pointer depending on whether this is for an
** odd or even row.
*/
ptr = (isodd) ? _odd : _even;
/*
** Build an index according to the presence of concrete in the special
** adjacent cells. This is a short list of adjacent cell flags since
** only 5 adjacent cells need to be examined. The choice of which 5
** depends on whether this is for an even or odd column.
*/
index = 0;
for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) {
CellClass & cellptr = Adjacent_Cell(*ptr++);
if (cellptr.Overlay == OVERLAY_CONCRETE) {
index |= (1<<i);
}
}
/*
** Special logic occurs for cells that are concrete filled.
*/
if (Overlay == OVERLAY_CONCRETE) {
/*
** Process the index value and place the appropriate concrete icon
** in the cell.
*/
if (isodd) {
switch (index) {
case OF_NE:
case OF_N|OF_NE:
case OF_E|OF_N:
case OF_E|OF_NE:
case OF_N|OF_NE|OF_E:
case OF_S|OF_N|OF_NE:
icon = C_RIGHT_UP; // right - up
break;
case OF_SE:
case OF_E|OF_SE:
case OF_S|OF_SE:
case OF_S|OF_E:
case OF_S|OF_SE|OF_E:
case OF_S|OF_SE|OF_N:
icon = C_RIGHT_DOWN; // right - down
break;
case OF_SE|OF_NE:
case OF_SE|OF_NE|OF_N:
case OF_SE|OF_NE|OF_S:
case OF_SE|OF_NE|OF_S|OF_N:
case OF_SE|OF_E|OF_N:
case OF_SE|OF_E|OF_NE|OF_N:
case OF_S|OF_E|OF_N:
case OF_S|OF_E|OF_NE:
case OF_S|OF_E|OF_NE|OF_N:
case OF_S|OF_SE|OF_E|OF_N:
case OF_S|OF_SE|OF_E|OF_NE|OF_N:
case OF_S|OF_SE|OF_E|OF_NE:
icon = C_RIGHT_UPDOWN; // right - up - down
break;
default:
icon = C_RIGHT; // right
break;
}
} else {
switch (index) {
case EF_NW:
case EF_NW|EF_N:
case EF_W|EF_N:
case EF_NW|EF_W|EF_N:
case EF_NW|EF_W:
case EF_NW|EF_S|EF_N:
icon = C_LEFT_UP; // left - up
break;
case EF_SW:
case EF_SW|EF_S:
case EF_W|EF_S:
case EF_W|EF_SW|EF_S:
case EF_W|EF_SW:
case EF_SW|EF_S|EF_N:
icon = C_LEFT_DOWN; // left - down
break;
case EF_NW|EF_SW:
case EF_NW|EF_SW|EF_N:
case EF_NW|EF_SW|EF_S:
case EF_NW|EF_SW|EF_S|EF_N:
case EF_W|EF_S|EF_N:
case EF_W|EF_SW|EF_N:
case EF_W|EF_SW|EF_S|EF_N:
case EF_NW|EF_W|EF_S:
case EF_NW|EF_W|EF_S|EF_N:
case EF_NW|EF_W|EF_SW|EF_S|EF_N:
case EF_NW|EF_W|EF_SW|EF_N:
case EF_NW|EF_W|EF_SW|EF_S:
icon = C_LEFT_UPDOWN; // left - up - down
break;
default:
icon = C_LEFT; // left
break;
}
}
} else {
// Presume that no concrete piece is needed.
icon = C_NONE;
if (isodd) {
index &= ~(OF_NE|OF_SE); // Ignore diagonals.
switch (index) {
case OF_N|OF_E:
icon = C_UP_RIGHT; // up right
break;
case OF_E|OF_S:
icon = C_DOWN_RIGHT; // down right
break;
case OF_N|OF_E|OF_S:
icon = C_UPDOWN_RIGHT; // up/down right
break;
default:
break;
}
} else {
index &= ~(EF_NW|EF_SW); // Ignore diagonals.
switch (index) {
case EF_N|EF_W:
icon = C_UP_LEFT; // up left
break;
case EF_W|EF_S:
icon = C_DOWN_LEFT; // down left
break;
case EF_N|EF_W|EF_S:
icon = C_UPDOWN_LEFT; // up/down left
break;
default:
break;
}
}
/*
** If any kind of fixup piece is needed, then add concrete
** to this location RECURSIVELY!
*/
if (icon != C_NONE) {
OverlayTypeClass::As_Reference(OVERLAY_CONCRETE).Create_And_Place(Cell_Number());
icon = C_NONE;
}
}
/*
** Update the icon on the map.
*/
if (icon != C_NONE && OverlayData != icon) {
OverlayData = icon;
//Array[cell].Base = 0;
Redraw_Objects();
}
#endif
}
/***********************************************************************************************
* CellClass::Wall_Update -- Updates the imagery for wall objects in cell. *
* *
* This routine will examine the cell and the adjacent cells to determine what the wall *
* should look like with the cell. It will then update the wall's imagery value and flag *
* the cell to be redrawn if necessary. This routine should be called whenever the wall *
* or an adjacent wall is created or destroyed. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
* 09/19/1994 BWG : Updated to handle partially-damaged walls. *
*=============================================================================================*/
void CellClass::Wall_Update(void)
{
if (Overlay == OVERLAY_NONE) {
return;
}
OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay);
if (!wall.IsWall) {
return;
}
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
static FacingType _offsets[5] = {FACING_N, FACING_E, FACING_S, FACING_W, FACING_NONE};
for (unsigned index = 0; index < (sizeof(_offsets)/sizeof(_offsets[0])); index++) {
CellClass * newcell = Adjacent_Cell(_offsets[index]);
if (newcell && newcell->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(newcell->Overlay).IsWall) {
int icon = 0;
/*
** Build the icon number according to walls located in the adjacent
** cells.
*/
for (unsigned i = 0; i < 4; i++) {
CellClass * adjcell = newcell->Adjacent_Cell(_offsets[i]);
if (adjcell && adjcell->Overlay == newcell->Overlay) {
icon |= 1 << i;
}
}
newcell->OverlayData = (newcell->OverlayData & 0xFFF0) | icon;
/*
** Handle special cases for the incomplete damaged wall sets. If a damage stage
** is calculated, but there is no artwork for it, then consider the wall to be
** completely destroyed.
*/
if (newcell->Overlay == OVERLAY_BRICK_WALL && newcell->OverlayData == 48) {
newcell->Overlay = OVERLAY_NONE;
newcell->OverlayData = 0;
Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
}
if (newcell->Overlay == OVERLAY_SANDBAG_WALL && newcell->OverlayData == 16) {
newcell->Overlay = OVERLAY_NONE;
newcell->OverlayData = 0;
Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
}
if (newcell->Overlay == OVERLAY_CYCLONE_WALL && newcell->OverlayData == 32) {
newcell->Overlay = OVERLAY_NONE;
newcell->OverlayData = 0;
Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
}
if (newcell->Overlay == OVERLAY_FENCE && (newcell->OverlayData == 16 || newcell->OverlayData == 32)) {
newcell->Overlay = OVERLAY_NONE;
newcell->OverlayData = 0;
Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
}
if (newcell->Overlay == OVERLAY_BARBWIRE_WALL && newcell->OverlayData == 16) {
newcell->Overlay = OVERLAY_NONE;
newcell->OverlayData = 0;
Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
}
newcell->Recalc_Attributes();
newcell->Redraw_Objects();
}
}
}
/***********************************************************************************************
* CellClass::Cell_Coord -- Returns the coordinate of this cell. *
* *
* This support function will determine the coordinate of this cell and return it. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with coordinate value of cell. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
COORDINATE CellClass::Cell_Coord(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
return(::Cell_Coord(Cell_Number()));
}
/***********************************************************************************************
* CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. *
* *
* This routine will lower the tiberium level in the cell. It is used by the harvesting *
* process as well as by combat damage to the tiberium fields. *
* *
* INPUT: levels -- The number of levels to reduce the tiberium. *
* *
* OUTPUT: bool; Was the tiberium level reduced by at least one level? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
int CellClass::Reduce_Tiberium(int levels)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
int reducer = 0;
if (levels > 0 && Land == LAND_TIBERIUM) {
if (OverlayData+1 > levels) {
OverlayData -= levels;
reducer = levels;
} else {
Overlay = OVERLAY_NONE;
reducer = OverlayData;
OverlayData = 0;
Recalc_Attributes();
}
}
return(reducer);
}
/***********************************************************************************************
* CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. *
* *
* This routine will change the wall shape used for a wall if it's damaged. *
* *
* INPUT: damage -- The number of damage points the wall was hit with. If this value is *
* -1, then the entire wall at this cell will be destroyed. *
* *
* OUTPUT: bool; Was the wall destroyed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/15/1995 BWG : Created. *
* 03/19/1995 JLB : Updates cell information if wall was destroyed. *
* 10/06/1996 JLB : Updates zone as necessary. *
*=============================================================================================*/
int CellClass::Reduce_Wall(int damage)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (Overlay != OVERLAY_NONE) {
bool destroyed = false;
OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay);
if (wall.IsWall) {
/*
** If the damage was great enough to ensure wall destruction, reduce the wall by one
** level (no more). Otherwise determine wall reduction based on a percentage chance
** proportional to the damage received and the wall's strength.
*/
if (damage == -1 || damage >= wall.DamagePoints) {
destroyed = true;
} else {
destroyed = Random_Pick(0, wall.DamagePoints) < damage;
}
/*
** If the wall is destroyed, destroy it and check for any adjustments to
** adjacent walls.
*/
if (destroyed) {
OverlayData+=16;
if (damage == -1 ||
(OverlayData>>4) >= wall.DamageLevels ||
((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) {
Owner = HOUSE_NONE;
Overlay = OVERLAY_NONE;
OverlayData = 0;
Recalc_Attributes();
Redraw_Objects();
CellClass * ncell = Adjacent_Cell(FACING_N);
if (ncell) ncell->Wall_Update();
CellClass * wcell = Adjacent_Cell(FACING_W);
if (wcell) wcell->Wall_Update();
CellClass * scell = Adjacent_Cell(FACING_S);
if (scell) scell->Wall_Update();
CellClass * ecell = Adjacent_Cell(FACING_E);
if (ecell) ecell->Wall_Update();
Detach_This_From_All(As_Target());
/*
** The zone calculation changes now for non-crushable zone sensitive
** travellers.
*/
if (wall.IsCrushable) {
Map.Zone_Reset(MZONEF_NORMAL);
} else {
Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL);
}
return(true);
}
}
}
}
return(false);
}
/***********************************************************************************************
* CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE *
* *
* INPUT: *
* coord COORDINATE to compute index for *
* *
* OUTPUT: *
* index into StoppingCoord that's closest to this coord *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1994 BR : Created. *
* 12/10/1994 JLB : Uses alternate sub-position algorithm. *
*=============================================================================================*/
int CellClass::Spot_Index(COORDINATE coord)
{
COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell.
/*
** If the coordinate is close enough to the center of the cell, then return
** the center position index.
*/
if (Distance(rel, (COORDINATE)0x00800080L) < 60) {
return(0);
}
/*
** Since the center cell position has been eliminated, a simple comparison
** as related to the center of the cell can be used to determine the sub
** position. Take advantage of the fact that the sub positions are organized
** from left to right, top to bottom.
*/
int index = 0;
if (Coord_X(rel) > 0x80) index |= 0x01;
if (Coord_Y(rel) > 0x80) index |= 0x02;
return(index+1);
}
/***********************************************************************************************
* CellClass::Closest_Free_Spot -- returns free spot closest to given coord *
* *
* Similar to the CellClass::Free_Spot; this routine finds the spot in *
* the cell closest to the given coordinate, and returns the COORDINATE of *
* that spot if it's available, NULL if it's not. *
* *
* INPUT: *
* coord coordinate to check (only sub cell position examined) *
* *
* any -- If only the closest spot is desired regardless of whether it is free or *
* not, then this parameter will be true. *
* *
* OUTPUT: *
* COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell *
* coordinate data portions of the coordinate passed in. Only the lower sub-cell *
* data is altered. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/08/1994 BR : Created. *
* 12/10/1994 JLB : Picks best of closest stopping positions. *
* 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. *
*=============================================================================================*/
COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
int spot_index = Spot_Index(coord);
/*
** This precalculated sequence table records the closest spots to any given spot. Sequential
** examination of these spots for availability ensures that the closest available one is
** discovered first.
*/
static unsigned char _sequence[5][4] = {
{1,2,3,4},
{0,2,3,4},
{0,1,4,3},
{0,1,4,2},
{0,2,3,1}
};
/*
** In the case of the center coordinate being requested, but is occupied, then all other
** sublocations are equidistant. Instead of picking a static sequence of examination, the
** order is mixed up by way of this table.
*/
static unsigned char _alternate[4][4] = {
{1,2,3,4},
{2,3,4,1},
{3,4,1,2},
{4,1,2,3},
};
coord = Coord_Whole(coord);
/*
** Cells occupied by buildings or vehicles don't have any free spots.
*/
if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) {
return(NULL);
}
/*
** If just the nearest position is desired regardless of whether occupied or not,
** then just return with the stopping coordinate value.
*/
if (any || Is_Spot_Free(spot_index)) {
return(Coord_Add(coord, StoppingCoordAbs[spot_index]));
}
/*
** Scan through all available sub-locations in the cell in order to determine
** the closest one to the coordinate requested. Use precalculated table so that
** when the first free position is found, bail.
*/
unsigned char * sequence;
if (spot_index == 0) {
sequence = &_alternate[Random_Pick(0, 3)][0];
} else {
sequence = &_sequence[spot_index][0];
}
for (int index = 0; index < 4; index++) {
int pos = *sequence++;
if (Is_Spot_Free(pos)) {
return(Coord_Add(coord, StoppingCoordAbs[pos]));
}
}
/*
** No free spot could be found so return a NULL coordinate.
*/
return(0x00000000L);
}
/***********************************************************************************************
* CellClass::Clear_Icon -- Calculates what the clear icon number should be. *
* *
* This support routine will determine what the clear icon number would be for the cell. *
* The icon number is determined by converting the cell number into an index into a *
* lookup table. This yields what appears to be a randomized map without the necessity of *
* generating and recording randomized map numbers. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with the icon number for clear terrain if it were displayed at the *
* cell. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/26/1994 JLB : Created. *
* 06/09/1995 JLB : Uses 16 entry scramble algorithm. *
*=============================================================================================*/
int CellClass::Clear_Icon(void) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
CELL cell = Cell_Number();
return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2));
// return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C));
}
/***********************************************************************************************
* CellClass::Incoming -- Causes objects in cell to "run for cover". *
* *
* This routine is called whenever a great, but slow moving, threat is presented to the *
* occupants of a cell. The occupants will, in most cases, stop what they are doing and *
* try to get out of the way. *
* *
* INPUT: threat -- The coordinate source of the threat. *
* *
* forced -- If this threat is so major that the occupants should stop what *
* they are doing, then this parameter should be set to true. *
* *
* nokidding -- Override the scatter to also affect human controlled objects. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 01/10/1995 JLB : Created. *
* 08/02/1996 JLB : Added the "nokidding" parameter. *
*=============================================================================================*/
void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
ObjectClass * object = NULL;
object = Cell_Occupier();
while (object != NULL) {
/*
** Special check to make sure that friendly units never scatter.
*/
if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) {
object->Scatter(threat, forced, nokidding);
}
object = object->Next;
}
}
/***********************************************************************************************
* CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. *
* *
* Use this routine to return a reference to the adjacent cell in the direction specified. *
* *
* INPUT: face -- The direction to use when determining the adjacent cell. *
* *
* OUTPUT: Returns with a reference to the adjacent cell. *
* *
* WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. *
* *
* HISTORY: *
* 03/19/1995 JLB : Created. *
*=============================================================================================*/
CellClass const * CellClass::Adjacent_Cell(FacingType face) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (face == FACING_NONE) {
return(this);
}
if ((unsigned)face >= FACING_COUNT) {
return(NULL);
}
CELL newcell = ::Adjacent_Cell(Cell_Number(), face);
if ((unsigned)newcell >= MAP_CELL_TOTAL) {
return(NULL);
}
return &Map[newcell];
}
/***************************************************************************
* CellClass::Adjust_Threat -- Allows adjustment of threat at cell level *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 04/24/1995 PWG : Created. *
*=========================================================================*/
void CellClass::Adjust_Threat(HousesType house, int threat_value)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
int region = Map.Cell_Region(Cell_Number());
for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) {
if (lp == house) continue;
HouseClass * house_ptr = HouseClass::As_Pointer(lp);
if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) {
house_ptr->Adjust_Threat(region, threat_value);
}
}
if (Debug_Threat) {
Map.Flag_To_Redraw(true);
}
}
/***********************************************************************************************
* CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. *
* *
* This routine will adjust the level of the Tiberium in the cell so that it will *
* smoothly blend with the adjacent Tiberium. This routine should only be called for *
* new Tiberium cells. Existing cells that contain Tiberium follow a different growth *
* pattern. *
* *
* INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay *
* used. *
* *
* OUTPUT: Returns with the added Tiberium value that is now available for harvesting. *
* *
* WARNINGS: The return value is only valid for the initial placement. Tiberium growth will *
* increase the net worth of the existing Tiberium. *
* *
* HISTORY: *
* 05/16/1995 JLB : Created. *
* 02/20/1996 JLB : Takes into account the ore type. *
*=============================================================================================*/
long CellClass::Tiberium_Adjust(bool pregame)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (Overlay != OVERLAY_NONE) {
if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) {
static int _adj[9] = {0,1,3,4,6,7,8,10,11};
static int _adjgem[9] = {0,0,0,1,1,1,2,2,2};
int count = 0;
/*
** Mixup the Tiberium overlays so that they don't look the same.
** Since the type of ore is known, also record the nominal
** value per step of that ore type.
*/
bool gems = false;
int value = 0;
if (pregame) {
switch (Overlay) {
case OVERLAY_GOLD1:
case OVERLAY_GOLD2:
case OVERLAY_GOLD3:
case OVERLAY_GOLD4:
value = Rule.GoldValue;
Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4);
break;
case OVERLAY_GEMS1:
case OVERLAY_GEMS2:
case OVERLAY_GEMS3:
case OVERLAY_GEMS4:
gems = true;
value = Rule.GemValue*4;
Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4);
break;
default:
break;
}
}
/*
** Add up all adjacent cells that contain tiberium.
** (Skip those cells which aren't on the map)
*/
for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) {
if ((unsigned)::Adjacent_Cell(Cell_Number(), face) >= MAP_CELL_TOTAL) continue;
CellClass * adj = Adjacent_Cell(face);
if (adj && adj->Overlay != OVERLAY_NONE &&
OverlayTypeClass::As_Reference(adj->Overlay).Land == LAND_TIBERIUM) {
count++;
}
}
if (gems) {
OverlayData = _adjgem[count];
OverlayData = min(OverlayData, 2);
} else {
OverlayData = _adj[count];
}
return((OverlayData+1) * value);
}
}
return(0);
}
extern bool MPSuperWeaponDisable;
/***********************************************************************************************
* CellClass::Goodie_Check -- Performs crate discovery logic. *
* *
* Call this routine whenever an object enters a cell. It will check for the existence *
* of a crate and generate any "goodie" it might contain. *
* *
* INPUT: object -- Pointer to the object that is triggering this crate. *
* *
* OUTPUT: Can the object continue to enter this cell? A false return value means that the *
* cell is now occupied and must not be entered. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/22/1995 JLB : Created. *
* 07/08/1995 JLB : Added a bunch of goodies to the crates. *
* 06/17/1996 JLB : Revamped for Red Alert *
*=============================================================================================*/
bool CellClass::Goodie_Check(FootClass * object)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) {
bool force_mcv = false;
int force_money = 0;
int damage;
COORDINATE coord;
/*
** Determine the total number of shares for all the crate powerups. This is used as
** the base pool to determine the odds from.
*/
int total_shares = 0;
for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) {
total_shares += CrateShares[index];
}
/*
** Pick a random crate powerup according to the shares allotted to each powerup.
** In solo play, the bonus item is dependant upon the rules control.
*/
CrateType powerup;
if (Session.Type == GAME_NORMAL) {
/*
** Solo play has money amount determined by rules.ini file.
*/
force_money = Rule.SoloCrateMoney;
if (Overlay == OVERLAY_STEEL_CRATE) {
powerup = Rule.SilverCrate;
}
if (Overlay == OVERLAY_WOOD_CRATE) {
powerup = Rule.WoodCrate;
}
if (Overlay == OVERLAY_WATER_CRATE) {
//Mono_Printf("%d-%s.\n", __LINE__, __FILE__);
powerup = Rule.WaterCrate;
}
} else {
int pick = Random_Pick(1, total_shares);
int share_count = 0;
for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) {
share_count += CrateShares[powerup];
if (pick <= share_count) break;
}
assert(powerup != CRATE_COUNT);
/*
** Depending on what was picked, there might be an alternate goodie if the selected
** goodie would have no effect.
*/
switch (powerup) {
case CRATE_UNIT:
if (object->House->CurUnits > 50) powerup = CRATE_MONEY;
break;
case CRATE_SQUAD:
if (object->House->CurInfantry > 100) powerup = CRATE_MONEY;
break;
case CRATE_DARKNESS:
if (object->House->IsGPSActive) powerup = CRATE_MONEY;
break;
case CRATE_ARMOR:
if (object->ArmorBias != 1) powerup = CRATE_MONEY;
break;
case CRATE_SPEED:
if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY;
break;
case CRATE_FIREPOWER:
if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY;
break;
case CRATE_REVEAL:
if (object->House->IsVisionary) {
if (object->House->IsGPSActive) {
powerup = CRATE_MONEY;
} else {
powerup = CRATE_DARKNESS;
}
}
break;
case CRATE_CLOAK:
if (object->IsCloakable) powerup = CRATE_MONEY;
break;
// case CRATE_HEAL_BASE:
// if (object->House->BScan == 0) powerup = CRATE_UNIT;
case CRATE_MONEY:
break;
case CRATE_ICBM:
case CRATE_PARA_BOMB:
case CRATE_SONAR:
if (Session.Type != GAME_NORMAL) {
if (MPSuperWeaponDisable) {
powerup = CRATE_MONEY;
}
}
break;
case CRATE_TIMEQUAKE:
/*
** For the time quake crate, scan through and count up all the
** units (and infantry and ships and aircraft) and if either
** side has very few, allow the time quake. Otherwise,
** change the crate to money or something. Only do this for
** multiplay - for solo play, they get what they get. First,
** check for time - the chance for getting a time quake crate
** should be very very low when they first start the mission,
** but as time goes on the chance goes up.
*/
if (Session.Type != GAME_NORMAL) {
int i,ucount;
int minunits = 1000;
bool found = false;
unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE);
if (minutes > 100) minutes = 100;
if (Random_Pick(0,100-(int)minutes) == 0) {
for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) {
ucount = 0;
HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1);
if (hptr != NULL && !hptr->IsDefeated) {
int j;
for( j=0; j < UNIT_COUNT; j++) {
ucount += hptr->QuantityU(j);
}
for( j=0; j < INFANTRY_COUNT; j++) {
ucount += hptr->QuantityI(j);
}
for( j=0; j < AIRCRAFT_COUNT; j++) {
ucount += hptr->QuantityA(j);
}
for( j=0; j < VESSEL_COUNT; j++) {
ucount += hptr->QuantityV(j);
}
int bcount = 0;
for( j=0; j < STRUCT_COUNT; j++) {
bcount += hptr->QuantityB(j);
}
ucount += bcount/2; // weight buildings less
minunits = min(minunits, ucount);
}
}
if (Random_Pick(0, minunits) == minunits) {
found = true;
}
}
if (!found) {
powerup = CRATE_MONEY;
}
}
break;
}
/*
** Possibly force it to be an MCV if there is
** sufficient money and no buildings left.
*/
if ( object->House->BScan == 0 &&
object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) &&
Session.Options.Bases &&
!(object->House->UScan & UNITF_MCV)) {
powerup = CRATE_UNIT;
force_mcv = true;
}
/*
** If the powerup is money but there is insufficient money to build a refinery but there is a construction
** yard available, then force the money to be enough to rebuild the refinery.
*/
if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST &&
object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) {
force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias;
}
/*
** Special override for water crates so that illegal goodies items
** won't appear.
*/
if (Overlay == OVERLAY_WATER_CRATE) {
switch (powerup) {
case CRATE_UNIT:
case CRATE_SQUAD:
powerup = CRATE_MONEY;
break;
default:
break;
}
}
}
/*
** Keep track of the number of each type of crate found
*/
if (Session.Type == GAME_INTERNET) {
object->House->TotalCrates->Increment_Unit_Total(powerup);
}
/*
** Remove the crate from the map.
*/
Map.Remove_Crate(Cell_Number());
// Map[Cell_Number()].Overlay = OVERLAY_NONE;
if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) {
Map.Place_Random_Crate();
}
/*
** Generate any corresponding animation associated with this crate powerup.
*/
if (CrateAnims[powerup] != ANIM_NONE) {
new AnimClass(CrateAnims[powerup], Cell_Coord());
}
/*
** Create the effect requested.
*/
bool tospeak = false;
switch (powerup) {
case CRATE_TIMEQUAKE:
TimeQuake = true;
break;
/*
** Give the player money.
*/
case CRATE_MONEY:
crate_money:
if (force_money > 0) {
object->House->Refund_Money(force_money);
} else {
object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900));
}
break;
/*
** Shroud the world in blackness.
*/
case CRATE_DARKNESS:
/*
** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM
*/
if (object->House->IsHuman) {
Map.Shroud_The_Map(object->House);
}
break;
/*
** Reveal the entire map.
*/
case CRATE_REVEAL:
/*
** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM
*/
object->House->IsVisionary = true;
if (object->House->IsHuman) {
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
Map.Map_Cell(cell, object->House);
}
Map.Flag_To_Redraw(true);
}
break;
/*
** Try to create a unit where the crate was.
*/
case CRATE_UNIT: {
UnitTypeClass const * utp = NULL;
/*
** Give the player an MCV if he has no base left but does have more than enough
** money to rebuild a new base. Of course, if he already has an MCV, then don't
** give him another one.
*/
if (force_mcv) {
utp = &UnitTypeClass::As_Reference(UNIT_MCV);
}
/*
** If the player has a base and a refinery, but no harvester, then give him
** a free one.
*/
if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) {
utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER);
}
/*
** Check for special unit type override value.
*/
if (Rule.UnitCrateType != UNIT_NONE) {
utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType);
}
/*
** If no unit type has been determined, then pick one at random.
*/
while (utp == NULL) {
#ifdef FIXIT_ANTS
#ifdef FIXIT_CSII // checked - ajw 9/28/98
UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3));
#else
UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3));
#endif
#else
UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1));
#endif
if (utype != UNIT_MCV || Session.Options.Bases) {
utp = &UnitTypeClass::As_Reference(utype);
if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) {
break;
}
utp = NULL;
}
}
if (utp != NULL) {
UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House);
if (goodie_unit != NULL) {
if (goodie_unit->Unlimbo(Cell_Coord())) {
return(false);
}
/*
** Try to place the object into a nearby cell if something is preventing
** placement at the crate location.
*/
CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed);
if (goodie_unit->Unlimbo(::Cell_Coord(cell))) {
return(false);
}
delete goodie_unit;
goto crate_money;
}
}
}
break;
/*
** Create a squad of miscellaneous composition.
*/
case CRATE_SQUAD:
for (int index = 0; index < 5; index++) {
static InfantryType _inf[] = {
INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,
INFANTRY_E2,
INFANTRY_E3,
INFANTRY_RENOVATOR
};
if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) {
if (index == 0) {
goto crate_money;
}
}
}
return(false);
/*
** A one para-bomb mission.
*/
case CRATE_PARA_BOMB:
if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) {
// Changes for client/server multiplayer. ST - 8/2/2019 2:56PM
if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
if (object->House->IsHuman) {
Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, object->House);
}
} else {
if (object->IsOwnedByPlayer) {
Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB);
Map.Column[1].Flag_To_Redraw();
}
}
}
break;
/*
** A one time sonar pulse
*/
case CRATE_SONAR:
if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) {
// Changes for client/server multiplayer. ST - 8/2/2019 2:56PM
if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
if (object->House->IsHuman) {
Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, object->House);
}
} else {
if (object->IsOwnedByPlayer) {
Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE);
Map.Column[1].Flag_To_Redraw();
}
}
}
break;
/*
** A group of explosions are triggered around the crate.
*/
case CRATE_EXPLOSION:
if (object != NULL) {
int d = CrateData[powerup];
object->Take_Damage(d, 0, WARHEAD_HE, 0, true);
}
for (int index = 0; index < 5; index++) {
COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200));
new AnimClass(ANIM_FBALL1, frag_coord);
damage = CrateData[powerup];
Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE);
}
break;
/*
** A napalm blast is triggered.
*/
case CRATE_NAPALM:
coord = Coord_Mid(Cell_Coord(), object->Center_Coord());
new AnimClass(ANIM_NAPALM3, coord);
if (object != NULL) {
int d = CrateData[powerup];
object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true);
}
damage = CrateData[powerup];
Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE);
break;
/*
** All objects within a certain range will gain the ability to cloak.
*/
case CRATE_CLOAK:
for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) {
((TechnoClass *)obj)->IsCloakable = true;
}
}
break;
/*
** All of the player's objects heal up.
*/
case CRATE_HEAL_BASE:
if (object->IsOwnedByPlayer) {
Sound_Effect(VOC_HEAL, object->Center_Coord());
}
for (int index = 0; index < Logic.Count(); index++) {
ObjectClass * obj = Logic[index];
if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) {
obj->Strength = obj->Class_Of().MaxStrength;
}
}
break;
case CRATE_ICBM:
if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) {
// Changes for client/server multiplayer. ST - 8/2/2019 2:56PM
if (Session.Type == GAME_GLYPHX_MULTIPLAYER) {
if (object->House->IsHuman) {
Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, object->House);
}
} else {
if (object->IsOwnedByPlayer) {
Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB);
Map.Column[1].Flag_To_Redraw();
}
}
}
break;
case CRATE_ARMOR:
for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) {
fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256));
((TechnoClass *)obj)->ArmorBias = val;
if (obj->Owner() == PlayerPtr->Class->House) tospeak = true;
}
}
if (tospeak) Speak(VOX_UPGRADE_ARMOR);
break;
case CRATE_SPEED:
for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) {
FootClass * foot = (FootClass *)obj;
fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256);
foot->SpeedBias = val;
if (foot->IsOwnedByPlayer) tospeak = true;
}
}
if (tospeak) Speak(VOX_UPGRADE_SPEED);
break;
case CRATE_FIREPOWER:
for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) {
fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256);
((TechnoClass *)obj)->FirepowerBias = val;
if (obj->Owner() == PlayerPtr->Class->House) tospeak = true;
}
}
if (tospeak) Speak(VOX_UPGRADE_FIREPOWER);
break;
case CRATE_INVULN:
for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) {
ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index];
if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) {
((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256));
obj->Mark(MARK_CHANGE);
}
}
break;
/*
** A chronal vortex appears targetted at the triggering object.
*/
case CRATE_VORTEX:
if ( !ChronalVortex.Is_Active()) {
ChronalVortex.Appear ( Cell_Coord() );
ChronalVortex.Set_Target ( (ObjectClass*) object );
Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord());
}
break;
default:
break;
}
}
return(true);
}
/***********************************************************************************************
* CellClass::Flag_Place -- Places a house flag down on the cell. *
* *
* This routine will place the house flag at this cell location. *
* *
* INPUT: house -- The house that is having its flag placed here. *
* *
* OUTPUT: Was the flag successfully placed here? *
* *
* WARNINGS: Failure to place means that the cell is impassable for some reason. *
* *
* HISTORY: *
* 05/23/1995 JLB : Created. *
*=============================================================================================*/
bool CellClass::Flag_Place(HousesType house)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) {
IsFlagged = true;
Owner = house;
Flag_Update();
Redraw_Objects();
return(true);
}
return(false);
}
/***********************************************************************************************
* CellClass::Flag_Remove -- Removes the house flag from the cell. *
* *
* This routine will free the cell of any house flag that may be located there. *
* *
* INPUT: none *
* *
* OUTPUT: Was there a flag here that was removed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/23/1995 JLB : Created. *
*=============================================================================================*/
bool CellClass::Flag_Remove(void)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
if (IsFlagged) {
IsFlagged = false;
Owner = HOUSE_NONE;
Flag_Update();
Redraw_Objects();
return(true);
}
return(false);
}
void CellClass::Flag_Update(void)
{
if (IsFlagged && !CTFFlag) {
Flag_Create();
} else if (!IsFlagged && CTFFlag) {
Flag_Destroy();
}
}
void CellClass::Flag_Create(void)
{
if (!CTFFlag) {
CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord());
if (CTFFlag == NULL) {
for (int i = 0; i < Anims.Count(); ++i) {
AnimClass* anim = Anims.Ptr(i);
if (*anim != ANIM_FLAG) {
delete anim;
break;
}
}
CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord());
}
assert(CTFFlag != NULL);
CTFFlag->Set_Owner(Owner);
}
}
void CellClass::Flag_Destroy(void)
{
delete CTFFlag;
CTFFlag = NULL;
}
/***********************************************************************************************
* CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
* *
* This routine is called when some event would cause a momentary disruption in the *
* cloaking device. All objects that are cloaked in the cell will have their cloaking *
* device shimmer. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/29/1995 JLB : Created. *
*=============================================================================================*/
void CellClass::Shimmer(void)
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
ObjectClass * object = Cell_Occupier();
while (object) {
object->Do_Shimmer();
object = object->Next;
}
}
/***********************************************************************************************
* CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel *
* *
* This routine is called when determining general passability for purposes of zone *
* calculation. Only blockages that cannot be circumvented are considered to make a cell *
* impassable. All other obstructions can either be destroyed or are temporary. *
* *
* INPUT: loco -- The locomotion type to use when determining passablility. *
* *
* ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? *
* *
* ignorevehicles -- If vehicles should be ignored, then this flag will be true. *
* *
* zone -- If specified, the zone must match this value or else movement is *
* presumed disallowed. *
* *
* check -- This specifies the zone type that this check applies to. *
* *
* OUTPUT: Is the cell generally passable to ground targeting? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/25/1995 JLB : Created. *
* 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. *
* 10/05/1996 JLB : Allows checking for crushable blockages. *
*=============================================================================================*/
bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const
{
assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL);
/*
** Flying objects always consider every cell passable since they can fly over everything.
*/
if (loco == SPEED_WINGED) {
return(true);
}
/*
** If a zone was specified, then see if the cell is in a legal
** zone to allow movement.
*/
if (zone != -1) {
if (zone != Zones[check]) {
return(false);
}
}
/*
** Check the occupy bits for passable legality. If ignore infantry is true, then
** don't consider infnatry.
*/
int composite = Flag.Composite;
if (ignoreinfantry) {
composite &= 0xE0; // Drop the infantry occupation bits.
}
if (ignorevehicles) {
composite &= 0x5F; // Drop the vehicle/building bit.
}
if (composite != 0) {
return(false);
}
/*
** Fetch the land type of the cell -- to be modified and used later.
*/
LandType land = Land_Type();
/*
** Walls are always considered to block the terrain for general passability
** purposes unless this is a wall crushing check or if the checking object
** can destroy walls.
*/
OverlayTypeClass const * overlay = NULL;
if (Overlay != OVERLAY_NONE) {
overlay = &OverlayTypeClass::As_Reference(Overlay);
}
if (overlay != NULL && overlay->IsWall) {
if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) {
return(false);
}
/*
** Crushing objects consider crushable walls as clear rather than the
** typical LAND_WALL setting.
*/
land = LAND_CLEAR;
}
/*
** See if the ground type is impassable to this locomotion type and if
** so, return the error condition.
*/
if (::Ground[land].Cost[loco] == 0) {
return(false);
}
/*
** All checks passed, so this cell must be passable.
*/
return(true);
}
/***********************************************************************************************
* CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. *
* *
* This routine will examine this cell and if there is a bridge here, it will return *
* true. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Is there a bridge located in this cell? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/30/1996 JLB : Created. *
*=============================================================================================*/
bool CellClass::Is_Bridge_Here(void) const
{
switch (TType) {
case TEMPLATE_BRIDGE1:
case TEMPLATE_BRIDGE1H:
case TEMPLATE_BRIDGE1D:
case TEMPLATE_BRIDGE2:
case TEMPLATE_BRIDGE2H:
case TEMPLATE_BRIDGE2D:
case TEMPLATE_BRIDGE_1A:
case TEMPLATE_BRIDGE_1B:
case TEMPLATE_BRIDGE_2A:
case TEMPLATE_BRIDGE_2B:
case TEMPLATE_BRIDGE_3A:
case TEMPLATE_BRIDGE_3B:
case TEMPLATE_BRIDGE_3C:
case TEMPLATE_BRIDGE_3D:
case TEMPLATE_BRIDGE_3E:
case TEMPLATE_BRIDGE_3F:
return(true);
}
return(false);
}
/***********************************************************************************************
* CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. *
* *
* This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can *
* grow only if there is Tiberium already present. It can only grow to a certain level *
* and then all further growth is suspended. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Can Tiberium grow in this cell? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/14/1996 JLB : Created. *
*=============================================================================================*/
bool CellClass::Can_Tiberium_Grow(void) const
{
if (!Rule.IsTGrowth) return(false);
if (Session.Type != GAME_NORMAL) {
if(!Session.Options.Tiberium) return(false);
}
if (Land_Type() != LAND_TIBERIUM) return(false);
if (OverlayData >= 11) return(false);
if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false);
return(true);
}
/***********************************************************************************************
* CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. *
* *
* This routine will examine the cell and determine if there is sufficient Tiberium *
* present that Tiberium spores will spread to adjacent cells. If the Tiberium level is *
* too low, spreading will not occur. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? *
* *
* WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells *
* available to spread to. *
* *
* HISTORY: *
* 08/14/1996 JLB : Created. *
*=============================================================================================*/
bool CellClass::Can_Tiberium_Spread(void) const
{
if (!Rule.IsTSpread) return(false);
if (Session.Type != GAME_NORMAL) {
if(!Session.Options.Tiberium) return(false);
}
if (Land_Type() != LAND_TIBERIUM) return(false);
if (OverlayData <= 6) return(false);
if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false);
return(true);
}
/***********************************************************************************************
* CellClass::Grow_Tiberium -- Grows the tiberium in the cell. *
* *
* This routine will cause the tiberium to grow in the cell. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Did Tiberium grow in the cell? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/14/1996 JLB : Created. *
*=============================================================================================*/
bool CellClass::Grow_Tiberium(void)
{
if (Can_Tiberium_Grow()) {
OverlayData++;
Redraw_Objects();
return(true);
}
return(false);
}
/***********************************************************************************************
* CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. *
* *
* This routine will cause the Tiberium to spread from this cell into an adjacent (random) *
* cell. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Did the Tiberium spread? *
* *
* WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this *
* routine will fail. *
* *
* HISTORY: *
* 08/14/1996 JLB : Created. *
*=============================================================================================*/
bool CellClass::Spread_Tiberium(bool forced)
{
if (!forced) {
if (!Can_Tiberium_Spread()) return(false);
}
FacingType offset = Random_Pick(FACING_N, FACING_NW);
for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
CellClass * newcell = Adjacent_Cell(index+offset);
if (newcell != NULL && newcell->Can_Tiberium_Germinate()) {
new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number());
newcell->OverlayData = 0;
return(true);
}
}
return(false);
}
/***********************************************************************************************
* CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. *
* *
* This routine will examine the cell and determine if Tiberium can start growth in it. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Can Tiberium grow in this cell? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 08/14/1996 JLB : Created. *
*=============================================================================================*/
bool CellClass::Can_Tiberium_Germinate(void) const
{
if (!Map.In_Radar(Cell_Number())) return(false);
if (Is_Bridge_Here()) return(false);
/*
** Don't allow Tiberium to grow on a cell with a building unless that building is
** invisible. In such a case, the Tiberium must grow or else the location of the
** building will be revealed.
*/
BuildingClass const * building = Cell_Building();
if (building != NULL && !building->Class->IsInvisible) return(false);
if (!Ground[Land_Type()].Build) return(false);
if (Overlay != OVERLAY_NONE) return(false);
return(true);
}
/*
** Additions to CellClass to track visibility per-player. ST - 8/2/2019 2:59PM
**
**
**
**
**
*/
/***********************************************************************************************
* CellClass::Set_Mapped -- Set the cell mapped for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:09PM - ST *
*=============================================================================================*/
void CellClass::Set_Mapped(HousesType house, bool set)
{
int shift = (int) house;
if (set) {
IsMappedByPlayerMask |= (1 << shift);
} else {
IsMappedByPlayerMask &= ~(1 << shift);
}
}
/***********************************************************************************************
* CellClass::Set_Mapped -- Set the cell mapped for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:09PM - ST *
*=============================================================================================*/
void CellClass::Set_Mapped(HouseClass *player, bool set)
{
if (player && player->Class) {
Set_Mapped(player->Class->House, set);
if (Session.Type == GAME_NORMAL && player->IsHuman) {
IsMapped = set; // Also set the regular flag in single player
}
}
}
/***********************************************************************************************
* CellClass::Is_Mapped -- Is the cell mapped for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:13PM - ST *
*=============================================================================================*/
bool CellClass::Is_Mapped(HousesType house) const
{
int shift = (int) house;
return (IsMappedByPlayerMask & (1 << shift)) ? true : false;
}
/***********************************************************************************************
* CellClass::Is_Mapped -- Is the cell mapped for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:13PM - ST *
*=============================================================================================*/
bool CellClass::Is_Mapped(HouseClass *player) const
{
if (player && player->Class) {
return Is_Mapped(player->Class->House);
}
return false;
}
/***********************************************************************************************
* CellClass::Set_Visible -- Set the cell visible for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:16PM - ST *
*=============================================================================================*/
void CellClass::Set_Visible(HousesType house, bool set)
{
int shift = (int) house;
if (set) {
IsVisibleByPlayerMask |= (1 << shift);
} else {
IsVisibleByPlayerMask &= ~(1 << shift);
}
}
/***********************************************************************************************
* CellClass::Set_Visible -- Set the cell visible for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:16PM - ST *
*=============================================================================================*/
void CellClass::Set_Visible(HouseClass *player, bool set)
{
if (player && player->Class) {
Set_Visible(player->Class->House, set);
if (Session.Type == GAME_NORMAL && player->IsHuman) {
IsVisible = set; // Also set the regular flag in single player. This is needed for rendering
}
}
}
/***********************************************************************************************
* CellClass::Is_Visible -- Is the cell visible for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:16PM - ST *
*=============================================================================================*/
bool CellClass::Is_Visible(HousesType house) const
{
int shift = (int) house;
return (IsVisibleByPlayerMask & (1 << shift)) ? true : false;
}
/***********************************************************************************************
* CellClass::Is_Visible -- Is the cell visible for the given player *
* *
* *
* HISTORY: *
* 3/5/2019 3:16PM - ST *
*=============================================================================================*/
bool CellClass::Is_Visible(HouseClass *player) const
{
if (player && player->Class) {
return Is_Visible(player->Class->House);
}
return false;
}
bool CellClass::Is_Jamming(HousesType house) const
{
int shift = (int)house;
return (Jammed & (1 << shift)) ? true : false;
}
bool CellClass::Is_Jamming(HouseClass *player) const
{
if (player && player->Class) {
return Is_Jamming(player->Class->House);
}
return false;
}
void CellClass::Override_Land_Type(LandType type)
{
OverrideLand = type;
}