CnC_Remastered_Collection/REDALERT/IOMAP.CPP
PG-SteveT fc5cd5a775 Command & Conquer Remastered post-launch patch
Improvements to harvester resource finding logic.

Don't allow the Advanced Comm Center to be capturable in skirmish or multiplayer.

Increased failed pathfinding fudge factor.

Buildings accept the Guard command if they can attack.

Don't allow force capturing of ally structures.

Fixes for laser Orcas in S3cr3t M1ss10n. Properly restore them after save. Reset Orcas after loads.

Fixed flag animation rendering in CTF.

Potentially fix a crash if aircraft are destroyed outside the map bounds.

Fixed legacy Obelisk line rendering.

Fix out-of-bounds crash in TD; issue was already fixed in RA.

Disable capture flag on Commandos.

Drop the flag when entering the limbo state.

Fixed end game race condition, winning team ID is always sent before individual player win/lose messages.

Fixed Chan spawn on SCB10EA.

Don't show enter cursor for enemy units on refineries and repair pads.

Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold.

Don't debug reveal legacy rendering when a player is defeated.

Fixed crash when loading saves of custom campaign maps.

Reset harvester archived target when given a direct harvest order.

Prevent NOD cargo planes from being force attacked.

Fixed unit selection on load.

Migrated queued repair pad functionality from RA to TD.

Randomly animate infantry in area guard mode.

Fixed crash accessing inactive objects.

Added some walls in SCG08EB to prevent civilians from killing themselves.

TD + RA: Audio: Overiding "Our base is under attack" cooldown timing from legacy from 2 minutes to 30 seconds, so it will be heard 4x as often.

Fixed adjacent cell out-of-bounds crash issues.

Kill player on disconnect

Fixed and improved build time calculations to be consistent between TD and RA.

Don't show health bars for cloaked Stealth Tanks in the legacy renderer.

Fix selection of individual control groups after mixed selection.

More adjustments to SCG08EB; switch C7 to C5, and add civilian AI to avoid Tiberium.

Extra safety checks for units that have no weapons and aircraft that can't hunt.

Fix loading of multiple infantry onto an APC.

Additional safety checks for invalid coordinates.

Prevent units from being instantly repaired.

Fix map passability.

Fail Allied mission 5B if the spy re-boards the starting transport (matches 5A and 5C behavior).

Fixed multiplayer formation move causing units to move at light speed.

Ignore movement destination checks if a unit is part of a mission-driven team.

Fix buffer overrun crash.

Ignore mines when determining win conditions.

Fixed river passability in Blue Lakes.
2020-06-22 09:43:21 -07:00

