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.
This commit is contained in:
PG-SteveT 2020-06-22 09:43:21 -07:00
parent b190e877f7
commit fc5cd5a775
70 changed files with 1191 additions and 573 deletions

View file

@ -2112,6 +2112,31 @@ static AnimTypeClass const MineExp1(
ANIM_NONE
);
static AnimTypeClass const Flag(
ANIM_FLAG, // Animation number.
"FLAGFLY", // Data name of animation.
21, // Maximum dimension of animation.
0, // Biggest animation stage.
false, // Theater specific art imagery?
false, // Normalized animation rate?
false, // Uses white translucent table?
false, // Scorches the ground?
false, // Forms a crater?
false, // Sticks to unit in square?
false, // Ground level animation?
false, // Translucent colors in this animation?
false, // Is this a flame thrower animation?
0, // Damage to apply per tick (fixed point).
1, // Delay between frames.
0, // Starting frame number.
0, // Loop start frame number.
-1, // Ending frame of loop back.
-1, // Number of animation stages.
-1, // Number of times the animation loops.
VOC_NONE, // Sound effect to play.
ANIM_NONE
);
#ifdef FIXIT_ANTS
static AnimTypeClass const Ant1Death(
ANIM_ANT1_DEATH, // Animation number.
@ -2424,6 +2449,7 @@ void AnimTypeClass::Init_Heap(void)
new AnimTypeClass(CrateTQuake);
new AnimTypeClass(ParaBomb);
new AnimTypeClass(MineExp1);
new AnimTypeClass(Flag);
#ifdef FIXIT_ANTS
new AnimTypeClass(Ant1Death);
new AnimTypeClass(Ant2Death);

View file

@ -1621,23 +1621,28 @@ ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType wa
*/
switch (res) {
case RESULT_DESTROYED:
Kill_Cargo(source);
Death_Announcement();
new AnimClass(ANIM_FBALL1, Target_Coord());
{
Kill_Cargo(source);
Death_Announcement();
COORDINATE coord = Target_Coord();
if (!(coord & HIGH_COORD_MASK)) {
new AnimClass(ANIM_FBALL1, coord);
}
/*
** Parachute a survivor if possible.
*/
if (Class->IsCrew && Percent_Chance(90) && Map[Center_Coord()].Is_Clear_To_Move(SPEED_FOOT, true, false)) {
InfantryClass * infantry = new InfantryClass(INFANTRY_E1, House->Class->House);
if (infantry != NULL) {
if (!infantry->Paradrop(Center_Coord())) {
delete infantry;
/*
** Parachute a survivor if possible.
*/
if (Class->IsCrew && Percent_Chance(90) && Map[Center_Coord()].Is_Clear_To_Move(SPEED_FOOT, true, false)) {
InfantryClass * infantry = new InfantryClass(INFANTRY_E1, House->Class->House);
if (infantry != NULL) {
if (!infantry->Paradrop(Center_Coord())) {
delete infantry;
}
}
}
}
delete this;
delete this;
}
break;
default:

View file

@ -260,6 +260,24 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
void const * transtable = NULL;
int shapenum = Class->Start + Fetch_Stage();
void const * remap = NULL;
ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
bool alt = false;
/*
** Some animations require special fixups.
*/
switch (Class->Type) {
case ANIM_ATOM_BLAST:
transtable = Map.UnitShadow;
break;
case ANIM_FLAG:
x += (ICON_PIXEL_W / 2) - 2;
y += (3 * ICON_PIXEL_H / 4) - Get_Build_Frame_Height(shapefile);
transtable = Map.UnitShadow;
alt = true;
break;
}
/*
** If the translucent table hasn't been determined yet, then check to see if it
@ -267,13 +285,19 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
*/
if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable;
if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable;
if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow;
/*
** Set the shape flags to properly take into account any fading or ghosting
** table necessary.
*/
ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
if (alt) {
flags = flags | SHAPE_FADING;
if (OwnerHouse != HOUSE_NONE) {
remap = HouseClass::As_Pointer(OwnerHouse)->Remap_Table(false);
} else {
remap = ColorRemaps[PCOLOR_GOLD].RemapTable;
}
}
if (transtable != NULL) flags = flags | SHAPE_GHOST;
/*
@ -357,6 +381,7 @@ short const * AnimClass::Overlap_List(void) const
( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1,
REFRESH_EOL
};
static short const OverlapFlag[] = { 0, 1, -MAP_CELL_W, -(MAP_CELL_W-1), MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL };
if (IsToDelete) {
static short const _list[] = {REFRESH_EOL};
@ -367,6 +392,10 @@ short const * AnimClass::Overlap_List(void) const
return(OverlapAtom);
}
if (Class->Type == ANIM_FLAG) {
return(OverlapFlag);
}
#ifdef PARTIAL
IsTheaterShape = Class->IsTheater;
if (Class->Get_Image_Data() != NULL) {
@ -512,7 +541,7 @@ void AnimClass::operator delete(void * ptr)
* 05/31/1994 JLB : Created. *
* 08/03/1994 JLB : Added a delayed affect parameter. *
*=============================================================================================*/
AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) :
AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, char loop) :
ObjectClass(RTTI_ANIM, Anims.ID(this)),
Class(AnimTypes.Ptr((int)animnum)),
xObject(TARGET_NONE),
@ -559,8 +588,12 @@ IsTheaterShape = false;
Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false);
}
Loops = (unsigned char)(max(loop, 1) * Class->Loops);
Loops = (unsigned char)max(Loops, 1);
if (Class->Loops >= 0) {
Loops = (char)(max(loop, 1) * Class->Loops);
Loops = (char)max(Loops, 1);
} else {
Loops = Class->Loops;
}
/*
** If the animation starts immediately, then play the associated sound effect now.
@ -812,8 +845,8 @@ void AnimClass::AI(void)
** Determine if this animation should loop another time. If so, then start the loop
** but if not, then proceed into the animation termination handler.
*/
if (Loops) Loops--;
if (Loops) {
if (Loops > 0) Loops--;
if (Loops != 0) {
Set_Stage(Class->LoopStart);
} else {

View file

@ -51,7 +51,7 @@ class AnimClass : public ObjectClass, public StageClass {
public:
AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1);
AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, char loop=1);
AnimClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {};
virtual ~AnimClass(void);
@ -79,6 +79,7 @@ class AnimClass : public ObjectClass, public StageClass {
void Set_Visible_Flags(unsigned flags) { VisibleFlags = flags; }
unsigned Get_Visible_Flags() const { return (Delay == 0) ? VisibleFlags : 0; }
virtual HousesType Owner(void) const {return OwnerHouse;};
virtual bool Can_Place_Here(COORDINATE ) const {return true;}
virtual bool Mark(MarkType mark=MARK_CHANGE);
virtual bool Render(bool forced) const;
@ -121,7 +122,7 @@ class AnimClass : public ObjectClass, public StageClass {
** This counter tells how many more times the animation should loop before it
** terminates.
*/
unsigned char Loops;
char Loops;
protected:
void Middle(void);

View file

@ -358,6 +358,9 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT
*/
case RADIO_OVER_OUT:
Begin_Mode(BSTATE_IDLE);
if (*this == STRUCT_REPAIR) {
Assign_Mission(MISSION_GUARD);
}
TechnoClass::Receive_Message(from, message, param);
return(RADIO_ROGER);
@ -4811,8 +4814,8 @@ int BuildingClass::Mission_Unload(void)
** Scatter everything around the weapon's factory door.
*/
for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) {
CellClass * cptr = &cellptr->Adjacent_Cell(f);
if (cptr->Cell_Building() == NULL) {
CellClass * cptr = cellptr->Adjacent_Cell(f);
if (cptr && cptr->Cell_Building() == NULL) {
cptr->Incoming(coord, true, true);
}
}

View file

@ -125,7 +125,8 @@ CellClass::CellClass(void) :
Land(LAND_CLEAR),
OverrideLand(LAND_NONE),
IsMappedByPlayerMask(0),
IsVisibleByPlayerMask(0)
IsVisibleByPlayerMask(0),
CTFFlag(NULL)
{
for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) {
Zones[zone] = 0;
@ -212,7 +213,7 @@ TechnoClass * CellClass::Cell_Techno(int x, int y) const
if (Cell_Occupier()) {
object = Cell_Occupier();
while (object) {
while (object && object->IsActive) {
if (object->Is_Techno()) {
COORDINATE coord = Coord_Fraction(object->Center_Coord());
long dist = Distance(coord, click);
@ -248,7 +249,7 @@ ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const
ObjectClass * object = Cell_Occupier();
while (object != NULL) {
while (object != NULL && object->IsActive) {
if (object->What_Am_I() == rtti) {
return(object);
}
@ -1236,14 +1237,6 @@ void CellClass::Draw_It(int x, int y, bool objects) const
#endif
}
/*
** Draw the flag if there is one located at this cell.
*/
if (IsFlagged) {
void const * flag_remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, REMAP_NORMAL);
CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow);
}
#ifdef CHEAT_KEYS
}
#endif
@ -1263,22 +1256,20 @@ void CellClass::Draw_It(int x, int y, bool objects) const
** hack overpass after the cells are redrawn so that subs can be
** redrawn separately.
*/
ObjectClass * optr[20 + ARRAY_SIZE(Overlapper)];
int count = 0;
static DynamicVectorClass<ObjectClass*> optr(20 + ARRAY_SIZE(Overlapper));
optr.Delete_All();
ObjectClass * object = Cell_Occupier();
while (object != NULL) {
if (!object->IsActive) break;
optr[count] = object;
optr.Add(object);
object->IsToDisplay = true;
object = object->Next;
count++;
}
for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) {
object = Overlapper[index];
if (object != NULL && object->IsActive) {
object->IsToDisplay = true;
optr[count] = object;
count++;
optr.Add(object);
}
}
@ -1286,7 +1277,7 @@ void CellClass::Draw_It(int x, int y, bool objects) const
** Sort the object list so that objects will be drawn from
** back to front.
*/
switch (count) {
switch (optr.Count()) {
/*
** If there are zero or one object, then sorting is
@ -1325,14 +1316,14 @@ void CellClass::Draw_It(int x, int y, bool objects) const
** a quicksort.
*/
default:
qsort(optr, count, sizeof(optr[0]), _ocompare);
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 < count; index++) {
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)) {
@ -1608,9 +1599,9 @@ void CellClass::Wall_Update(void)
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]);
CellClass * newcell = Adjacent_Cell(_offsets[index]);
if (newcell.Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(newcell.Overlay).IsWall) {
if (newcell && newcell->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(newcell->Overlay).IsWall) {
int icon = 0;
/*
@ -1618,45 +1609,46 @@ void CellClass::Wall_Update(void)
** cells.
*/
for (unsigned i = 0; i < 4; i++) {
if (newcell.Adjacent_Cell(_offsets[i]).Overlay == newcell.Overlay) {
CellClass * adjcell = newcell->Adjacent_Cell(_offsets[i]);
if (adjcell && adjcell->Overlay == newcell->Overlay) {
icon |= 1 << i;
}
}
newcell.OverlayData = (newcell.OverlayData & 0xFFF0) | icon;
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_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_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_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_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);
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();
newcell->Recalc_Attributes();
newcell->Redraw_Objects();
}
}
}
@ -1773,10 +1765,14 @@ int CellClass::Reduce_Wall(int damage)
OverlayData = 0;
Recalc_Attributes();
Redraw_Objects();
Adjacent_Cell(FACING_N).Wall_Update();
Adjacent_Cell(FACING_W).Wall_Update();
Adjacent_Cell(FACING_S).Wall_Update();
Adjacent_Cell(FACING_E).Wall_Update();
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());
/*
@ -2022,27 +2018,24 @@ void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding)
* HISTORY: *
* 03/19/1995 JLB : Created. *
*=============================================================================================*/
CellClass const & CellClass::Adjacent_Cell(FacingType face) const
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(*this);
return(NULL);
}
//The top row doesn't have any adjacent cells to the north. - LLL
if (ID < MAP_CELL_W && (face == FACING_N || face == FACING_NE || face == FACING_NW)) {
return (*this);
CELL newcell = ::Adjacent_Cell(Cell_Number(), face);
if ((unsigned)newcell >= MAP_CELL_TOTAL) {
return(NULL);
}
//The bottom row doesn't have any adjacent cells to the south. - LLL
if ((ID > MAP_CELL_TOTAL - MAP_CELL_W) && (face == FACING_S || face == FACING_SE || face == FACING_SW)) {
return (*this);
}
CellClass const * ptr = this + AdjacentCell[face];
if ((unsigned)ptr->Cell_Number() > MAP_CELL_TOTAL) return(*this);
return(*ptr);
return &Map[newcell];
}
@ -2144,10 +2137,10 @@ long CellClass::Tiberium_Adjust(bool pregame)
*/
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);
CellClass * adj = Adjacent_Cell(face);
if (adj.Overlay != OVERLAY_NONE &&
OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) {
if (adj && adj->Overlay != OVERLAY_NONE &&
OverlayTypeClass::As_Reference(adj->Overlay).Land == LAND_TIBERIUM) {
count++;
}
}
@ -2763,6 +2756,7 @@ bool CellClass::Flag_Place(HousesType house)
if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) {
IsFlagged = true;
Owner = house;
Flag_Update();
Redraw_Objects();
return(true);
}
@ -2791,6 +2785,7 @@ bool CellClass::Flag_Remove(void)
if (IsFlagged) {
IsFlagged = false;
Owner = HOUSE_NONE;
Flag_Update();
Redraw_Objects();
return(true);
}
@ -2798,6 +2793,34 @@ bool CellClass::Flag_Remove(void)
}
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) {
CTFFlag->OwnerHouse = Owner;
}
}
}
void CellClass::Flag_Destroy(void)
{
delete CTFFlag;
CTFFlag = NULL;
}
/***********************************************************************************************
* CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
* *
@ -3088,7 +3111,7 @@ bool CellClass::Spread_Tiberium(bool forced)
}
FacingType offset = Random_Pick(FACING_N, FACING_NW);
for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
CellClass * newcell = &Adjacent_Cell(index+offset);
CellClass * newcell = Adjacent_Cell(index+offset);
if (newcell != NULL && newcell->Can_Tiberium_Germinate()) {
new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number());

View file

@ -231,8 +231,8 @@ class CellClass
COORDINATE Cell_Coord(void) const;
COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const;
COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());}
CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));}
CellClass const & Adjacent_Cell(FacingType face) const;
CellClass * Adjacent_Cell(FacingType face) {return (CellClass *)((*((CellClass const *)this)).Adjacent_Cell(face));}
CellClass const * Adjacent_Cell(FacingType face) const;
InfantryClass * Cell_Infantry(void) const;
LandType Land_Type(void) const {return((OverrideLand != LAND_NONE) ? OverrideLand : Land);}
ObjectClass * Cell_Find_Object(RTTIType rtti) const;
@ -263,6 +263,9 @@ class CellClass
void Overlap_Up(ObjectClass * object);
bool Flag_Place(HousesType house);
bool Flag_Remove(void);
void Flag_Update(void);
void Flag_Create(void);
void Flag_Destroy(void);
/*
** File I/O.
@ -323,10 +326,15 @@ class CellClass
LandType OverrideLand; // The overriden land type of this cell.
/*
** Points to the flag animation on this cell in CTF games.
*/
AnimClass* CTFFlag;
/*
** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load
*/
unsigned char SaveLoadPadding[32];
unsigned char SaveLoadPadding[28];
};
#endif

View file

@ -190,7 +190,8 @@ void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, Warh
** further than one cell away.
*/
if (i != FACING_NONE) {
cellptr = &Map[cell].Adjacent_Cell(i);
cellptr = Map[cell].Adjacent_Cell(i);
if (!cellptr) continue;
}
/*

View file

@ -3921,6 +3921,8 @@ void Handle_Team(int team, int action)
TeamEvent = (char)action + 1;
}
TeamFormDataStruct& team_form_data = TeamFormData[PlayerPtr->Class->House];
AllowVoice = true;
switch (action) {
@ -3935,9 +3937,11 @@ void Handle_Team(int team, int action)
** If a non team member is currently selected, then deselect all objects
** before selecting this team.
*/
if (CurrentObject.Count()) {
if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) {
for (index = 0; index < CurrentObject.Count(); index++) {
ObjectClass * obj = CurrentObject[index];
if (obj->Is_Foot() && ((FootClass *)obj)->Group != team) {
Unselect_All();
break;
}
}
for (index = 0; index < Vessels.Count(); index++) {
@ -4036,8 +4040,8 @@ void Handle_Team(int team, int action)
case 2: {
long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL;
long maxx = 0, maxy = 0;
TeamSpeed[team] = SPEED_WHEEL;
TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
team_form_data.TeamSpeed[team] = SPEED_WHEEL;
team_form_data.TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
for (index = 0; index < Units.Count(); index++) {
UnitClass * obj = Units.Ptr(index);
if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) {
@ -4051,9 +4055,9 @@ void Handle_Team(int team, int action)
if (xc > maxx) maxx = xc;
if (yc < miny) miny = yc;
if (yc > maxy) maxy = yc;
if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
TeamMaxSpeed[team] = obj->Class->MaxSpeed;
TeamSpeed[team] = obj->Class->Speed;
if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) {
team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed;
team_form_data.TeamSpeed[team] = obj->Class->Speed;
}
}
}
@ -4072,9 +4076,9 @@ void Handle_Team(int team, int action)
if (xc > maxx) maxx = xc;
if (yc < miny) miny = yc;
if (yc > maxy) maxy = yc;
if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
TeamMaxSpeed[team] = obj->Class->MaxSpeed;
TeamSpeed[team] = obj->Class->Speed;
if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) {
team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed;
team_form_data.TeamSpeed[team] = obj->Class->Speed;
}
}
}
@ -4093,8 +4097,8 @@ void Handle_Team(int team, int action)
if (xc > maxx) maxx = xc;
if (yc < miny) miny = yc;
if (yc > maxy) maxy = yc;
if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
TeamMaxSpeed[team] = obj->Class->MaxSpeed;
if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) {
team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed;
}
}
}
@ -5300,8 +5304,7 @@ static void Do_Record_Playback(void)
Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent));
Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber));
Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent));
Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed));
Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed));
Session.RecordFile.Write (TeamFormData, sizeof(TeamFormData));
Session.RecordFile.Write (&FormMove, sizeof(FormMove));
Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed));
Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed));
@ -5372,11 +5375,11 @@ static void Do_Record_Playback(void)
Toggle_Formation();
}
Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed));
Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed));
Session.RecordFile.Read (&FormMove, sizeof(FormMove));
Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed));
Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed));
Session.RecordFile.Read (TeamFormData, sizeof(TeamFormData));
Session.RecordFile.Read (&FormMove, sizeof(FormMove));
Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed));
Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed));
/*
** The map isn't drawn in playback mode, so draw it here.
*/