528 lines
28 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/IOMAP.CPP 1 3/03/97 10:24a Joe_bostic $ */
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : IOMAP.CPP *
* *
* Programmer : Bill Randolph *
* *
* Start Date : January 16, 1995 *
* *
* Last Update : March 12, 1996 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* All map-related loading/saving routines should go in this module, so it can be overlayed. *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CellClass::Code_Pointers -- codes class's pointers for load/save *
* CellClass::Decode_Pointers -- decodes pointers for load/save *
* CellClass::Load -- Reads from a save game file. *
* CellClass::Save -- Write to a save game file. *
* CellClass::Should_Save -- Should the cell be written to disk? *
* DisplayClass::Code_Pointers -- codes class's pointers for load/save *
* DisplayClass::Decode_Pointers -- decodes pointers for load/save *
* MapClass::Code_Pointers -- codes class's pointers for load/save *
* MapClass::Decode_Pointers -- decodes pointers for load/save *
* MouseClass::Load -- Loads from a save game file. *
* MouseClass::Save -- Saves to a save game file. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
/***********************************************************************************************
* CellClass::Should_Save -- Should the cell be written to disk? *
* *
* This function will determine if the cell needs to be written to disk. Any cell that *
* contains special data should be written to disk. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Should this cell's data be written to disk? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
bool CellClass::Should_Save(void) const
{
static CellClass const _identity_cell;
return(memcmp(&_identity_cell, this, sizeof(*this)) != 0);
}
/***********************************************************************************************
* CellClass::Load -- Loads from a save game file. *
* *
* INPUT: file -- The file to read the cell's data from. *
* *
* OUTPUT: true = success, false = failure *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
bool CellClass::Load(Straw & file)
{
file.Get(this, sizeof(*this));
return(true);
}
/***********************************************************************************************
* CellClass::Save -- Write to a save game file. *
* *
* INPUT: file -- The file to write the cell's data to. *
* *
* OUTPUT: true = success, false = failure *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
*=============================================================================================*/
bool CellClass::Save(Pipe & file) const
{
file.Put(this, sizeof(*this));
return(true);
}
/***********************************************************************************************
* CellClass::Code_Pointers -- codes class's pointers for load/save *
* *
* This routine "codes" the pointers in the class by converting them to a number *
* that still represents the object pointed to, but isn't actually a pointer. This *
* allows a saved game to properly load without relying on the games data still *
* being in the exact same location. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/02/1995 BR : Created. *
*=============================================================================================*/
void CellClass::Code_Pointers(void)
{
if (Cell_Occupier() != NULL) {
OccupierPtr = (ObjectClass *)OccupierPtr->As_Target();
}
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
if (Overlapper[index] != NULL && Overlapper[index]->IsActive) {
Overlapper[index] = (ObjectClass *)Overlapper[index]->As_Target();
} else {
Overlapper[index] = NULL;
}
}
assert(CTFFlag == NULL);
}
/***********************************************************************************************
* CellClass::Decode_Pointers -- decodes pointers for load/save *
* *
* This routine "decodes" the pointers coded in Code_Pointers by converting the *
* code values back into object pointers. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/02/1995 BR : Created. *
* 03/12/1996 JLB : Simplified. *
*=============================================================================================*/
void CellClass::Decode_Pointers(void)
{
if (OccupierPtr != NULL) {
OccupierPtr = As_Object((TARGET)OccupierPtr, false);
assert(OccupierPtr != NULL);
}
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
if (Overlapper[index] != NULL) {
Overlapper[index] = As_Object((TARGET)Overlapper[index], false);
assert(Overlapper[index] != NULL);
}
}
CTFFlag = NULL;
}
/***********************************************************************************************
* MouseClass::Load -- Loads from a save game file. *
* *
* Loading the map is very complicated. Here are the steps: *
* - Read the Theater for this save-game *
* - call Init_Theater to perform theater-specific inits *
* - call Free_Cells to free the cell array, because loading the map object will overwrite *
* the pointer to the cell array *
* - read the map object from disk *
* - call Alloc_Cells to re-allocate the cell array *
* - call Init_Cells to set the cells to a known state, because not every cell will be loaded *
* - read the cell objects into the cell array *
* - After the map & all objects have been loaded & the pointers decoded, Init_IO() >MUST< be *
* called to restore the map's button list to the proper state. *
* *
* INPUT: file -- The file to read the cell's data from. *
* *
* OUTPUT: true = success, false = failure *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
* 03/12/1996 JLB : Simplified. *
*=============================================================================================*/
bool MouseClass::Load(Straw & file)
{
/*
** Load Theater: Even though this value is located in the DisplayClass,
** it must be loaded first so initialization can be done before any other
** map data is loaded. If initialization isn't done first, data read from
** disk will be over-written when initialization occurs. This code must
** go in the most-derived Map class.
*/
TheaterType theater;
if (file.Get(&theater, sizeof(theater)) != sizeof(theater)) {
return(false);
}
#ifdef WIN32
LastTheater = THEATER_NONE;
#endif
/*
** Remove any old theater specific uncompressed shapes
*/
#ifdef WIN32
// if (theater != LastTheater) {
Reset_Theater_Shapes();
// }
#endif //WIN32
/*
** Init display mixfiles
*/
Init_Theater(theater);
TerrainTypeClass::Init(Scen.Theater);
TemplateTypeClass::Init(Scen.Theater);
OverlayTypeClass::Init(Scen.Theater);
UnitTypeClass::Init(Scen.Theater);
InfantryTypeClass::Init(Scen.Theater);
BuildingTypeClass::Init(Scen.Theater);
BulletTypeClass::Init(Scen.Theater);
AnimTypeClass::Init(Scen.Theater);
AircraftTypeClass::Init(Scen.Theater);
VesselTypeClass::Init(Scen.Theater);
SmudgeTypeClass::Init(Scen.Theater);
//LastTheater = Scen.Theater;
/*
** Free the cell array, because we're about to overwrite its pointers
*/
Free_Cells();
/*
** Read the entire map object in. Only read in sizeof(MouseClass), so if we're
** in editor mode, none of the map editor object is read in.
*/
file.Get(this, sizeof(*this));
#ifdef SCENARIO_EDITOR
new(this) MapEditClass(NoInitClass());
#else
new(this) MouseClass(NoInitClass());
#endif
/*
** Reallocate the cell array
*/
Alloc_Cells();
/*
** Init all cells to empty
*/
Init_Cells();
/*
** Read # cells saved
*/
int count;
if (file.Get(&count, sizeof(count)) != sizeof(count)) {
return(false);
}
/*
** Read cells
*/
for (int index = 0; index < count; index++) {
CELL cell = 0;
if (file.Get(&cell, sizeof(cell)) != sizeof(cell)) {
return(false);
}
if (!(*this)[cell].Load(file)) {
return(false);
}
}
LastTheater = Scen.Theater;
return(true);
}
/***********************************************************************************************
* MouseClass::Save -- Save to a save game file. *
* *
* INPUT: file -- The file to write the cell's data to. *
* *
* OUTPUT: true = success, false = failure *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/19/1994 JLB : Created. *
* 02/26/1996 JLB : Cleaned up. *
*=============================================================================================*/
bool MouseClass::Save(Pipe & file) const
{
/*
** Save Theater >first<
*/
TheaterType theater = Scen.Theater;
file.Put(&theater, sizeof(theater));
file.Put(this, sizeof(*this));
/*
** Count how many cells will be saved.
*/
int count = 0;
CellClass const * cellptr = &(*this)[(CELL)0];
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
if (cellptr->Should_Save()) {
count++;
}
cellptr++;
}
/*
** write out count of the cells.
*/
file.Put(&count, sizeof(count));
/*
** Save cells that need it
*/
cellptr = &(*this)[(CELL)0];
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
if (cellptr->Should_Save()) {
file.Put(&cell, sizeof(cell));
cellptr->Save(file);
count--;
}
cellptr++;
}
if (count != 0) return(false);
return(true);
}
/***********************************************************************************************
* DisplayClass::Code_Pointers -- codes class's pointers for load/save *
* *
* This routine "codes" the pointers in the class by converting them to a number *
* that still represents the object pointed to, but isn't actually a pointer. This *
* allows a saved game to properly load without relying on the games data still *
* being in the exact same location. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/02/1995 BR : Created. *
*=============================================================================================*/
void DisplayClass::Code_Pointers(void)
{
/*
** Code PendingObjectPtr.
*/
if (PendingObjectPtr) {
PendingObjectPtr = (ObjectClass *)PendingObjectPtr->As_Target();
}
/*
** Fix for saving game while in structure placement mode. ST - 4/15/2020 2:41PM
*/
memset(CursorShapeSave, 0, sizeof(CursorShapeSave));
if (CursorSize && CursorSize != CursorShapeSave) {
int save_buffer_element_size = sizeof(CursorShapeSave) / sizeof(CursorShapeSave[0]);
int index = 0;
while (index < save_buffer_element_size - 2 && CursorSize[index] != REFRESH_EOL) {
CursorShapeSave[index] = CursorSize[index];
index++;
}
CursorShapeSave[index] = REFRESH_EOL;
}
/*
** Chain to parent.
*/
MapClass::Code_Pointers();
}
/***********************************************************************************************
* DisplayClass::Decode_Pointers -- decodes pointers for load/save *
* *
* This routine "decodes" the pointers coded in Code_Pointers by converting the *
* code values back into object pointers. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/02/1995 BR : Created. *
*=============================================================================================*/
void DisplayClass::Decode_Pointers(void)
{
/*
** Decode PendingObjectPtr. We can't decode PendingObject here, because we'd
** have to reference PendingObjectPtr->Class_Of(), and the object that
** PendingObjectPtr is pointing to hasn't been decoded yet. Since we can't
** decode PendingObjectPtr, we can't set the placement cursor shape here
** either. These have to be done as last-minute fixups.
*/
if (PendingObjectPtr) {
PendingObjectPtr = As_Object((TARGET)PendingObjectPtr, false);
assert(PendingObjectPtr != NULL);
}
if (CursorSize) {
CursorSize = CursorShapeSave;
}
/*
** Chain to parent.
*/
MapClass::Decode_Pointers();
}
/***********************************************************************************************
* MapClass::Code_Pointers -- codes class's pointers for load/save *
* *
* This routine "codes" the pointers in the class by converting them to a number *
* that still represents the object pointed to, but isn't actually a pointer. This *
* allows a saved game to properly load without relying on the games data still *
* being in the exact same location. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/02/1995 BR : Created. *
*=============================================================================================*/
void MapClass::Code_Pointers(void)
{
CELL cell;
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
(*this)[cell].Flag_Destroy();
}
CellClass * cellptr = &(*this)[(CELL)0];
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
cellptr->Code_Pointers();
cellptr++;
}
}
/***********************************************************************************************
* MapClass::Decode_Pointers -- decodes pointers for load/save *
* *
* This routine "decodes" the pointers coded in Code_Pointers by converting the *
* code values back into object pointers. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 01/02/1995 BR : Created. *
*=============================================================================================*/
void MapClass::Decode_Pointers(void)
{
CellClass * cellptr = &(*this)[(CELL)0];
for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
cellptr->Decode_Pointers();
cellptr++;
}
}