View file

@ -2323,6 +2323,7 @@ typedef enum AnimType : char {
ANIM_CRATE_TQUAKE,
ANIM_PARA_BOMB,
ANIM_MINE_EXP1,
ANIM_FLAG,
#ifdef FIXIT_ANTS
ANIM_ANT1_DEATH,

View file

@ -4024,7 +4024,7 @@ void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * obj
/*
** Only consider objects that are owned by the player.
*/
if (!foot->IsOwnedByPlayer) continue;
if (!foot->Is_Owned_By_Player()) continue;
/*
** If another member of this team has been discovered and

View file

@ -107,6 +107,7 @@ typedef enum {
#define RANDOM_START_POSITION 0x7f
#define KILL_PLAYER_ON_DISCONNECT 1
@ -1999,18 +2000,34 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uin
return;
}
HousesType house;
HouseClass *ptr;
GlyphX_Debug_Print("CNC_Handle_Player_Switch_To_AI");
if (GAME_TO_PLAY == GAME_NORMAL) {
return;
}
#ifdef KILL_PLAYER_ON_DISCONNECT
/*
** Kill player's units on disconnect.
*/
if (player_id != 0) {
DLLExportClass::Set_Player_Context(player_id);
if (PlayerPtr) {
PlayerPtr->Flag_To_Die();
}
}
#else //KILL_PLAYER_ON_DISCONNECT
if (player_id != 0) {
HousesType house;
HouseClass *ptr;
DLLExportClass::Set_Player_Context(player_id);
if (PlayerPtr) {
PlayerPtr->WasHuman = true;
PlayerPtr->IsHuman = false;
@ -2055,6 +2072,9 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uin
}
}
}
#endif //KILL_PLAYER_ON_DISCONNECT
}
@ -2898,12 +2918,24 @@ void DLLExportClass::On_Multiplayer_Game_Over(void)
event.GameOver.SabotagedStructureType = 0;
event.GameOver.TimerRemaining = -1;
// Trigger an event for each human player
for (int i=0 ; i<player_count; i++) {
// Trigger an event for each human player, winner first (even if it's an AI)
for (int i = 0; i < player_count; i++) {
HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
if (player_ptr->IsHuman) {
if (!player_ptr->IsDefeated) {
event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr);
event.GameOver.PlayerWins = !player_ptr->IsDefeated;
event.GameOver.IsHuman = player_ptr->IsHuman;
event.GameOver.PlayerWins = true;
event.GameOver.RemainingCredits = player_ptr->Available_Money();
EventCallback(event);
}
}
for (int i = 0; i < player_count; i++) {
HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
if (player_ptr->IsHuman && player_ptr->IsDefeated) {
event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr);
event.GameOver.IsHuman = true;
event.GameOver.PlayerWins = false;
event.GameOver.RemainingCredits = player_ptr->Available_Money();
EventCallback(event);
}
@ -4135,6 +4167,10 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(Sidebar
switch (request_type) {
// MBL 06.02.2020 - 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
// Handle and fall through to start construction (from hold state) below
case SIDEBAR_REQUEST_START_CONSTRUCTION_MULTI:
case SIDEBAR_REQUEST_START_CONSTRUCTION:
DLLExportClass::Start_Construction(player_id, buildable_type, buildable_id);
break;
@ -4372,7 +4408,7 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
if (tech) {
sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias;
sidebar_entry.PowerProvided = 0;
sidebar_entry.BuildTime = tech->Time_To_Build(); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60;
sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60;
strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH);
} else {
sidebar_entry.Cost = 0;
@ -4527,7 +4563,7 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
if (tech) {
sidebar_entry.Cost = tech->Cost;
sidebar_entry.PowerProvided = 0;
sidebar_entry.BuildTime = tech->Time_To_Build(); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60;
sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60;
strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH);
} else {
sidebar_entry.Cost = 0;
@ -7376,8 +7412,12 @@ void DLLExportClass::Selected_Guard_Mode(uint64 player_id)
for (int index = 0; index < CurrentObject.Count(); index++) {
ObjectClass const * tech = CurrentObject[index];
if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) {
OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA));
if (tech != NULL && tech->Can_Player_Fire()) {
if (tech->Can_Player_Move()) {
OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA));
} else {
OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD));
}
}
}
}
@ -7458,6 +7498,8 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
int index;
bool setform = 0;
TeamFormDataStruct& team_form_data = TeamFormData[PlayerPtr->Class->House];
//
// Recording support
//
@ -7481,8 +7523,8 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
team = obj->Group;
if (team < MAX_TEAMS) {
setform = obj->XFormOffset == (int)0x80000000;
TeamSpeed[team] = SPEED_WHEEL;
TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
team_form_data.TeamSpeed[team] = SPEED_WHEEL;
team_form_data.TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
break;
}
}
@ -7500,8 +7542,8 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
team = obj->Group;
if (team < MAX_TEAMS) {
setform = obj->XFormOffset == (int)0x80000000;
TeamSpeed[team] = SPEED_WHEEL;
TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
team_form_data.TeamSpeed[team] = SPEED_WHEEL;
team_form_data.TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
break;
}
}
@ -7521,8 +7563,8 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
team = obj->Group;
if (team < MAX_TEAMS) {
setform = obj->XFormOffset == 0x80000000UL;
TeamSpeed[team] = SPEED_WHEEL;
TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
team_form_data.TeamSpeed[team] = SPEED_WHEEL;
team_form_data.TeamMaxSpeed[team] = MPH_LIGHT_SPEED;
break;
}
}
@ -7547,9 +7589,9 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
if (xc > maxx) maxx = xc;
if (yc < miny) miny = yc;
if (yc > maxy) maxy = yc;
if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
TeamMaxSpeed[team] = obj->Class->MaxSpeed;
TeamSpeed[team] = obj->Class->Speed;
if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) {
team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed;
team_form_data.TeamSpeed[team] = obj->Class->Speed;
}
} else {
obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
@ -7568,8 +7610,8 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
if (xc > maxx) maxx = xc;
if (yc < miny) miny = yc;
if (yc > maxy) maxy = yc;
if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
TeamMaxSpeed[team] = obj->Class->MaxSpeed;
if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) {
team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed;
}
} else {
obj->XFormOffset = obj->YFormOffset = (int)0x80000000;
@ -7588,8 +7630,8 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
if (xc > maxx) maxx = xc;
if (yc < miny) miny = yc;
if (yc > maxy) maxy = yc;
if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) {
TeamMaxSpeed[team] = obj->Class->MaxSpeed;
if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) {
team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed;
}
} else {
obj->XFormOffset = obj->YFormOffset = 0x80000000UL;
@ -8158,7 +8200,7 @@ void DLLExportClass::Debug_Heal_Unit(int x, int y)
CellClass* cells[cellcount];
cells[0] = cellptr;
for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
cells[(int)index + 1] = &cellptr->Adjacent_Cell(index);
cells[(int)index + 1] = cellptr->Adjacent_Cell(index);
}
for (int index = 0; index < cellcount; index++) {

View file

@ -29,7 +29,7 @@ struct CarryoverObjectStruct;
**
**
*/
#define CNC_DLL_API_VERSION 0x100
#define CNC_DLL_API_VERSION 0x101
@ -627,6 +627,7 @@ struct EventCallbackStruct {
//
// Single-player data
//
bool IsHuman;
bool PlayerWins; //This should specify player id
const char* MovieName;
const char* MovieName2;

View file

@ -1372,7 +1372,7 @@ void DriveClass::AI(void)
land = Map[Center_Coord()].Land_Type();
}
if (IsLocked && Mission != MISSION_ENTER && Target_Legal(NavCom) && !Is_In_Same_Zone(As_Cell(NavCom)) &&
land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER) {
land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER && !Team) {
Stop_Driver();
Assign_Destination(TARGET_NONE);
} else {

View file

@ -255,8 +255,7 @@ extern DynamicVectorClass<TriggerClass *> HouseTriggers[HOUSE_COUNT];
extern BaseClass Base;
/* These variables are used to keep track of the slowest speed of a team */
extern MPHType TeamMaxSpeed[MAX_TEAMS];
extern SpeedType TeamSpeed[MAX_TEAMS];
extern TeamFormDataStruct TeamFormData[HOUSE_COUNT];
extern bool FormMove;
extern SpeedType FormSpeed;
extern MPHType FormMaxSpeed;

View file

@ -445,29 +445,13 @@ bool FactoryClass::Start(void)
int time;
if (Object) {
time = Object->Time_To_Build();
// } else {
// time = TICKS_PER_MINUTE * 5;
time = Object->Class_Of().Time_To_Build(House->Class->House);
}
/*
** Adjust time according to IQ setting of computer controlled house. The
** build time will range from double normal time at the slowest to
** just normal time at the fastest.
*/
if (!House->IsHuman && Rule.Diff[House->Difficulty].IsBuildSlowdown) {
time = time * Inverse(fixed(House->IQ+Rule.MaxIQ, Rule.MaxIQ*2));
}
time /= STEP_COUNT;
time = Bound(time, 1, 255);
int rate = time / Bound(House->Power_Fraction(), fixed(1, 16), fixed(1));
// int frac = House->Power_Fraction();
// frac = Bound(frac, 0x0010, 0x0100);
// int rate = (time*256) / frac;
rate /= STEP_COUNT;
rate = Bound(rate, 1, 255);
Set_Rate(rate);
Set_Rate(time);
IsSuspended = false;
return(true);
}

View file

@ -629,7 +629,7 @@ top_of_list:
// MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array;
// The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix;
// We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; This code does not exist in Tiberian Dawn.
// We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering;
#if 0
left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left)/sizeof(moves_left[0]), threshhold);
// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold);
@ -649,7 +649,7 @@ top_of_list:
// MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array;
// The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix;
// We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; This code does not exist in Tiberian Dawn.
// We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering;
#if 0
right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right)/sizeof(moves_right[0]), threshhold);
// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold);

View file

@ -130,8 +130,7 @@ template<> FixedIHeapClass * CCPtr<SmudgeTypeClass>::Heap = &SmudgeTypes;
#endif
/* These variables are used to keep track of the slowest speed of a team */
MPHType TeamMaxSpeed[MAX_TEAMS];
SpeedType TeamSpeed[MAX_TEAMS];
TeamFormDataStruct TeamFormData[HOUSE_COUNT];
bool FormMove;
SpeedType FormSpeed;
MPHType FormMaxSpeed;

View file

@ -1125,6 +1125,7 @@ void HouseClass::AI(void)
unit->Mark(MARK_CHANGE);
} else {
CELL cell = As_Cell(FlagLocation);
Map[cell].Flag_Update();
Map[cell].Redraw_Objects();
}
}
@ -1823,7 +1824,11 @@ void HouseClass::Attacked(BuildingClass* source)
if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) {
#endif
Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay);
// MBL 06.13.2020 - Timing change from 2 minute cooldown, per https://jaas.ea.com/browse/TDRA-6784
// SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); // 2 minutes
// SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE/2); // 30 seconds as requested
SpeakAttackDelay = Options.Normalize_Delay( (TICKS_PER_MINUTE/2)+(TICKS_PER_SECOND*5) ); // Tweaked for accuracy
/*
** If there is a trigger event associated with being attacked, process it
@ -3762,7 +3767,6 @@ void HouseClass::MPlayer_Defeated(void)
*/
if (PlayerPtr == this) {
Session.ObiWan = 1;
Debug_Unshroud = true;
HidPage.Clear();
Map.Flag_To_Redraw(true);
@ -3919,11 +3923,6 @@ void HouseClass::MPlayer_Defeated(void)
#endif // MPATH
}
/*
** Be sure our messages get displayed, even if we're about to exit.
*/
Map.Render();
}
@ -4411,10 +4410,14 @@ void HouseClass::Sell_Wall(CELL cell)
Map[cell].OverlayData = 0;
Map[cell].Owner = HOUSE_NONE;
Map[cell].Wall_Update();
Map[cell].Adjacent_Cell(FACING_N).Wall_Update();
Map[cell].Adjacent_Cell(FACING_W).Wall_Update();
Map[cell].Adjacent_Cell(FACING_S).Wall_Update();
Map[cell].Adjacent_Cell(FACING_E).Wall_Update();
CellClass * ncell = Map[cell].Adjacent_Cell(FACING_N);
if (ncell) ncell->Wall_Update();
CellClass * wcell = Map[cell].Adjacent_Cell(FACING_W);
if (wcell) wcell->Wall_Update();
CellClass * scell = Map[cell].Adjacent_Cell(FACING_S);
if (scell) scell->Wall_Update();
CellClass * ecell = Map[cell].Adjacent_Cell(FACING_E);
if (ecell) ecell->Wall_Update();
Map[cell].Recalc_Attributes();
Map[cell].Redraw_Objects();
Map.Radar_Pixel(cell);
@ -8016,7 +8019,7 @@ void HouseClass::Check_Pertinent_Structures(void)
BuildingClass *b = Buildings.Ptr(index);
if (b && b->IsActive && b->House == this) {
if (!b->Class->IsWall) {
if (!b->Class->IsWall && *b != STRUCT_APMINE && *b != STRUCT_AVMINE) {
if (!b->IsInLimbo && b->Strength > 0) {
any_good_buildings = true;
break;

View file

@ -142,6 +142,8 @@ void CellClass::Code_Pointers(void)
Overlapper[index] = NULL;
}
}
assert(CTFFlag == NULL);
}
@ -177,6 +179,8 @@ void CellClass::Decode_Pointers(void)
assert(Overlapper[index] != NULL);
}
}
CTFFlag = NULL;
}
@ -480,8 +484,14 @@ void DisplayClass::Decode_Pointers(void)
*=============================================================================================*/
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 cell = 0; cell < MAP_CELL_TOTAL; cell++) {
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
cellptr->Code_Pointers();
cellptr++;
}

View file

@ -428,7 +428,7 @@ dxisbig:
#if (0)
/*
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/MiscAsm.cpp#33 $
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/MiscAsm.cpp#57 $
;***************************************************************************
;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
;***************************************************************************

View file

@ -2270,7 +2270,7 @@ int ObjectTypeClass::Cost_Of(void) const
* HISTORY: *
* 07/19/1995 JLB : Created. *
*=============================================================================================*/
int ObjectTypeClass::Time_To_Build(void) const
int ObjectTypeClass::Time_To_Build(HousesType ) const
{
return(0);
}

View file

@ -329,7 +329,8 @@ static TechnoClass * _Who_Can_Pop_Out_Of(CELL origin)
for (int f = -1; f < 8; f++) {
CellClass * ptr = cellptr;
if (f != -1) {
ptr = &ptr->Adjacent_Cell(FacingType(f));
ptr = ptr->Adjacent_Cell(FacingType(f));
if (!ptr) continue;
}
BuildingClass * building = ptr->Cell_Building();

View file

@ -2470,7 +2470,10 @@ bool Read_Scenario_INI(char * fname, bool )
/*
** Special cases:
** Gold Rush multiplayer map cell 9033 - LAND_ROCK
** The Lake District multiplayer map cell 8482 - LAND_ROCK
** Blue Lakes multiplayer map cell 11937 - LAND_RIVER
** USSR mission 13 - fixup trigger action
** Allied mission 5B - fail mission if spy re-boards the transport at mission start
** Allied mission 9A - fail mission if tech center is destroyed before being spied
** Aftermath: Brother in Arms - have transports move to separate waypoints
** Aftermath: Let's Make a Steal - Make the pillboxes un-capturable
@ -2480,11 +2483,45 @@ bool Read_Scenario_INI(char * fname, bool )
Map[(CELL)9033].Override_Land_Type(LAND_ROCK);
}
if (_stricmp(Scen.ScenarioName, "scm93ea.ini") == 0) {
Map[(CELL)8482].Override_Land_Type(LAND_ROCK);
}
if (_stricmp(Scen.ScenarioName, "scmh4ea.ini") == 0) {
Map[(CELL)11937].Override_Land_Type(LAND_RIVER);
}
if (_stricmp(Scen.ScenarioName, "scu13ea.ini") == 0) {
TriggerTypeClass* trigger = TriggerTypes.Ptr(11);
trigger->Action1.Trigger.Set_Raw(39);
}
if (_stricmp(Scen.ScenarioName, "scg05eb.ini") == 0) {
TeamTypeClass* spy1_team = TeamTypeClass::From_Name("spy1");
assert(spy1_team != NULL);
spy1_team->MissionList[spy1_team->MissionCount].Mission = TMISSION_SET_GLOBAL;
spy1_team->MissionList[spy1_team->MissionCount].Data.Value = 16;
spy1_team->MissionCount++;
TriggerTypeClass* los3_trigger = new TriggerTypeClass();
los3_trigger->IsPersistant = TriggerTypeClass::VOLATILE;
los3_trigger->House = HOUSE_GREECE;
los3_trigger->EventControl = MULTI_AND;
los3_trigger->ActionControl = MULTI_AND;
los3_trigger->Event1.Event = TEVENT_GLOBAL_SET;
los3_trigger->Event1.Data.Value = 16;
los3_trigger->Event2.Event = TEVENT_ALL_DESTROYED;
los3_trigger->Event2.Data.House = HOUSE_GREECE;
los3_trigger->Action1.Action = TACTION_LOSE;
los3_trigger->Action1.Data.Value = -255;
los3_trigger->Action2.Action = TACTION_NONE;
los3_trigger->Action2.Data.Value = -1;
TriggerTypeClass* frc1_trigger = TriggerTypeClass::From_Name("frc1");
assert(frc1_trigger != NULL);
frc1_trigger->Action1.Trigger = los3_trigger;
}
if (_stricmp(Scen.ScenarioName, "scg09ea.ini") == 0) {
TriggerTypeClass* spyd_trigger = TriggerTypeClass::From_Name("Spyd");
assert(spyd_trigger != NULL);

View file

@ -2485,6 +2485,7 @@ int TeamClass::TMission_Formation(void)
int xdir = 0;
int ydir = 0;
bool evenodd = 1;
HousesType house = (member != NULL) ? member->Owner() : HOUSE_NONE;
/*
** Assign appropriate formation offsets for each of the members
@ -2606,9 +2607,10 @@ int TeamClass::TMission_Formation(void)
/*
** Now calculate the group's movement type and speed
*/
if (Formation != FORMATION_NONE) {
TeamSpeed[group] = SPEED_WHEEL;
TeamMaxSpeed[group] = MPH_LIGHT_SPEED;
if (Formation != FORMATION_NONE && house != HOUSE_NONE) {
TeamFormDataStruct& team_form_data = TeamFormData[house];
team_form_data.TeamSpeed[group] = SPEED_WHEEL;
team_form_data.TeamMaxSpeed[group] = MPH_LIGHT_SPEED;
member = Member;
while (member != NULL) {
RTTIType mytype = member->What_Am_I();
@ -2634,9 +2636,9 @@ int TeamClass::TMission_Formation(void)
}
if (speedcheck) {
if (memmax < TeamMaxSpeed[group]) {
TeamMaxSpeed[group] = memmax;
TeamSpeed[group] = memspeed;
if (memmax < team_form_data.TeamMaxSpeed[group]) {
team_form_data.TeamMaxSpeed[group] = memmax;
team_form_data.TeamSpeed[group] = memspeed;
}
}
member = member->Member;
@ -2648,8 +2650,8 @@ int TeamClass::TMission_Formation(void)
*/
member = Member;
while (member != NULL) {
member->FormationSpeed = TeamSpeed[group];
member->FormationMaxSpeed = TeamMaxSpeed[group];
member->FormationSpeed = team_form_data.TeamSpeed[group];
member->FormationMaxSpeed = team_form_data.TeamMaxSpeed[group];
if (member->What_Am_I() == RTTI_INFANTRY) {
member->FormationSpeed = SPEED_FOOT;
member->FormationMaxSpeed = MPH_SLOW_ISH;

View file

@ -47,6 +47,12 @@
*/
#define STRAY_DISTANCE 2
struct TeamFormDataStruct
{
MPHType TeamMaxSpeed[MAX_TEAMS];
SpeedType TeamSpeed[MAX_TEAMS];
};
class TeamClass : public AbstractClass
{
public:

View file

@ -637,80 +637,6 @@ TechnoClass::TechnoClass(RTTIType rtti, int id, HousesType house) :
}
/***********************************************************************************************
* TechnoClass::Time_To_Build -- Determines the time it would take to build this. *
* *
* Use this routine to determine the amount of time it would take to build an object of *
* this type. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with the time it should take (unmodified by outside factors) to build *
* this object. The time is expressed in game frames. *
* *
* WARNINGS: The time will usually be modified by power status and handicap rating for the *
* owning house. The value returned is merely the raw unmodified time to build. *
* *
* HISTORY: *
* 07/29/1996 JLB : Created. *
* 09/27/1996 JLB : Takes into account the power availability. *
*=============================================================================================*/
//#define UNIT_BUILD_BIAS fixed(5,4)
//#define UNIT_BUILD_BIAS fixed(6,4)
#define UNIT_BUILD_BIAS fixed(1,1)
//#define UNIT_BUILD_BIAS fixed(5,1)
extern int UnitBuildPenalty;
int TechnoClass::Time_To_Build(void) const
{
int val = Class_Of().Time_To_Build();
#ifdef FIXIT_VERSION_3
if (Session.Type == GAME_NORMAL ) {
#else
if (Session.Type == GAME_NORMAL ||
PlayingAgainstVersion == VERSION_RED_ALERT_104 ||
PlayingAgainstVersion == VERSION_RED_ALERT_107){
#endif
val *= House->BuildSpeedBias;
}else{
if (What_Am_I() == RTTI_BUILDING || What_Am_I() == RTTI_INFANTRY) {
val *= House->BuildSpeedBias;
} else {
val *= House->BuildSpeedBias * fixed (UnitBuildPenalty, 100); //UNIT_BUILD_BIAS;
}
}
/*
** Adjust the time to build based on the power output of the owning house.
*/
fixed power = House->Power_Fraction();
if (power > 1) power = 1;
if (power < 1 && power > fixed::_3_4) power = fixed::_3_4;
if (power < fixed::_1_2) power = fixed::_1_2;
power.Inverse();
val *= power;
int divisor = House->Factory_Count(What_Am_I());
if (divisor != 0) {
#ifdef FIXIT_CSII // checked - ajw 9/28/98
// Hack: allow the multiple-factory bonus, but only up to two factories if
// this is an AM<->AM game.
if(NewUnitsEnabled) {
val /= min(divisor,2);
} else {
val /= divisor;
}
#else
val /= divisor;
#endif
}
return(val);
}
/***********************************************************************************************
* TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? *
* *
@ -3458,8 +3384,9 @@ void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARG
if (Is_Foot()) {
const FootClass* foot = (const FootClass*)this;
if (foot->Group < MAX_TEAMS) {
speed = TeamSpeed[foot->Group];
maxspeed = TeamMaxSpeed[foot->Group];
TeamFormDataStruct& team_form_data = TeamFormData[foot->Owner()];
speed = team_form_data.TeamSpeed[foot->Group];
maxspeed = team_form_data.TeamMaxSpeed[foot->Group];
}
}
Queue_Mission(TargetClass(this), mission, target, destination, speed, maxspeed);
@ -6494,9 +6421,82 @@ int TechnoTypeClass::Get_Ownable(void) const
* HISTORY: *
* 07/29/1995 JLB : Created. *
*=============================================================================================*/
int TechnoTypeClass::Time_To_Build(void) const
//#define UNIT_BUILD_BIAS fixed(5,4)
//#define UNIT_BUILD_BIAS fixed(6,4)
#define UNIT_BUILD_BIAS fixed(1,1)
//#define UNIT_BUILD_BIAS fixed(5,1)
extern int UnitBuildPenalty;
int TechnoTypeClass::Time_To_Build(HousesType house) const
{
return(Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000));
int time = Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000);
HouseClass* hptr = HouseClass::As_Pointer(house);
if (!hptr || !hptr->IsActive) {
return time;
}
#ifdef FIXIT_VERSION_3
if (Session.Type == GAME_NORMAL) {
#else
if (Session.Type == GAME_NORMAL ||
PlayingAgainstVersion == VERSION_RED_ALERT_104 ||
PlayingAgainstVersion == VERSION_RED_ALERT_107) {
#endif
time *= hptr->BuildSpeedBias;
}
else {
if (What_Am_I() == RTTI_BUILDINGTYPE || What_Am_I() == RTTI_INFANTRYTYPE) {
time *= hptr->BuildSpeedBias;
}
else {
time *= hptr->BuildSpeedBias * fixed(UnitBuildPenalty, 100); //UNIT_BUILD_BIAS;
}
}
/*
** Adjust time according to IQ setting of computer controlled house. The
** build time will range from double normal time at the slowest to
** just normal time at the fastest.
*/
if (!hptr->IsHuman && Rule.Diff[hptr->Difficulty].IsBuildSlowdown) {
time = time * Inverse(fixed(hptr->IQ + Rule.MaxIQ, Rule.MaxIQ * 2));
}
/*
** Adjust the time to build based on the power output of the owning house.
*/
fixed power = hptr->Power_Fraction();
fixed scale(1);
if (power == 0) {
scale = fixed(4, 1);
}
else if (power < fixed::_1_2) {
scale = fixed(5, 2);
}
else if (power < 1) {
scale = fixed(3, 2);
}
time *= scale;
int divisor = hptr->Factory_Count(What_Am_I());
if (divisor != 0) {
#ifdef FIXIT_CSII // checked - ajw 9/28/98
// Hack: allow the multiple-factory bonus, but only up to two factories if
// this is an AM<->AM game.
if (NewUnitsEnabled) {
time /= min(divisor, 2);
}
else {
time /= divisor;
}
#else
time /= divisor;
#endif
}
return(time);
}

View file

@ -277,7 +277,6 @@ class TechnoClass : public RadioClass,
int Anti_Air(void) const;
int Anti_Armor(void) const;
int Anti_Infantry(void) const;
int Time_To_Build(void) const;
int What_Weapon_Should_I_Use(TARGET target) const;
virtual ActionType What_Action(CELL cell) const;
virtual ActionType What_Action(ObjectClass const * target) const;

View file

@ -292,7 +292,7 @@ class ObjectTypeClass : public AbstractTypeClass
virtual void Dimensions(int &width, int &height) const;
virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const = 0;
virtual int Cost_Of(void) const;
virtual int Time_To_Build(void) const;
virtual int Time_To_Build(HousesType house) const;
virtual ObjectClass * Create_One_Of(HouseClass *) const = 0;
virtual short const * Occupy_List(bool placement=false) const;
virtual short const * Overlap_List(void) const;
@ -572,7 +572,7 @@ class TechnoTypeClass : public ObjectTypeClass
virtual int Repair_Step(void) const;
virtual void const * Get_Cameo_Data(void) const;
virtual int Cost_Of(void) const;
virtual int Time_To_Build(void) const;
virtual int Time_To_Build(HousesType house) const;
virtual int Get_Ownable(void) const;
virtual bool Read_INI(CCINIClass & ini);
@ -1789,7 +1789,7 @@ class AnimTypeClass : public ObjectTypeClass
** This is the normal loop count for this animation. Usually this is one, but
** for some animations, it may be larger.
*/
unsigned Loops;
int Loops;
/*
** This is the sound effect to play when this animation starts. Usually, this

View file

@ -1276,6 +1276,18 @@ void UnitClass::Active_Click_With(ActionType action, CELL cell)
}
void UnitClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination)
{
assert(Units.ID(this) == ID);
assert(IsActive);
if (mission == MISSION_HARVEST) {
ArchiveTarget = TARGET_NONE;
}
DriveClass::Player_Assign_Mission(mission, target, destination);
}
/***********************************************************************************************
* UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
* *
@ -2144,7 +2156,10 @@ void UnitClass::Draw_It(int x, int y, WindowNumberType window) const
** If this unit is carrying the flag, then draw that on top of everything else.
*/
if (Flagged != HOUSE_NONE) {
CC_Draw_Shape(this, "FLAGFLY", MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow, DIR_N, 0x0100, Flagged);
shapefile = MFCD::Retrieve("FLAGFLY.SHP");
int flag_x = x + (ICON_PIXEL_W / 2) - 2;
int flag_y = y + (3 * ICON_PIXEL_H / 4) - Get_Build_Frame_Height(shapefile);
CC_Draw_Shape(this, "FLAGFLY", shapefile, Frame % 14, flag_x, flag_y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow, DIR_N, 0x0100, Flagged);
}
DriveClass::Draw_It(x, y, window);
@ -2163,14 +2178,14 @@ void UnitClass::Draw_It(int x, int y, WindowNumberType window) const
* *
* x,y -- Relative offset from the center cell to perform the check upon. *
* *
* OUTPUT: bool; Is it located directly over a Tiberium patch? *
* OUTPUT: int; Amount of Tiberium at this location. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/18/1994 JLB : Created. *
*=============================================================================================*/
bool UnitClass::Tiberium_Check(CELL & center, int x, int y)
int UnitClass::Tiberium_Check(CELL & center, int x, int y)
{
assert(Units.ID(this) == ID);
assert(IsActive);
@ -2179,20 +2194,20 @@ bool UnitClass::Tiberium_Check(CELL & center, int x, int y)
** If the specified offset from the origin will cause it
** to spill past the map edge, then abort this cell check.
*/
if (Cell_X(center)+x < Map.MapCellX) return(false);
if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false);
if (Cell_Y(center)+y < Map.MapCellY) return(false);
if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false);
if (Cell_X(center)+x < Map.MapCellX) return(0);
if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(0);
if (Cell_Y(center)+y < Map.MapCellY) return(0);
if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(0);
center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y);
if ((Session.Type != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].IsMapped))) {
if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(false);
if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(0);
if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) {
return(true);
return(Map[center].OverlayData);
}
}
return(false);
return(0);
}
@ -2228,31 +2243,42 @@ bool UnitClass::Goto_Tiberium(int rad)
** Perform a ring search outward from the center.
*/
for (int radius = 1; radius < rad; radius++) {
CELL cell = center;
CELL bestcell = 0;
int tiberium = 0;
int besttiberium = 0;
for (int x = -radius; x <= radius; x++) {
CELL cell = center;
if (Tiberium_Check(cell, x, -radius)) {
Assign_Destination(::As_Target(cell));
return(false);
tiberium = Tiberium_Check(cell, x, -radius);
if (tiberium > besttiberium) {
bestcell = cell;
besttiberium = tiberium;
}
cell = center;
if (Tiberium_Check(cell, x, +radius)) {
Assign_Destination(::As_Target(cell));
return(false);
tiberium = Tiberium_Check(cell, x, +radius);
if (tiberium > besttiberium) {
bestcell = cell;
besttiberium = tiberium;
}
cell = center;
if (Tiberium_Check(cell, -radius, x)) {
Assign_Destination(::As_Target(cell));
return(false);
tiberium = Tiberium_Check(cell, -radius, x);
if (tiberium > besttiberium) {
bestcell = cell;
besttiberium = tiberium;
}
cell = center;
if (Tiberium_Check(cell, +radius, x)) {
Assign_Destination(::As_Target(cell));
return(false);
tiberium = Tiberium_Check(cell, +radius, x);
if (tiberium > besttiberium) {
bestcell = cell;
besttiberium = tiberium;
}
}
if (bestcell) {
Assign_Destination(::As_Target(bestcell));
return(false);
}
}
}
}
@ -2827,6 +2853,7 @@ int UnitClass::Mission_Harvest(void)
Set_Rate(2);
Set_Stage(0);
Status = HARVESTING;
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
return(1);
} else {
@ -2874,7 +2901,6 @@ int UnitClass::Mission_Harvest(void)
IsHarvesting = false;
if (Tiberium_Load() == 1) {
Status = FINDHOME;
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
} else {
if (!Goto_Tiberium(Rule.TiberiumShortScan / CELL_LEPTON_W) && !Target_Legal(NavCom)) {
ArchiveTarget = TARGET_NONE;
@ -2885,6 +2911,8 @@ int UnitClass::Mission_Harvest(void)
}
}
return(1);
} else if (!Target_Legal(NavCom) && ArchiveTarget == TARGET_NONE) {
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
}
return(1);
// return(TICKS_PER_SECOND*Rule.OreDumpRate);
@ -3502,16 +3530,14 @@ ActionType UnitClass::What_Action(ObjectClass const * object) const
/*
** Special return to friendly refinery action.
*/
if (House->IsPlayerControl && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) {
if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) {
action = ACTION_ENTER;
}
if (Is_Owned_By_Player() && House->Class->House == object->Owner() && object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) {
action = ACTION_ENTER;
}
/*
** Special return to friendly repair factory action.
*/
if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
if (Is_Owned_By_Player() && House->Class->House == object->Owner() && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
BuildingClass * building = (BuildingClass *)object;
if (building->Class->Type == STRUCT_REPAIR && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER && !building->In_Radio_Contact() && !building->Is_Something_Attached()) {
action = ACTION_MOVE;

View file

@ -137,7 +137,7 @@ class UnitClass : public DriveClass
bool Try_To_Deploy(void);
virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false);
bool Tiberium_Check(CELL &center, int x, int y);
int Tiberium_Check(CELL &center, int x, int y);
bool Flag_Attach(HousesType house);
bool Flag_Remove(void);
bool Goto_Tiberium(int radius);
@ -185,6 +185,7 @@ class UnitClass : public DriveClass
virtual ActionType What_Action(ObjectClass const * object) const;
virtual void Active_Click_With(ActionType action, ObjectClass * object);
virtual void Active_Click_With(ActionType action, CELL cell);
virtual void Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination);
/*
** Combat related.

View file

@ -1824,8 +1824,8 @@ int VesselClass::Mission_Unload(void)
** Tell everyone around the transport to scatter.
*/
for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
CellClass * cellptr = &Map[Coord].Adjacent_Cell(face);
if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) {
CellClass * cellptr = Map[Coord].Adjacent_Cell(face);
if (cellptr && cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) {
cellptr->Incoming(0, true);
}
}

View file

@ -4842,7 +4842,7 @@ extern "C" int __cdecl Confine_Rect ( int * x , int * y , int w , int h , int wi
/*
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/WIN32LIB/DrawMisc.cpp#33 $
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/WIN32LIB/DrawMisc.cpp#57 $
;***************************************************************************
;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
;***************************************************************************